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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClément Foucault <foucault.clem@gmail.com>2020-07-15 15:18:30 +0300
committerClément Foucault <foucault.clem@gmail.com>2020-07-15 15:23:35 +0300
commite8f8c13d4b76ba587ef7cf33370b286d4fbd36bc (patch)
tree371472ae220ad8740b310aaa8f4c5746448302c5 /source/blender/editors
parent0c062a9e082130212447c2b67e8e16b8a2e622d1 (diff)
parent44bb73e765a6f79bc14a46449368f83e572d8bad (diff)
PointCloud: Initial rendering support for Workbenchtmp-pointcloud-render
Also includes outline overlays. Removes the temp overlay drawing We make the geometry follow camera like billboards this uses less geometry. Currently we use half octahedron for now. Goal would be to use icospheres. This patch also optimize the case when pointcloud has uniform radius. However we should premultiply the radius prop by the default radius beforehand to avoid a multiplication on CPU. Differential Revision: https://developer.blender.org/D8301
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/anim_markers.c2
-rw-r--r--source/blender/editors/armature/meshlaplacian.c4
-rw-r--r--source/blender/editors/armature/meshlaplacian.h2
-rw-r--r--source/blender/editors/armature/pose_lib.c10
-rw-r--r--source/blender/editors/curve/editcurve.c1
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c3
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c6
-rw-r--r--source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_mesh.c2
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c6
-rw-r--r--source/blender/editors/gpencil/gpencil_vertex_paint.c2
-rw-r--r--source/blender/editors/include/ED_anim_api.h2
-rw-r--r--source/blender/editors/include/ED_fileselect.h2
-rw-r--r--source/blender/editors/include/ED_gpencil.h2
-rw-r--r--source/blender/editors/include/ED_image.h4
-rw-r--r--source/blender/editors/include/ED_node.h2
-rw-r--r--source/blender/editors/include/ED_object.h8
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_uvedit.h23
-rw-r--r--source/blender/editors/include/ED_view3d.h4
-rw-r--r--source/blender/editors/include/UI_interface.h9
-rw-r--r--source/blender/editors/interface/interface.c8
-rw-r--r--source/blender/editors/interface/interface_handlers.c10
-rw-r--r--source/blender/editors/interface/interface_templates.c55
-rw-r--r--source/blender/editors/interface/interface_widgets.c2
-rw-r--r--source/blender/editors/interface/view2d_ops.c6
-rw-r--r--source/blender/editors/lattice/editlattice_select.c2
-rw-r--r--source/blender/editors/mask/mask_add.c2
-rw-r--r--source/blender/editors/mesh/editmesh_knife.c358
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c15
-rw-r--r--source/blender/editors/mesh/mesh_intern.h1
-rw-r--r--source/blender/editors/mesh/meshtools.c41
-rw-r--r--source/blender/editors/object/object_constraint.c16
-rw-r--r--source/blender/editors/object/object_edit.c2
-rw-r--r--source/blender/editors/object/object_intern.h1
-rw-r--r--source/blender/editors/object/object_modifier.c158
-rw-r--r--source/blender/editors/object/object_ops.c1
-rw-r--r--source/blender/editors/object/object_relations.c134
-rw-r--r--source/blender/editors/object/object_shader_fx.c96
-rw-r--r--source/blender/editors/physics/dynamicpaint_ops.c10
-rw-r--r--source/blender/editors/physics/particle_object.c8
-rw-r--r--source/blender/editors/physics/physics_fluid.c3
-rw-r--r--source/blender/editors/render/render_shading.c10
-rw-r--r--source/blender/editors/screen/screen_ops.c7
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c16
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c6
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex_color_ops.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c203
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_automasking.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_cloth.c17
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_color.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_filter_mesh.c2
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h7
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_paint_color.c12
-rw-r--r--source/blender/editors/space_api/spacetypes.c1
-rw-r--r--source/blender/editors/space_buttons/space_buttons.c10
-rw-r--r--source/blender/editors/space_clip/clip_draw.c2
-rw-r--r--source/blender/editors/space_clip/clip_ops.c2
-rw-r--r--source/blender/editors/space_clip/tracking_ops_solve.c2
-rw-r--r--source/blender/editors/space_clip/tracking_ops_track.c8
-rw-r--r--source/blender/editors/space_clip/tracking_select.c4
-rw-r--r--source/blender/editors/space_file/filelist.c14
-rw-r--r--source/blender/editors/space_file/filesel.c2
-rw-r--r--source/blender/editors/space_file/fsmenu.c35
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c2
-rw-r--r--source/blender/editors/space_image/image_buttons.c8
-rw-r--r--source/blender/editors/space_image/image_draw.c8
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c2
-rw-r--r--source/blender/editors/space_nla/nla_draw.c5
-rw-r--r--source/blender/editors/space_node/drawnode.c6
-rw-r--r--source/blender/editors/space_node/node_edit.c7
-rw-r--r--source/blender/editors/space_node/node_view.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c98
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_scopes.c4
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_fly.c6
-rw-r--r--source/blender/editors/space_view3d/view3d_select.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_utils.c9
-rw-r--r--source/blender/editors/transform/transform_convert.c81
-rw-r--r--source/blender/editors/transform/transform_convert.h2
-rw-r--r--source/blender/editors/transform/transform_convert_curve.c16
-rw-r--r--source/blender/editors/transform/transform_convert_gpencil.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mesh.c101
-rw-r--r--source/blender/editors/transform/transform_convert_mesh_uv.c263
-rw-r--r--source/blender/editors/transform/transform_convert_particle.c2
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c22
-rw-r--r--source/blender/editors/transform/transform_mode_vert_slide.c4
-rw-r--r--source/blender/editors/transform/transform_snap_object.c20
-rw-r--r--source/blender/editors/uvedit/CMakeLists.txt3
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h5
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c18
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c10
-rw-r--r--source/blender/editors/uvedit/uvedit_path.c771
-rw-r--r--source/blender/editors/uvedit/uvedit_rip.c981
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c218
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c4
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c38
102 files changed, 3393 insertions, 743 deletions
diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c
index d823b976a47..bcdbf4c74f0 100644
--- a/source/blender/editors/animation/anim_markers.c
+++ b/source/blender/editors/animation/anim_markers.c
@@ -540,7 +540,7 @@ static void draw_markers_background(rctf *rect)
immUnbindProgram();
}
-static bool marker_is_in_frame_range(TimeMarker *marker, int frame_range[2])
+static bool marker_is_in_frame_range(TimeMarker *marker, const int frame_range[2])
{
if (marker->frame < frame_range[0]) {
return false;
diff --git a/source/blender/editors/armature/meshlaplacian.c b/source/blender/editors/armature/meshlaplacian.c
index 23fb6c0ab27..11066595e2e 100644
--- a/source/blender/editors/armature/meshlaplacian.c
+++ b/source/blender/editors/armature/meshlaplacian.c
@@ -655,7 +655,7 @@ void heat_bone_weighting(Object *ob,
bDeformGroup **dgroupflip,
float (*root)[3],
float (*tip)[3],
- int *selected,
+ const int *selected,
const char **err_str)
{
LaplacianSystem *sys;
@@ -1236,7 +1236,7 @@ static float meshdeform_boundary_phi(const MeshDeformBind *mdb,
}
static float meshdeform_interp_w(MeshDeformBind *mdb,
- float *gridvec,
+ const float *gridvec,
float *UNUSED(vec),
int UNUSED(cagevert))
{
diff --git a/source/blender/editors/armature/meshlaplacian.h b/source/blender/editors/armature/meshlaplacian.h
index ef4759eab4a..0a9e6e878e4 100644
--- a/source/blender/editors/armature/meshlaplacian.h
+++ b/source/blender/editors/armature/meshlaplacian.h
@@ -56,7 +56,7 @@ void heat_bone_weighting(struct Object *ob,
struct bDeformGroup **dgroupflip,
float (*root)[3],
float (*tip)[3],
- int *selected,
+ const int *selected,
const char **error);
#ifdef RIGID_DEFORM
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index 9d70f8877c6..6ce9ed06f1a 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -87,12 +87,12 @@ static void action_set_activemarker(void *UNUSED(a), void *UNUSED(b), void *UNUS
* It acts as a kind of "glorified clipboard for poses", allowing for naming of poses.
*
* Features:
- * - PoseLibs are simply normal Actions.
- * - Each "pose" is simply a set of keyframes that occur on a particular frame.
- * - A set of TimeMarkers that belong to each Action, help 'label' where a 'pose' can be
+ * - Pose-libs are simply normal Actions.
+ * - Each "pose" is simply a set of key-frames that occur on a particular frame.
+ * - A set of #TimeMarker that belong to each Action, help 'label' where a 'pose' can be
* found in the Action.
- * - The Scrollwheel or PageUp/Down buttons when used in a special mode or after pressing/holding
- * [a modifier] key, cycles through the poses available for the active pose's poselib,
+ * - The Scroll-wheel or PageUp/Down buttons when used in a special mode or after pressing/holding
+ * [a modifier] key, cycles through the poses available for the active pose's pose-lib,
* allowing the animator to preview what action best suits that pose.
*/
/* ************************************************************* */
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 38ab917ba72..3e428eaffc2 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -5498,6 +5498,7 @@ static int ed_editcurve_addvert(Curve *cu,
if (nu) {
nurb_new = BKE_nurb_copy(nu, 1, 1);
+ memcpy(nurb_new->bezt, nu->bezt, sizeof(BezTriple));
}
else {
nurb_new = MEM_callocN(sizeof(Nurb), "BLI_editcurve_addvert new_bezt_nurb 2");
diff --git a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
index e6333d7d3e0..f31e004264c 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/arrow3d_gizmo.c
@@ -88,7 +88,8 @@ static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const
const int draw_style = RNA_enum_get(arrow->gizmo.ptr, "draw_style");
const int draw_options = RNA_enum_get(arrow->gizmo.ptr, "draw_options");
- immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
+ immBindBuiltinProgram(select ? GPU_SHADER_3D_UNIFORM_COLOR :
+ GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR);
float viewport[4];
GPU_viewport_size_get_f(viewport);
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
index fd24149e9ea..85f84af5f14 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c
@@ -637,7 +637,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
}
if (select) {
- /* expand for hotspot */
+ /* Expand for hot-spot. */
const float size[2] = {size_real[0] + margin[0] / 2, size_real[1] + margin[1] / 2};
if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_SCALE) {
@@ -694,7 +694,7 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz,
bool show = false;
if (gz->highlight_part == ED_GIZMO_CAGE2D_PART_TRANSLATE) {
/* Only show if we're drawing the center handle
- * otherwise the entire rectangle is the hotspot. */
+ * otherwise the entire rectangle is the hot-spot. */
if (draw_options & ED_GIZMO_CAGE2D_DRAW_FLAG_XFORM_CENTER_HANDLE) {
show = true;
}
@@ -805,7 +805,7 @@ static int gizmo_cage2d_test_select(bContext *C, wmGizmo *gz, const int mval[2])
return -1;
}
- /* expand for hotspot */
+ /* Expand for hots-pot. */
const float size[2] = {size_real[0] + margin[0] / 2, size_real[1] + margin[1] / 2};
const int transform_flag = RNA_enum_get(gz->ptr, "transform");
diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
index b0af8641767..8955a666e22 100644
--- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
+++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c
@@ -314,7 +314,7 @@ static void gizmo_cage3d_draw_intern(
}
if (select) {
- /* expand for hotspot */
+ /* Expand for hot-spot. */
#if 0
const float size[3] = {
size_real[0] + margin[0] / 2,
diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c
index 763f5687edf..68ab5100bf4 100644
--- a/source/blender/editors/gpencil/gpencil_mesh.c
+++ b/source/blender/editors/gpencil/gpencil_mesh.c
@@ -82,7 +82,7 @@ static bool gpencil_bake_mesh_animation_poll(bContext *C)
}
typedef struct GpBakeOb {
- struct GPBakelist *next, *prev;
+ struct GpBakeOb *next, *prev;
Object *ob;
} GpBakeOb;
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index dd5e16a9d9b..a695d2e4cf2 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -2245,7 +2245,7 @@ static void gpencil_copy_points(
}
static void gpencil_insert_point(
- bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], float co_b[3])
+ bGPDstroke *gps, bGPDspoint *a_pt, bGPDspoint *b_pt, const float co_a[3], const float co_b[3])
{
bGPDspoint *temp_points;
int totnewpoints, oldtotpoints;
@@ -2764,7 +2764,7 @@ void ED_gpencil_init_random_settings(Brush *brush,
}
static void gpencil_sbuffer_vertex_color_random(
- bGPdata *gpd, Brush *brush, tGPspoint *tpt, float random_color[3], float pen_pressure)
+ bGPdata *gpd, Brush *brush, tGPspoint *tpt, const float random_color[3], float pen_pressure)
{
BrushGpencilSettings *brush_settings = brush->gpencil_settings;
if (brush_settings->flag & GP_BRUSH_GROUP_RANDOM) {
@@ -2895,7 +2895,7 @@ void ED_gpencil_sbuffer_vertex_color_set(Depsgraph *depsgraph,
/* Check if the stroke collides with brush. */
bool ED_gpencil_stroke_check_collision(GP_SpaceConversion *gsc,
bGPDstroke *gps,
- float mouse[2],
+ const float mouse[2],
const int radius,
const float diff_mat[4][4])
{
diff --git a/source/blender/editors/gpencil/gpencil_vertex_paint.c b/source/blender/editors/gpencil/gpencil_vertex_paint.c
index 5f8ff228d4d..c36bc4388d7 100644
--- a/source/blender/editors/gpencil/gpencil_vertex_paint.c
+++ b/source/blender/editors/gpencil/gpencil_vertex_paint.c
@@ -348,7 +348,7 @@ static void gpencil_grid_cell_average_color_idx_get(tGP_BrushVertexpaintData *gs
}
}
-static int gpencil_grid_cell_index_get(tGP_BrushVertexpaintData *gso, int pc[2])
+static int gpencil_grid_cell_index_get(tGP_BrushVertexpaintData *gso, const int pc[2])
{
float bottom[2], top[2];
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 3aecec0d6b6..bffa11a32f2 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -708,7 +708,7 @@ void getcolor_fcurve_rainbow(int cur, int tot, float out[3]);
/* ----------------- NLA Drawing ----------------------- */
/* NOTE: Technically, this is not in the animation module (it's in space_nla)
- * but these are sometimes needed by various animation apis.
+ * but these are sometimes needed by various animation API's.
*/
/* Get color to use for NLA Action channel's background */
diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h
index a523e924e54..8a239559627 100644
--- a/source/blender/editors/include/ED_fileselect.h
+++ b/source/blender/editors/include/ED_fileselect.h
@@ -106,7 +106,7 @@ struct FileSelectParams *ED_fileselect_get_params(struct SpaceFile *sfile);
short ED_fileselect_set_params(struct SpaceFile *sfile);
void ED_fileselect_set_params_from_userdef(struct SpaceFile *sfile);
void ED_fileselect_params_to_userdef(struct SpaceFile *sfile,
- int temp_win_size[],
+ const int temp_win_size[],
const bool is_maximized);
void ED_fileselect_reset_params(struct SpaceFile *sfile);
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index f961f835f12..64276706759 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -351,7 +351,7 @@ void ED_gpencil_init_random_settings(struct Brush *brush,
bool ED_gpencil_stroke_check_collision(struct GP_SpaceConversion *gsc,
struct bGPDstroke *gps,
- float mouse[2],
+ const float mouse[2],
const int radius,
const float diff_mat[4][4]);
bool ED_gpencil_stroke_point_is_inside(struct bGPDstroke *gps,
diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h
index a8476e3d1ca..e8343fbbc19 100644
--- a/source/blender/editors/include/ED_image.h
+++ b/source/blender/editors/include/ED_image.h
@@ -125,8 +125,8 @@ void ED_image_draw_info(struct Scene *scene,
const unsigned char cp[4],
const float fp[4],
const float linearcol[4],
- int *zp,
- float *zpf);
+ const int *zp,
+ const float *zpf);
bool ED_space_image_show_cache(struct SpaceImage *sima);
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index 47ccc0788c2..382902cd2de 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -121,7 +121,7 @@ void ED_operatormacros_node(void);
bool ED_space_node_color_sample(struct Main *bmain,
struct SpaceNode *snode,
struct ARegion *region,
- int mval[2],
+ const int mval[2],
float r_col[3]);
#ifdef __cplusplus
diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h
index a851eb735b8..d8f55a0f60a 100644
--- a/source/blender/editors/include/ED_object.h
+++ b/source/blender/editors/include/ED_object.h
@@ -362,9 +362,10 @@ struct ModifierData *ED_object_modifier_add(struct ReportList *reports,
int type);
bool ED_object_modifier_remove(struct ReportList *reports,
struct Main *bmain,
+ struct Scene *scene,
struct Object *ob,
struct ModifierData *md);
-void ED_object_modifier_clear(struct Main *bmain, struct Object *ob);
+void ED_object_modifier_clear(struct Main *bmain, struct Scene *scene, struct Object *ob);
bool ED_object_modifier_move_down(struct ReportList *reports,
struct Object *ob,
struct ModifierData *md);
@@ -389,8 +390,11 @@ bool ED_object_modifier_apply(struct Main *bmain,
struct Scene *scene,
struct Object *ob,
struct ModifierData *md,
- int mode);
+ int mode,
+ bool keep_modifier);
int ED_object_modifier_copy(struct ReportList *reports,
+ struct Main *bmain,
+ struct Scene *scene,
struct Object *ob,
struct ModifierData *md);
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index bc6a4b23609..71b7d35908b 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -349,6 +349,7 @@ bool ED_operator_console_active(struct bContext *C);
bool ED_operator_object_active(struct bContext *C);
bool ED_operator_object_active_editable_ex(struct bContext *C, const Object *ob);
bool ED_operator_object_active_editable(struct bContext *C);
+bool ED_operator_object_active_local_editable(struct bContext *C);
bool ED_operator_object_active_editable_mesh(struct bContext *C);
bool ED_operator_object_active_editable_font(struct bContext *C);
bool ED_operator_editable_mesh(struct bContext *C);
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index f656aaf9c07..ec41e785714 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -47,6 +47,7 @@ struct wmKeyConfig;
/* uvedit_ops.c */
void ED_operatortypes_uvedit(void);
+void ED_operatormacros_uvedit(void);
void ED_keymap_uvedit(struct wmKeyConfig *keyconf);
bool ED_uvedit_minmax(const struct Scene *scene,
@@ -174,8 +175,26 @@ bool ED_uvedit_nearest_uv_multi(const struct Scene *scene,
float *dist_sq,
float r_uv[2]);
-void ED_uvedit_get_aspect(
- const struct Scene *scene, struct Object *ob, struct BMesh *em, float *r_aspx, float *r_aspy);
+struct BMFace **ED_uvedit_selected_faces(struct Scene *scene,
+ struct BMesh *bm,
+ int len_max,
+ int *r_faces_len);
+struct BMLoop **ED_uvedit_selected_edges(struct Scene *scene,
+ struct BMesh *bm,
+ int len_max,
+ int *r_edges_len);
+struct BMLoop **ED_uvedit_selected_verts(struct Scene *scene,
+ struct BMesh *bm,
+ int len_max,
+ int *r_verts_len);
+
+void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy);
+
+void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l);
+struct BMLoop *ED_uvedit_active_vert_loop_get(struct BMesh *bm);
+
+void ED_uvedit_active_edge_loop_set(struct BMesh *bm, struct BMLoop *l);
+struct BMLoop *ED_uvedit_active_edge_loop_get(struct BMesh *bm);
/* uvedit_unwrap_ops.c */
void ED_uvedit_live_unwrap_begin(struct Scene *scene, struct Object *obedit);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 5e706856738..f8a4884c594 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -150,7 +150,7 @@ void ED_view3d_cursor3d_update(struct bContext *C,
struct Camera *ED_view3d_camera_data_get(struct View3D *v3d, struct RegionView3D *rv3d);
void ED_view3d_to_m4(float mat[4][4], const float ofs[3], const float quat[4], const float dist);
-void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], float *dist);
+void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], const float *dist);
void ED_view3d_from_object(
const struct Object *ob, float ofs[3], float quat[4], float *dist, float *lens);
@@ -502,7 +502,7 @@ bool ED_view3d_autodist_simple(struct ARegion *region,
const int mval[2],
float mouse_worldloc[3],
int margin,
- float *force_depth);
+ const float *force_depth);
bool ED_view3d_autodist_depth(struct ARegion *region, const int mval[2], int margin, float *depth);
bool ED_view3d_autodist_depth_seg(struct ARegion *region,
const int mval_sta[2],
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 7fd45e06fbf..7ce74a87ea0 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -293,12 +293,13 @@ enum {
/* 16 to copy ICON_DEFAULT_HEIGHT */
#define UI_DPI_ICON_SIZE ((float)16 * UI_DPI_FAC)
-/* Button types, bits stored in 1 value... and a short even!
- * - bits 0-4: bitnr (0-31)
+/**
+ * Button types, bits stored in 1 value... and a short even!
+ * - bits 0-4: #uiBut.bitnr (0-31)
* - bits 5-7: pointer type
* - bit 8: for 'bit'
* - bit 9-15: button type (now 6 bits, 64 types)
- * */
+ */
typedef enum {
UI_BUT_POIN_CHAR = 32,
UI_BUT_POIN_SHORT = 64,
@@ -1505,7 +1506,7 @@ uiBut *uiDefHotKeyevtButS(uiBlock *block,
short width,
short height,
short *keypoin,
- short *modkeypoin,
+ const short *modkeypoin,
const char *tip);
uiBut *uiDefSearchBut(uiBlock *block,
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index 6c1f9d4f017..ca89c5f606f 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -737,8 +737,8 @@ static bool ui_but_update_from_old_block(const bContext *C,
#else
BLI_assert(*but_old_p == NULL || BLI_findindex(&oldblock->buttons, *but_old_p) != -1);
- /* fastpath - avoid loop-in-loop, calling 'ui_but_find_old'
- * as long as old/new buttons are aligned */
+ /* Fast-path - avoid loop-in-loop, calling #ui_but_find_old
+ * as long as old/new buttons are aligned. */
if (LIKELY(*but_old_p && ui_but_equals_old(but, *but_old_p))) {
oldbut = *but_old_p;
}
@@ -2308,7 +2308,7 @@ bool ui_but_is_rna_valid(uiBut *but)
}
/**
- * Checks if the button supports ctrl+mousewheel cycling
+ * Checks if the button supports cycling next/previous menu items (ctrl+mouse-wheel).
*/
bool ui_but_supports_cycling(const uiBut *but)
{
@@ -6262,7 +6262,7 @@ uiBut *uiDefHotKeyevtButS(uiBlock *block,
short width,
short height,
short *keypoin,
- short *modkeypoin,
+ const short *modkeypoin,
const char *tip)
{
uiBut *but = ui_def_but(block,
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index ccbbaf40992..bad833265d9 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -8935,11 +8935,11 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
my = event->y;
ui_window_to_block(region, listbox->block, &mx, &my);
- /* convert pan to scrollwheel */
+ /* Convert pan to scroll-wheel. */
if (type == MOUSEPAN) {
ui_pan_to_scroll(event, &type, &val);
- /* if type still is mousepan, we call it handled, since delta-y accumulate */
+ /* If type still is mouse-pan, we call it handled, since delta-y accumulate. */
/* also see wm_event_system.c do_wheel_ui hack */
if (type == MOUSEPAN) {
retval = WM_UI_HANDLER_BREAK;
@@ -9666,7 +9666,7 @@ static int ui_handle_menu_event(bContext *C,
int type = event->type;
int val = event->val;
- /* convert pan to scrollwheel */
+ /* Convert pan to scroll-wheel. */
if (type == MOUSEPAN) {
ui_pan_to_scroll(event, &type, &val);
}
@@ -9691,7 +9691,7 @@ static int ui_handle_menu_event(bContext *C,
case EVT_PAGEDOWNKEY:
case EVT_HOMEKEY:
case EVT_ENDKEY:
- /* arrowkeys: only handle for block_loop blocks */
+ /* Arrow-keys: only handle for block_loop blocks. */
if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) {
/* pass */
}
@@ -9699,7 +9699,7 @@ static int ui_handle_menu_event(bContext *C,
int type = event->type;
int val = event->val;
- /* convert pan to scrollwheel */
+ /* Convert pan to scroll-wheel. */
if (type == MOUSEPAN) {
ui_pan_to_scroll(event, &type, &val);
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 8f98f380854..3c0b5bd3027 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -362,12 +362,13 @@ static bool id_search_add(const bContext *C,
*/
char name_ui[MAX_ID_FULL_NAME_UI];
int iconid = ui_id_icon_get(C, id, template_ui->preview);
- bool has_sep_char = (id->lib != NULL);
+ const bool use_lib_prefix = template_ui->preview || iconid;
+ const bool has_sep_char = (id->lib != NULL);
/* When using previews, the library hint (linked, overridden, missing) is added with a
* character prefix, otherwise we can use a icon. */
- BKE_id_full_name_ui_prefix_get(name_ui, id, template_ui->preview, UI_SEP_CHAR);
- if (!template_ui->preview) {
+ BKE_id_full_name_ui_prefix_get(name_ui, id, use_lib_prefix, UI_SEP_CHAR);
+ if (!use_lib_prefix) {
iconid = UI_library_icon_get(id);
}
@@ -521,7 +522,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
switch (event) {
case UI_ID_BROWSE:
case UI_ID_PIN:
- RNA_warning("warning, id event %d shouldnt come here", event);
+ RNA_warning("warning, id event %d shouldn't come here", event);
break;
case UI_ID_OPEN:
case UI_ID_ADD_NEW:
@@ -558,7 +559,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
case UI_ID_LOCAL:
if (id) {
Main *bmain = CTX_data_main(C);
- if (BKE_lib_override_library_is_enabled() && CTX_wm_window(C)->eventstate->shift) {
+ if (CTX_wm_window(C)->eventstate->shift) {
if (ID_IS_OVERRIDABLE_LIBRARY(id)) {
/* Only remap that specific ID usage to overriding local data-block. */
ID *override_id = BKE_lib_override_library_create_from_id(bmain, id, false);
@@ -568,6 +569,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* Assign new pointer, takes care of updates/notifiers */
RNA_id_pointer_create(override_id, &idptr);
}
+ undo_push_label = "Make Library Override";
}
}
else {
@@ -576,11 +578,13 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template_ui->ptr, template_ui->prop);
+ undo_push_label = "Make Local";
}
}
- RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
- RNA_property_update(C, &template_ui->ptr, template_ui->prop);
- undo_push_label = "Make Local";
+ if (undo_push_label != NULL) {
+ RNA_property_pointer_set(&template_ui->ptr, template_ui->prop, idptr, NULL);
+ RNA_property_update(C, &template_ui->ptr, template_ui->prop);
+ }
}
break;
case UI_ID_OVERRIDE:
@@ -930,10 +934,8 @@ static void template_ID(const bContext *C,
0,
0,
0,
- BKE_lib_override_library_is_enabled() ?
- TIP_("Direct linked library data-block, click to make local, "
- "Shift + Click to create a library override") :
- TIP_("Direct linked library data-block, click to make local"));
+ TIP_("Direct linked library data-block, click to make local, "
+ "Shift + Click to create a library override"));
if (disabled) {
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
@@ -2186,8 +2188,13 @@ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C)
char panel_idname[MAX_NAME];
shaderfx_panel_id(fx, panel_idname);
+ /* Create custom data RNA pointer. */
+ PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr);
+
Panel *new_panel = UI_panel_add_instanced(
- sa, region, &region->panels, panel_idname, i, NULL);
+ sa, region, &region->panels, panel_idname, i, fx_ptr);
+
if (new_panel != NULL) {
UI_panel_set_expand_from_list_data(C, new_panel);
}
@@ -2200,6 +2207,27 @@ void uiTemplateShaderFx(uiLayout *UNUSED(layout), bContext *C)
UI_panel_set_expand_from_list_data(C, panel);
}
}
+
+ /* Assuming there's only one group of instanced panels, update the custom data pointers. */
+ Panel *panel = region->panels.first;
+ LISTBASE_FOREACH (ShaderFxData *, fx, shaderfx) {
+ const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(fx->type);
+ if (fxi->panelRegister == NULL) {
+ continue;
+ }
+
+ /* Move to the next instanced panel corresponding to the next modifier. */
+ while ((panel->type == NULL) || !(panel->type->flag & PNL_INSTANCED)) {
+ panel = panel->next;
+ BLI_assert(panel != NULL); /* There shouldn't be fewer panels than modifiers with UIs. */
+ }
+
+ PointerRNA *fx_ptr = MEM_mallocN(sizeof(PointerRNA), "panel customdata");
+ RNA_pointer_create(&ob->id, &RNA_ShaderFx, fx, fx_ptr);
+ UI_panel_custom_data_set(panel, fx_ptr);
+
+ panel = panel->next;
+ }
}
}
@@ -4821,6 +4849,7 @@ static void CurveProfile_buttons_layout(uiLayout *layout, PointerRNA *ptr, RNAUp
/* Preset selector */
/* There is probably potential to use simpler "uiItemR" functions here, but automatic updating
* after a preset is selected would be more complicated. */
+ row = uiLayoutRow(layout, true);
bt = uiDefBlockBut(
block, CurveProfile_buttons_presets, profile, "Preset", 0, 0, UI_UNIT_X, UI_UNIT_X, "");
UI_but_funcN_set(bt, rna_update_cb, MEM_dupallocN(cb), NULL);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 31f8c89c2bc..c8f2bec145b 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -4584,7 +4584,7 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu
if ((but->drawflag & (UI_BUT_TEXT_LEFT | UI_BUT_TEXT_RIGHT)) == 0) {
but->drawflag |= UI_BUT_TEXT_LEFT;
}
- /* widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the
+ /* #widget_optionbut() carefully sets the text rectangle for fine tuned paddings. If the
* text drawing were to add its own padding, DPI and zoom factor would be applied twice
* in the final padding, so it's difficult to control it. */
but->drawflag |= UI_BUT_NO_TEXT_PADDING;
diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c
index a2c83c24e96..c37a7279773 100644
--- a/source/blender/editors/interface/view2d_ops.c
+++ b/source/blender/editors/interface/view2d_ops.c
@@ -68,7 +68,7 @@ static bool view2d_poll(bContext *C)
/**
* This group of operators come in several forms:
* -# Modal 'dragging' with MMB - where movement of mouse dictates amount to pan view by
- * -# Scrollwheel 'steps' - rolling mousewheel by one step moves view by predefined amount
+ * -# Scroll-wheel 'steps' - rolling mouse-wheel by one step moves view by predefined amount
*
* In order to make sure this works, each operator must define the following RNA-Operator Props:
* - `deltax, deltay` - define how much to move view by (relative to zoom-correction factor)
@@ -738,8 +738,8 @@ static void VIEW2D_OT_scroll_up(wmOperatorType *ot)
/**
* This group of operators come in several forms:
- * -# Scrollwheel 'steps' - rolling mousewheel by one step zooms view by predefined amount.
- * -# Scrollwheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y).
+ * -# Scroll-wheel 'steps' - rolling mouse-wheel by one step zooms view by predefined amount.
+ * -# Scroll-wheel 'steps' + alt + ctrl/shift - zooms view on one axis only (ctrl=x, shift=y).
* XXX this could be implemented...
* -# Pad +/- Keys - pressing each key moves the zooms the view by a predefined amount.
*
diff --git a/source/blender/editors/lattice/editlattice_select.c b/source/blender/editors/lattice/editlattice_select.c
index 0b62934d190..49cf4779496 100644
--- a/source/blender/editors/lattice/editlattice_select.c
+++ b/source/blender/editors/lattice/editlattice_select.c
@@ -268,7 +268,7 @@ void LATTICE_OT_select_mirror(wmOperatorType *ot)
* \{ */
static bool lattice_test_bitmap_uvw(
- Lattice *lt, BLI_bitmap *selpoints, int u, int v, int w, const bool selected)
+ Lattice *lt, const BLI_bitmap *selpoints, int u, int v, int w, const bool selected)
{
if ((u < 0 || u >= lt->pntsu) || (v < 0 || v >= lt->pntsv) || (w < 0 || w >= lt->pntsw)) {
return false;
diff --git a/source/blender/editors/mask/mask_add.c b/source/blender/editors/mask/mask_add.c
index b7fd661d8e6..c19a5b8ef68 100644
--- a/source/blender/editors/mask/mask_add.c
+++ b/source/blender/editors/mask/mask_add.c
@@ -439,7 +439,7 @@ static bool add_vertex_new(const bContext *C, Mask *mask, MaskLayer *mask_layer,
/* Convert coordinate from normalized space to pixel one.
* TODO(sergey): Make the function more generally available. */
static void mask_point_make_pixel_space(bContext *C,
- float point_normalized[2],
+ const float point_normalized[2],
float point_pixel[2])
{
ScrArea *area = CTX_wm_area(C);
diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c
index adc3be6b2ac..0394874e8c1 100644
--- a/source/blender/editors/mesh/editmesh_knife.c
+++ b/source/blender/editors/mesh/editmesh_knife.c
@@ -102,7 +102,7 @@ typedef struct KnifeVert {
ListBase edges;
ListBase faces;
- float co[3], cageco[3], sco[2]; /* sco is screen coordinates for cageco */
+ float co[3], cageco[3];
bool is_face, in_space;
bool is_cut; /* along a cut created by user input (will draw too) */
} KnifeVert;
@@ -429,8 +429,6 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const
copy_v3_v3(kfv->co, co);
copy_v3_v3(kfv->cageco, cageco);
- knife_project_v2(kcd, kfv->cageco, kfv->sco);
-
return kfv;
}
@@ -1548,8 +1546,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
{
SmallHash faces, kfes, kfvs;
float v1[3], v2[3], v3[3], v4[3], s1[2], s2[2];
- BVHTree *planetree, *tree;
- BVHTreeOverlap *results, *result;
+ BVHTree *tree;
+ int *results, *result;
BMLoop **ls;
BMFace *f;
KnifeEdge *kfe;
@@ -1562,7 +1560,6 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
KnifeLineHit hit;
void *val;
void **val_p;
- float plane_cos[12];
float s[2], se1[2], se2[2], sint[2];
float r1[3], r2[3];
float d, d1, d2, lambda;
@@ -1623,22 +1620,22 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
clip_to_ortho_planes(v2, v4, kcd->ortho_extent_center, kcd->ortho_extent + 10.0f);
}
+ float plane[4];
+ {
+ float v1_v2[3], v1_v3[3];
+ sub_v3_v3v3(v1_v2, v2, v1);
+ sub_v3_v3v3(v1_v3, v3, v1);
+ cross_v3_v3v3(plane, v1_v2, v1_v3);
+ plane_from_point_normal_v3(plane, v1, plane);
+ }
+
/* First use bvh tree to find faces, knife edges, and knife verts that might
* intersect the cut plane with rays v1-v3 and v2-v4.
* This deduplicates the candidates before doing more expensive intersection tests. */
tree = BKE_bmbvh_tree_get(kcd->bmbvh);
- planetree = BLI_bvhtree_new(4, FLT_EPSILON * 4, 8, 8);
- copy_v3_v3(plane_cos + 0, v1);
- copy_v3_v3(plane_cos + 3, v2);
- copy_v3_v3(plane_cos + 6, v3);
- copy_v3_v3(plane_cos + 9, v4);
- BLI_bvhtree_insert(planetree, 0, plane_cos, 4);
- BLI_bvhtree_balance(planetree);
-
- results = BLI_bvhtree_overlap(tree, planetree, &tot, NULL, NULL);
+ results = BLI_bvhtree_intersect_plane(tree, plane, &tot);
if (!results) {
- BLI_bvhtree_free(planetree);
return;
}
@@ -1647,9 +1644,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_init(&kfvs);
for (i = 0, result = results; i < tot; i++, result++) {
- ls = (BMLoop **)kcd->em->looptris[result->indexA];
+ ls = (BMLoop **)kcd->em->looptris[*result];
f = ls[0]->f;
- set_lowest_face_tri(kcd, f, result->indexA);
+ set_lowest_face_tri(kcd, f, *result);
/* occlude but never cut unselected faces (when only_select is used) */
if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) {
@@ -1834,7 +1831,6 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd)
BLI_smallhash_release(&faces);
BLI_smallhash_release(&kfes);
BLI_smallhash_release(&kfvs);
- BLI_bvhtree_free(planetree);
if (results) {
MEM_freeN(results);
}
@@ -1928,10 +1924,11 @@ static int knife_sample_screen_density(KnifeTool_OpData *kcd, const float radius
for (i = 0; i < 2; i++) {
KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv->sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- dis_sq = len_squared_v2v2(kfv->sco, sco);
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
if (dis_sq < radius_sq) {
if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
@@ -1961,11 +1958,12 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize)
}
/* p is closest point on edge to the mouse cursor */
-static KnifeEdge *knife_find_closest_edge(
- KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, bool *is_space)
+static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd,
+ BMFace *f,
+ float p[3],
+ float cagep[3])
{
- BMFace *f;
- float co[3], cageco[3], sco[2];
+ float sco[2];
float maxdist;
if (kcd->is_interactive) {
@@ -1979,127 +1977,105 @@ static KnifeEdge *knife_find_closest_edge(
maxdist = KNIFE_FLT_EPS;
}
- f = knife_find_closest_face(kcd, co, cageco, NULL);
- *is_space = !f;
-
- kcd->curr.bmface = f;
-
- if (f) {
- const float maxdist_sq = maxdist * maxdist;
- KnifeEdge *cure = NULL;
- float cur_cagep[3];
- ListBase *lst;
- Ref *ref;
- float dis_sq, curdis_sq = FLT_MAX;
-
- /* set p to co, in case we don't find anything, means a face cut */
- copy_v3_v3(p, co);
- copy_v3_v3(cagep, cageco);
-
- knife_project_v2(kcd, cageco, sco);
-
- /* look through all edges associated with this face */
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- float test_cagep[3];
- float lambda;
-
- /* project edge vertices into screen space */
- knife_project_v2(kcd, kfe->v1->cageco, kfe->v1->sco);
- knife_project_v2(kcd, kfe->v2->cageco, kfe->v2->sco);
-
- /* check if we're close enough and calculate 'lambda' */
- if (kcd->is_angle_snapping) {
- /* if snapping, check we're in bounds */
- float sco_snap[2];
- isect_line_line_v2_point(
- kfe->v1->sco, kfe->v2->sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
- lambda = line_point_factor_v2(sco_snap, kfe->v1->sco, kfe->v2->sco);
-
- /* be strict about angle-snapping within edge */
- if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
- continue;
- }
+ const float maxdist_sq = maxdist * maxdist;
+ KnifeEdge *cure = NULL;
+ float cur_cagep[3];
+ ListBase *lst;
+ Ref *ref;
+ float dis_sq, curdis_sq = FLT_MAX;
+
+ knife_project_v2(kcd, cagep, sco);
+
+ /* look through all edges associated with this face */
+ lst = knife_get_face_kedges(kcd, f);
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ float kfv1_sco[2], kfv2_sco[2], test_cagep[3];
+ float lambda;
+
+ /* project edge vertices into screen space */
+ knife_project_v2(kcd, kfe->v1->cageco, kfv1_sco);
+ knife_project_v2(kcd, kfe->v2->cageco, kfv2_sco);
+
+ /* check if we're close enough and calculate 'lambda' */
+ if (kcd->is_angle_snapping) {
+ /* if snapping, check we're in bounds */
+ float sco_snap[2];
+ isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap);
+ lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco);
+
+ /* be strict about angle-snapping within edge */
+ if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) {
+ continue;
+ }
- dis_sq = len_squared_v2v2(sco, sco_snap);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- /* we already have 'lambda' */
- }
- else {
- continue;
- }
+ dis_sq = len_squared_v2v2(sco, sco_snap);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ /* we already have 'lambda' */
}
else {
- dis_sq = dist_squared_to_line_segment_v2(sco, kfe->v1->sco, kfe->v2->sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- lambda = line_point_factor_v2(sco, kfe->v1->sco, kfe->v2->sco);
- }
- else {
- continue;
- }
+ continue;
}
-
- /* now we have 'lambda' calculated (in screen-space) */
- knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda);
-
- if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
- /* check we're in the view */
- if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) {
- continue;
- }
+ }
+ else {
+ dis_sq = dist_squared_to_line_segment_v2(sco, kfv1_sco, kfv2_sco);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ lambda = line_point_factor_v2(sco, kfv1_sco, kfv2_sco);
+ }
+ else {
+ continue;
}
-
- cure = kfe;
- curdis_sq = dis_sq;
- copy_v3_v3(cur_cagep, test_cagep);
}
- if (fptr) {
- *fptr = f;
+ /* now we have 'lambda' calculated (in screen-space) */
+ knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda);
+
+ if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
+ /* check we're in the view */
+ if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) {
+ continue;
+ }
}
- if (cure) {
- if (!kcd->ignore_edge_snapping || !(cure->e)) {
- KnifeVert *edgesnap = NULL;
+ cure = kfe;
+ curdis_sq = dis_sq;
+ copy_v3_v3(cur_cagep, test_cagep);
+ }
- if (kcd->snap_midpoints) {
- mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
- mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
- }
- else {
- float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
- copy_v3_v3(cagep, cur_cagep);
- interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
- }
+ if (cure) {
+ if (!kcd->ignore_edge_snapping || !(cure->e)) {
+ KnifeVert *edgesnap = NULL;
- /* update mouse coordinates to the snapped-to edge's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- edgesnap = new_knife_vert(kcd, p, cagep);
- kcd->curr.mval[0] = edgesnap->sco[0];
- kcd->curr.mval[1] = edgesnap->sco[1];
+ if (kcd->snap_midpoints) {
+ mid_v3_v3v3(p, cure->v1->co, cure->v2->co);
+ mid_v3_v3v3(cagep, cure->v1->cageco, cure->v2->cageco);
}
else {
- return NULL;
+ float lambda = line_point_factor_v3(cur_cagep, cure->v1->cageco, cure->v2->cageco);
+ copy_v3_v3(cagep, cur_cagep);
+ interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda);
}
- }
-
- return cure;
- }
- if (fptr) {
- *fptr = NULL;
+ /* update mouse coordinates to the snapped-to edge's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ edgesnap = new_knife_vert(kcd, p, cagep);
+ knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval);
+ }
+ else {
+ return NULL;
+ }
}
- return NULL;
+ return cure;
}
/* find a vertex near the mouse cursor, if it exists */
-static KnifeVert *knife_find_closest_vert(
- KnifeTool_OpData *kcd, float p[3], float cagep[3], BMFace **fptr, bool *is_space)
+static KnifeVert *knife_find_closest_vert_of_face(KnifeTool_OpData *kcd,
+ BMFace *f,
+ float p[3],
+ float cagep[3])
{
- BMFace *f;
- float co[3], cageco[3], sco[2];
+ float sco[2];
float maxdist;
if (kcd->is_interactive) {
@@ -2112,84 +2088,58 @@ static KnifeVert *knife_find_closest_vert(
maxdist = KNIFE_FLT_EPS;
}
- f = knife_find_closest_face(kcd, co, cageco, is_space);
-
- kcd->curr.bmface = f;
-
- if (f) {
- const float maxdist_sq = maxdist * maxdist;
- ListBase *lst;
- Ref *ref;
- KnifeVert *curv = NULL;
- float dis_sq, curdis_sq = FLT_MAX;
-
- /* set p to co, in case we don't find anything, means a face cut */
- copy_v3_v3(p, co);
- copy_v3_v3(cagep, cageco);
+ const float maxdist_sq = maxdist * maxdist;
+ ListBase *lst;
+ Ref *ref;
+ KnifeVert *curv = NULL;
+ float cur_kfv_sco[2];
+ float dis_sq, curdis_sq = FLT_MAX;
- knife_project_v2(kcd, cageco, sco);
+ knife_project_v2(kcd, cagep, sco);
- lst = knife_get_face_kedges(kcd, f);
- for (ref = lst->first; ref; ref = ref->next) {
- KnifeEdge *kfe = ref->ref;
- int i;
+ lst = knife_get_face_kedges(kcd, f);
+ for (ref = lst->first; ref; ref = ref->next) {
+ KnifeEdge *kfe = ref->ref;
+ int i;
- for (i = 0; i < 2; i++) {
- KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ for (i = 0; i < 2; i++) {
+ KnifeVert *kfv = i ? kfe->v2 : kfe->v1;
+ float kfv_sco[2];
- knife_project_v2(kcd, kfv->cageco, kfv->sco);
+ knife_project_v2(kcd, kfv->cageco, kfv_sco);
- /* be strict about angle snapping, the vertex needs to be very close to the angle,
- * or we ignore */
- if (kcd->is_angle_snapping) {
- if (dist_squared_to_line_segment_v2(kfv->sco, kcd->prev.mval, kcd->curr.mval) >
- KNIFE_FLT_EPSBIG) {
- continue;
- }
+ /* be strict about angle snapping, the vertex needs to be very close to the angle,
+ * or we ignore */
+ if (kcd->is_angle_snapping) {
+ if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) >
+ KNIFE_FLT_EPSBIG) {
+ continue;
}
+ }
- dis_sq = len_squared_v2v2(kfv->sco, sco);
- if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
- if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) {
- if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) {
- curv = kfv;
- curdis_sq = dis_sq;
- }
- }
- else {
- curv = kfv;
- curdis_sq = dis_sq;
- }
+ dis_sq = len_squared_v2v2(kfv_sco, sco);
+ if (dis_sq < curdis_sq && dis_sq < maxdist_sq) {
+ if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) ||
+ !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) {
+ curv = kfv;
+ curdis_sq = dis_sq;
+ copy_v2_v2(cur_kfv_sco, kfv_sco);
}
}
}
+ }
- if (!kcd->ignore_vert_snapping || !(curv && curv->v)) {
- if (fptr) {
- *fptr = f;
- }
-
- if (curv) {
- copy_v3_v3(p, curv->co);
- copy_v3_v3(cagep, curv->cageco);
-
- /* update mouse coordinates to the snapped-to vertex's screen coordinates
- * this is important for angle snap, which uses the previous mouse position */
- kcd->curr.mval[0] = curv->sco[0];
- kcd->curr.mval[1] = curv->sco[1];
- }
-
- return curv;
- }
+ if (!kcd->ignore_vert_snapping || !(curv && curv->v)) {
+ if (curv) {
+ copy_v3_v3(p, curv->co);
+ copy_v3_v3(cagep, curv->cageco);
- if (fptr) {
- *fptr = f;
+ /* update mouse coordinates to the snapped-to vertex's screen coordinates
+ * this is important for angle snap, which uses the previous mouse position */
+ copy_v2_v2(kcd->curr.mval, cur_kfv_sco);
}
- return NULL;
- }
- if (fptr) {
- *fptr = NULL;
+ return curv;
}
return NULL;
@@ -2236,11 +2186,10 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd)
return true;
}
-/* update active knife edge/vert pointers */
-static int knife_update_active(KnifeTool_OpData *kcd)
+static void knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2])
{
knife_pos_data_clear(&kcd->curr);
- copy_v2_v2(kcd->curr.mval, kcd->mval);
+ copy_v2_v2(kcd->curr.mval, mval);
/* view matrix may have changed, reproject */
knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval);
@@ -2252,15 +2201,26 @@ static int knife_update_active(KnifeTool_OpData *kcd)
kcd->is_angle_snapping = false;
}
- kcd->curr.vert = knife_find_closest_vert(
- kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space);
+ kcd->curr.bmface = knife_find_closest_face(
+ kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space);
+
+ if (kcd->curr.bmface) {
+ kcd->curr.vert = knife_find_closest_vert_of_face(
+ kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
- if (!kcd->curr.vert &&
- /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */
- !kcd->is_drag_hold) {
- kcd->curr.edge = knife_find_closest_edge(
- kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.bmface, &kcd->curr.is_space);
+ if (!kcd->curr.vert &&
+ /* no edge snapping while dragging (edges are too sticky when cuts are immediate) */
+ !kcd->is_drag_hold) {
+ kcd->curr.edge = knife_find_closest_edge_of_face(
+ kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage);
+ }
}
+}
+
+/* update active knife edge/vert pointers */
+static int knife_update_active(KnifeTool_OpData *kcd)
+{
+ knife_snap_update_from_mval(kcd, kcd->mval);
/* if no hits are found this would normally default to (0, 0, 0) so instead
* get a point at the mouse ray closest to the previous point.
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index b60e0074512..0cc4dcef442 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -778,6 +778,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
UvElement *newvlist = NULL, *vlist = element_map->vert[i];
UvElement *iterv, *v, *lastv, *next;
float *uv, *uv2, uvdiff[2];
+ bool uv_vert_sel, uv2_vert_sel;
while (vlist) {
v = vlist;
@@ -788,6 +789,7 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = v->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv = luv->uv;
+ uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
lastv = NULL;
iterv = vlist;
@@ -798,12 +800,17 @@ UvElementMap *BM_uv_element_map_create(BMesh *bm,
l = iterv->l;
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
uv2 = luv->uv;
+ uv2_vert_sel = luv->flag & MLOOPUV_VERTSEL;
sub_v2_v2v2(uvdiff, uv2, uv);
- if (fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT && fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT &&
- (!use_winding ||
- winding[BM_elem_index_get(iterv->l->f)] == winding[BM_elem_index_get(v->l->f)])) {
+ /* Check if the uv loops share the same selection state (if not, they are not connected as
+ * they have been ripped or other edit commands have seperated them). */
+ bool connected = uv_vert_sel == uv2_vert_sel && fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT &&
+ fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT;
+
+ if (connected && (!use_winding || winding[BM_elem_index_get(iterv->l->f)] ==
+ winding[BM_elem_index_get(v->l->f)])) {
if (lastv) {
lastv->next = next;
}
@@ -1029,7 +1036,7 @@ bool EDBM_vert_color_check(BMEditMesh *em)
/** \name Mirror Cache API
* \{ */
-static BMVert *cache_mirr_intptr_as_bmvert(intptr_t *index_lookup, int index)
+static BMVert *cache_mirr_intptr_as_bmvert(const intptr_t *index_lookup, int index)
{
intptr_t eve_i = index_lookup[index];
return (eve_i == -1) ? NULL : (BMVert *)eve_i;
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index bebad312454..cec425d687d 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -76,6 +76,7 @@ struct BMElem *EDBM_elem_from_selectmode(struct BMEditMesh *em,
struct BMVert *eve,
struct BMEdge *eed,
struct BMFace *efa);
+
int EDBM_elem_to_index_any(struct BMEditMesh *em, struct BMElem *ele);
struct BMElem *EDBM_elem_from_index_any(struct BMEditMesh *em, int index);
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index b0730b32bed..4d84db9b35b 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -297,6 +297,37 @@ static void join_mesh_single(Depsgraph *depsgraph,
*mpoly_pp += me->totpoly;
}
+/* Face Sets IDs are a sparse sequence, so this function offsets all the IDs by face_set_offset and
+ * updates face_set_offset with the maximum ID value. This way, when used in multiple meshes, all
+ * of them will have different IDs for their Face Sets. */
+static void mesh_join_offset_face_sets_ID(const Mesh *mesh, int *face_set_offset)
+{
+ if (!mesh->totpoly) {
+ return;
+ }
+
+ int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
+ if (!face_sets) {
+ return;
+ }
+
+ int max_face_set = 0;
+ for (int f = 0; f < mesh->totpoly; f++) {
+ /* As face sets encode the visibility in the integer sign, the offset needs to be added or
+ * subtracted depending on the initial sign of the integer to get the new ID. */
+ if (abs(face_sets[f]) <= *face_set_offset) {
+ if (face_sets[f] > 0) {
+ face_sets[f] += *face_set_offset;
+ }
+ else {
+ face_sets[f] -= *face_set_offset;
+ }
+ }
+ max_face_set = max_ii(max_face_set, abs(face_sets[f]));
+ }
+ *face_set_offset = max_face_set;
+}
+
int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -431,7 +462,13 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
key->type = KEY_RELATIVE;
}
- /* First pass over objects: Copying materials, vertex-groups & face-maps across. */
+ /* Update face_set_id_offset with the face set data in the active object first. This way the Face
+ * Sets IDs in the active object are not the ones that are modified. */
+ Mesh *mesh_active = BKE_mesh_from_object(ob);
+ int face_set_id_offset = 0;
+ mesh_join_offset_face_sets_ID(mesh_active, &face_set_id_offset);
+
+ /* Copy materials, vertex-groups, face sets & face-maps across objects. */
CTX_DATA_BEGIN (C, Object *, ob_iter, selected_editable_objects) {
/* only act if a mesh, and not the one we're joining to */
if ((ob != ob_iter) && (ob_iter->type == OB_MESH)) {
@@ -463,6 +500,8 @@ int ED_mesh_join_objects_exec(bContext *C, wmOperator *op)
ob->actfmap = 1;
}
+ mesh_join_offset_face_sets_ID(me, &face_set_id_offset);
+
if (me->totvert) {
/* Add this object's materials to the base one's if they don't exist already
* (but only if limits not exceeded yet) */
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 3618eae7db9..90ea71ae5c5 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -670,12 +670,12 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
if (!ob) {
CTX_wm_operator_poll_msg_set(C, "Context missing active object");
- return 0;
+ return false;
}
if (ID_IS_LINKED(ob) || (ptr.owner_id && ID_IS_LINKED(ptr.owner_id))) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit library data");
- return 0;
+ return false;
}
if (ID_IS_OVERRIDE_LIBRARY(ob) && ptr.data != NULL) {
@@ -683,7 +683,7 @@ static bool edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
return (((bConstraint *)ptr.data)->flag & CONSTRAINT_OVERRIDE_LIBRARY_LOCAL) != 0;
}
- return 1;
+ return true;
}
static bool edit_constraint_poll(bContext *C)
@@ -702,7 +702,7 @@ static void edit_constraint_properties(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
+static bool edit_constraint_invoke_properties(bContext *C, wmOperator *op)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "constraint", &RNA_Constraint);
Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C);
@@ -711,7 +711,7 @@ static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
if (RNA_struct_property_is_set(op->ptr, "constraint") &&
RNA_struct_property_is_set(op->ptr, "owner")) {
- return 1;
+ return true;
}
if (ptr.data) {
@@ -727,10 +727,10 @@ static int edit_constraint_invoke_properties(bContext *C, wmOperator *op)
RNA_enum_set(op->ptr, "owner", EDIT_CONSTRAINT_OWNER_BONE);
}
- return 1;
+ return true;
}
- return 0;
+ return false;
}
static bConstraint *edit_constraint_property_get(wmOperator *op, Object *ob, int type)
@@ -1683,7 +1683,7 @@ void OBJECT_OT_constraints_clear(wmOperatorType *ot)
/* callbacks */
ot->exec = object_constraints_clear_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
}
/** \} */
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index a9eb454eb04..04113f70e52 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -895,7 +895,7 @@ void ED_object_check_force_modifiers(Main *bmain, Scene *scene, Object *object)
else {
if (!pd || (pd->shape != PFIELD_SHAPE_SURFACE) ||
ELEM(pd->forcefield, 0, PFIELD_GUIDE, PFIELD_TEXTURE)) {
- ED_object_modifier_remove(NULL, bmain, object, md);
+ ED_object_modifier_remove(NULL, bmain, scene, object, md);
}
}
}
diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h
index afc87c0caba..9dc204b9083 100644
--- a/source/blender/editors/object/object_intern.h
+++ b/source/blender/editors/object/object_intern.h
@@ -167,6 +167,7 @@ void OBJECT_OT_modifier_move_up(struct wmOperatorType *ot);
void OBJECT_OT_modifier_move_down(struct wmOperatorType *ot);
void OBJECT_OT_modifier_move_to_index(struct wmOperatorType *ot);
void OBJECT_OT_modifier_apply(struct wmOperatorType *ot);
+void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot);
void OBJECT_OT_modifier_convert(struct wmOperatorType *ot);
void OBJECT_OT_modifier_copy(struct wmOperatorType *ot);
void OBJECT_OT_multires_subdivide(struct wmOperatorType *ot);
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 74109563929..6f254ea9400 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -330,10 +330,8 @@ static bool object_modifier_safe_to_delete(Main *bmain,
!ED_object_iter_other(bmain, ob, false, object_has_modifier_cb, &type));
}
-static bool object_modifier_remove(Main *bmain,
- Object *ob,
- ModifierData *md,
- bool *r_sort_depsgraph)
+static bool object_modifier_remove(
+ Main *bmain, Scene *scene, Object *ob, ModifierData *md, bool *r_sort_depsgraph)
{
/* It seems on rapid delete it is possible to
* get called twice on same modifier, so make
@@ -344,11 +342,8 @@ static bool object_modifier_remove(Main *bmain,
/* special cases */
if (md->type == eModifierType_ParticleSystem) {
- ParticleSystemModifierData *psmd = (ParticleSystemModifierData *)md;
-
- BLI_remlink(&ob->particlesystem, psmd->psys);
- psys_free(ob, psmd->psys);
- psmd->psys = NULL;
+ object_remove_particle_system(bmain, scene, ob);
+ return true;
}
else if (md->type == eModifierType_Softbody) {
if (ob->soft) {
@@ -391,12 +386,13 @@ static bool object_modifier_remove(Main *bmain,
return 1;
}
-bool ED_object_modifier_remove(ReportList *reports, Main *bmain, Object *ob, ModifierData *md)
+bool ED_object_modifier_remove(
+ ReportList *reports, Main *bmain, Scene *scene, Object *ob, ModifierData *md)
{
bool sort_depsgraph = false;
bool ok;
- ok = object_modifier_remove(bmain, ob, md, &sort_depsgraph);
+ ok = object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph);
if (!ok) {
BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name);
@@ -409,7 +405,7 @@ bool ED_object_modifier_remove(ReportList *reports, Main *bmain, Object *ob, Mod
return 1;
}
-void ED_object_modifier_clear(Main *bmain, Object *ob)
+void ED_object_modifier_clear(Main *bmain, Scene *scene, Object *ob)
{
ModifierData *md = ob->modifiers.first;
bool sort_depsgraph = false;
@@ -423,7 +419,7 @@ void ED_object_modifier_clear(Main *bmain, Object *ob)
next_md = md->next;
- object_modifier_remove(bmain, ob, md, &sort_depsgraph);
+ object_modifier_remove(bmain, scene, ob, md, &sort_depsgraph);
md = next_md;
}
@@ -822,7 +818,8 @@ bool ED_object_modifier_apply(Main *bmain,
Scene *scene,
Object *ob,
ModifierData *md,
- int mode)
+ int mode,
+ bool keep_modifier)
{
int prev_mode;
@@ -830,7 +827,7 @@ bool ED_object_modifier_apply(Main *bmain,
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in edit mode");
return false;
}
- if (ID_REAL_USERS(ob->data) > 1) {
+ if (mode != MODIFIER_APPLY_SHAPE && ID_REAL_USERS(ob->data) > 1) {
BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data");
return false;
}
@@ -869,22 +866,34 @@ bool ED_object_modifier_apply(Main *bmain,
}
md_eval->mode = prev_mode;
- BLI_remlink(&ob->modifiers, md);
- BKE_modifier_free(md);
+
+ if (!keep_modifier) {
+ BLI_remlink(&ob->modifiers, md);
+ BKE_modifier_free(md);
+ }
BKE_object_free_derived_caches(ob);
return true;
}
-int ED_object_modifier_copy(ReportList *UNUSED(reports), Object *ob, ModifierData *md)
+int ED_object_modifier_copy(
+ ReportList *UNUSED(reports), Main *bmain, Scene *scene, Object *ob, ModifierData *md)
{
ModifierData *nmd;
- nmd = BKE_modifier_new(md->type);
- BKE_modifier_copydata(md, nmd);
- BLI_insertlinkafter(&ob->modifiers, md, nmd);
- BKE_modifier_unique_name(&ob->modifiers, nmd);
+ if (md->type == eModifierType_ParticleSystem) {
+ nmd = object_copy_particle_system(bmain, scene, ob, ((ParticleSystemModifierData *)md)->psys);
+ BLI_remlink(&ob->modifiers, nmd);
+ BLI_insertlinkafter(&ob->modifiers, md, nmd);
+ return true;
+ }
+ else {
+ nmd = BKE_modifier_new(md->type);
+ BKE_modifier_copydata(md, nmd);
+ BLI_insertlinkafter(&ob->modifiers, md, nmd);
+ BKE_modifier_unique_name(&ob->modifiers, nmd);
+ }
return 1;
}
@@ -1116,6 +1125,7 @@ ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type)
static int modifier_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
@@ -1129,7 +1139,7 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
char name[MAX_NAME];
strcpy(name, md->name);
- if (!ED_object_modifier_remove(op->reports, bmain, ob, md)) {
+ if (!ED_object_modifier_remove(op->reports, bmain, scene, ob, md)) {
return OPERATOR_CANCELLED;
}
@@ -1326,7 +1336,7 @@ void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot)
/** \name Apply Modifier Operator
* \{ */
-static bool modifier_apply_poll(bContext *C)
+static bool modifier_apply_poll_ex(bContext *C, bool allow_shared)
{
if (!edit_modifier_poll_generic(C, &RNA_Modifier, 0, false)) {
return false;
@@ -1341,7 +1351,7 @@ static bool modifier_apply_poll(bContext *C)
CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied on override data");
return false;
}
- if ((ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) {
+ if (!allow_shared && (ob->data != NULL) && ID_REAL_USERS(ob->data) > 1) {
CTX_wm_operator_poll_msg_set(C, "Modifiers cannot be applied to multi-user data");
return false;
}
@@ -1356,14 +1366,18 @@ static bool modifier_apply_poll(bContext *C)
return true;
}
-static int modifier_apply_exec(bContext *C, wmOperator *op)
+static bool modifier_apply_poll(bContext *C)
+{
+ return modifier_apply_poll_ex(C, false);
+}
+
+static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, bool keep_modifier)
{
Main *bmain = CTX_data_main(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
- int apply_as = RNA_enum_get(op->ptr, "apply_as");
if (md == NULL) {
return OPERATOR_CANCELLED;
@@ -1373,7 +1387,8 @@ static int modifier_apply_exec(bContext *C, wmOperator *op)
char name[MAX_NAME];
strcpy(name, md->name);
- if (!ED_object_modifier_apply(bmain, op->reports, depsgraph, scene, ob, md, apply_as)) {
+ if (!ED_object_modifier_apply(
+ bmain, op->reports, depsgraph, scene, ob, md, apply_as, keep_modifier)) {
return OPERATOR_CANCELLED;
}
@@ -1388,6 +1403,11 @@ static int modifier_apply_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static int modifier_apply_exec(bContext *C, wmOperator *op)
+{
+ return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_DATA, false);
+}
+
static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
int retval;
@@ -1397,16 +1417,6 @@ static int modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *eve
return retval;
}
-static const EnumPropertyItem modifier_apply_as_items[] = {
- {MODIFIER_APPLY_DATA, "DATA", 0, "Object Data", "Apply modifier to the object's data"},
- {MODIFIER_APPLY_SHAPE,
- "SHAPE",
- 0,
- "New Shape",
- "Apply deform-only modifier to a new shape on this object"},
- {0, NULL, 0, NULL, NULL},
-};
-
void OBJECT_OT_modifier_apply(wmOperatorType *ot)
{
ot->name = "Apply Modifier";
@@ -1420,12 +1430,68 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
- RNA_def_enum(ot->srna,
- "apply_as",
- modifier_apply_as_items,
- MODIFIER_APPLY_DATA,
- "Apply as",
- "How to apply the modifier to the geometry");
+ edit_modifier_properties(ot);
+ edit_modifier_report_property(ot);
+}
+
+/** \} */
+
+/* ------------------------------------------------------------------- */
+/** \name Apply Modifier As Shapekey Operator
+ * \{ */
+
+static bool modifier_apply_as_shapekey_poll(bContext *C)
+{
+ return modifier_apply_poll_ex(C, true);
+}
+
+static int modifier_apply_as_shapekey_exec(bContext *C, wmOperator *op)
+{
+ bool keep = RNA_boolean_get(op->ptr, "keep_modifier");
+
+ return modifier_apply_exec_ex(C, op, MODIFIER_APPLY_SHAPE, keep);
+}
+
+static int modifier_apply_as_shapekey_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ int retval;
+ if (edit_modifier_invoke_properties(C, op, event, &retval)) {
+ return modifier_apply_as_shapekey_exec(C, op);
+ }
+ else {
+ return retval;
+ }
+}
+
+static char *modifier_apply_as_shapekey_get_description(struct bContext *UNUSED(C),
+ struct wmOperatorType *UNUSED(op),
+ struct PointerRNA *values)
+{
+ bool keep = RNA_boolean_get(values, "keep_modifier");
+
+ if (keep) {
+ return BLI_strdup("Apply modifier as a new shapekey and keep it in the stack");
+ }
+
+ return NULL;
+}
+
+void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot)
+{
+ ot->name = "Apply Modifier As Shapekey";
+ ot->description = "Apply modifier as a new shapekey and remove from the stack";
+ ot->idname = "OBJECT_OT_modifier_apply_as_shapekey";
+
+ ot->invoke = modifier_apply_as_shapekey_invoke;
+ ot->exec = modifier_apply_as_shapekey_exec;
+ ot->poll = modifier_apply_as_shapekey_poll;
+ ot->get_description = modifier_apply_as_shapekey_get_description;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
+
+ RNA_def_boolean(
+ ot->srna, "keep_modifier", false, "Keep Modifier", "Do not remove the modifier from stack");
edit_modifier_properties(ot);
edit_modifier_report_property(ot);
}
@@ -1487,10 +1553,12 @@ void OBJECT_OT_modifier_convert(wmOperatorType *ot)
static int modifier_copy_exec(bContext *C, wmOperator *op)
{
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
Object *ob = ED_object_active_context(C);
ModifierData *md = edit_modifier_property_get(op, ob, 0);
- if (!md || !ED_object_modifier_copy(op->reports, ob, md)) {
+ if (!md || !ED_object_modifier_copy(op->reports, bmain, scene, ob, md)) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index e28bbb3fb1c..92880e5a114 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -130,6 +130,7 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_modifier_move_down);
WM_operatortype_append(OBJECT_OT_modifier_move_to_index);
WM_operatortype_append(OBJECT_OT_modifier_apply);
+ WM_operatortype_append(OBJECT_OT_modifier_apply_as_shapekey);
WM_operatortype_append(OBJECT_OT_modifier_convert);
WM_operatortype_append(OBJECT_OT_modifier_copy);
WM_operatortype_append(OBJECT_OT_multires_subdivide);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index d37f03b7f55..945c7e87eb1 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -1878,7 +1878,7 @@ static void single_obdata_users(
/* Needed to remap texcomesh below. */
me = ob->data = ID_NEW_SET(ob->data, BKE_mesh_copy(bmain, ob->data));
if (me->key) { /* We do not need to set me->key->id.newid here... */
- BKE_animdata_copy_id_action(bmain, (ID *)me->key, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)me->key);
}
break;
case OB_MBALL:
@@ -1891,13 +1891,13 @@ static void single_obdata_users(
ID_NEW_REMAP(cu->bevobj);
ID_NEW_REMAP(cu->taperobj);
if (cu->key) { /* We do not need to set cu->key->id.newid here... */
- BKE_animdata_copy_id_action(bmain, (ID *)cu->key, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)cu->key);
}
break;
case OB_LATTICE:
ob->data = lat = ID_NEW_SET(ob->data, BKE_lattice_copy(bmain, ob->data));
if (lat->key) { /* We do not need to set lat->key->id.newid here... */
- BKE_animdata_copy_id_action(bmain, (ID *)lat->key, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)lat->key);
}
break;
case OB_ARMATURE:
@@ -1937,7 +1937,7 @@ static void single_obdata_users(
* AnimData structure, which is not what we want.
* (sergey)
*/
- BKE_animdata_copy_id_action(bmain, (ID *)ob->data, false);
+ BKE_animdata_copy_id_action(bmain, (ID *)ob->data);
id_us_min(id);
}
@@ -1958,7 +1958,7 @@ static void single_object_action_users(
FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) {
if (!ID_IS_LINKED(ob)) {
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- BKE_animdata_copy_id_action(bmain, &ob->id, false);
+ BKE_animdata_copy_id_action(bmain, &ob->id);
}
}
FOREACH_OBJECT_FLAG_END;
@@ -1980,7 +1980,7 @@ static void single_mat_users(
if (ma->id.us > 1) {
man = BKE_material_copy(bmain, ma);
- BKE_animdata_copy_id_action(bmain, &man->id, false);
+ BKE_animdata_copy_id_action(bmain, &man->id);
man->id.us = 0;
BKE_object_material_assign(bmain, ob, man, a, BKE_MAT_ASSIGN_USERPREF);
@@ -2242,28 +2242,6 @@ void OBJECT_OT_make_local(wmOperatorType *ot)
/** \name Make Library Override Operator
* \{ */
-static bool make_override_hierarchy_recursive_tag(Main *bmain, ID *id)
-{
- MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id);
-
- /* This way we won't process again that ID should we encounter it again through another
- * relationship hierarchy.
- * Note that this does not free any memory from relations, so we can still use the entries.
- */
- BKE_main_relations_ID_remove(bmain, id);
-
- for (; entry != NULL; entry = entry->next) {
- /* We only consider IDs from the same library. */
- if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) {
- if (make_override_hierarchy_recursive_tag(bmain, *entry->id_pointer)) {
- id->tag |= LIB_TAG_DOIT;
- }
- }
- }
-
- return (id->tag & LIB_TAG_DOIT) != 0;
-}
-
static int make_override_tag_ids_cb(LibraryIDLinkCallbackData *cb_data)
{
if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) {
@@ -2315,14 +2293,9 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve
return OPERATOR_CANCELLED;
}
- /* Get object to work on - use a menu if we need to... */
- if (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
- ID_IS_LINKED(obact->instance_collection)) {
- /* Gives menu with list of objects in group. */
- WM_enum_search_invoke(C, op, event);
- return OPERATOR_CANCELLED;
- }
- if (ID_IS_LINKED(obact)) {
+ if ((!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
+ ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection)) ||
+ ID_IS_OVERRIDABLE_LIBRARY(obact)) {
uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION);
uiLayout *layout = UI_popup_menu_layout(pup);
@@ -2337,6 +2310,11 @@ static int make_override_library_invoke(bContext *C, wmOperator *op, const wmEve
/* This invoke just calls another instance of this operator... */
return OPERATOR_INTERFACE;
}
+ else if (ID_IS_LINKED(obact)) {
+ /* Show menu with list of directly linked collections containing the active object. */
+ WM_enum_search_invoke(C, op, event);
+ return OPERATOR_CANCELLED;
+ }
/* Error.. cannot continue. */
BKE_report(op->reports,
@@ -2366,11 +2344,28 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
id_root = &obact->instance_collection->id;
}
else if (!ID_IS_OVERRIDABLE_LIBRARY(obact)) {
- BKE_reportf(op->reports,
- RPT_ERROR_INVALID_INPUT,
- "Active object '%s' is not overridable",
- obact->id.name + 2);
- return OPERATOR_CANCELLED;
+ const int i = RNA_property_enum_get(op->ptr, op->type->prop);
+ const uint collection_session_uuid = *((uint *)&i);
+ if (collection_session_uuid == MAIN_ID_SESSION_UUID_UNSET) {
+ BKE_reportf(op->reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Active object '%s' is not overridable",
+ obact->id.name + 2);
+ return OPERATOR_CANCELLED;
+ }
+
+ Collection *collection = BLI_listbase_bytes_find(&bmain->collections,
+ &collection_session_uuid,
+ sizeof(collection_session_uuid),
+ offsetof(ID, session_uuid));
+ if (!ID_IS_OVERRIDABLE_LIBRARY(collection)) {
+ BKE_reportf(op->reports,
+ RPT_ERROR_INVALID_INPUT,
+ "Could not find an overridable collection containing object '%s'",
+ obact->id.name + 2);
+ return OPERATOR_CANCELLED;
+ }
+ id_root = &collection->id;
}
/* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */
else {
@@ -2399,12 +2394,8 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
}
}
- /* Then we tag all intermediary data-blocks in-between two overridden ones (e.g. if a shapekey
- * has a driver using an armature object's bone, we need to override the shapekey/obdata, the
- * objects using them, etc.) */
- make_override_hierarchy_recursive_tag(bmain, id_root);
-
- BKE_main_relations_free(bmain);
+ /* Note that this call will also free the main relations data we created above. */
+ BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, false);
ID *id;
FOREACH_MAIN_ID_BEGIN (bmain, id) {
@@ -2523,10 +2514,40 @@ static bool make_override_library_poll(bContext *C)
Object *obact = CTX_data_active_object(C);
/* Object must be directly linked to be overridable. */
- return (BKE_lib_override_library_is_enabled() && ED_operator_objectmode(C) && obact != NULL &&
- ((ID_IS_LINKED(obact) && obact->id.tag & LIB_TAG_EXTERN) ||
- (!ID_IS_LINKED(obact) && obact->instance_collection != NULL &&
- ID_IS_LINKED(obact->instance_collection))));
+ return (ED_operator_objectmode(C) && obact != NULL &&
+ (ID_IS_LINKED(obact) || (obact->instance_collection != NULL &&
+ ID_IS_OVERRIDABLE_LIBRARY(obact->instance_collection))));
+}
+
+static const EnumPropertyItem *make_override_collections_of_linked_object_itemf(
+ bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free)
+{
+ EnumPropertyItem item_tmp = {0}, *item = NULL;
+ int totitem = 0;
+
+ Object *object = ED_object_active_context(C);
+ Main *bmain = CTX_data_main(C);
+
+ if (!object || !ID_IS_LINKED(object)) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ /* Only check for directly linked collections. */
+ if (!ID_IS_LINKED(&collection->id) || (collection->id.tag & LIB_TAG_INDIRECT) != 0) {
+ continue;
+ }
+ if (BKE_collection_has_object_recursive(collection, object)) {
+ item_tmp.identifier = item_tmp.name = collection->id.name + 2;
+ item_tmp.value = *((int *)&collection->id.session_uuid);
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
}
void OBJECT_OT_make_override_library(wmOperatorType *ot)
@@ -2547,12 +2568,13 @@ void OBJECT_OT_make_override_library(wmOperatorType *ot)
/* properties */
PropertyRNA *prop;
prop = RNA_def_enum(ot->srna,
- "object",
+ "collection",
DummyRNA_DEFAULT_items,
- 0,
- "Override Object",
- "Name of lib-linked/collection object to make an override from");
- RNA_def_enum_funcs(prop, proxy_collection_object_itemf);
+ MAIN_ID_SESSION_UUID_UNSET,
+ "Override Collection",
+ "Name of directly linked collection containing the selected object, to make "
+ "an override from");
+ RNA_def_enum_funcs(prop, make_override_collections_of_linked_object_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
}
diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c
index 8df87a8a2f9..977d4abd4d4 100644
--- a/source/blender/editors/object/object_shader_fx.c
+++ b/source/blender/editors/object/object_shader_fx.c
@@ -24,6 +24,7 @@
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
+#include <string.h>
#include "MEM_guardedalloc.h"
@@ -55,6 +56,8 @@
#include "ED_object.h"
#include "ED_screen.h"
+#include "UI_interface.h"
+
#include "WM_api.h"
#include "WM_types.h"
@@ -247,7 +250,7 @@ static int shaderfx_add_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
@@ -360,21 +363,57 @@ static void edit_shaderfx_properties(wmOperatorType *ot)
RNA_def_property_flag(prop, PROP_HIDDEN);
}
-static int edit_shaderfx_invoke_properties(bContext *C, wmOperator *op)
+static void edit_shaderfx_report_property(wmOperatorType *ot)
{
- ShaderFxData *fx;
+ PropertyRNA *prop = RNA_def_boolean(
+ ot->srna, "report", false, "Report", "Create a notification after the operation");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+/**
+ * \param event: If this isn't NULL, the operator will also look for panels underneath
+ * the cursor with customdata set to a modifier.
+ * \param r_retval: This should be used if #event is used in order to to return
+ * #OPERATOR_PASS_THROUGH to check other operators with the same key set.
+ */
+static bool edit_shaderfx_invoke_properties(bContext *C,
+ wmOperator *op,
+ const wmEvent *event,
+ int *r_retval)
+{
if (RNA_struct_property_is_set(op->ptr, "shaderfx")) {
return true;
}
- PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx);
- if (ptr.data) {
- fx = ptr.data;
+ PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx);
+ if (ctx_ptr.data != NULL) {
+ ShaderFxData *fx = ctx_ptr.data;
RNA_string_set(op->ptr, "shaderfx", fx->name);
return true;
}
+ /* Check the custom data of panels under the mouse for an effect. */
+ if (event != NULL) {
+ PointerRNA *panel_ptr = UI_region_panel_custom_data_under_cursor(C, event);
+
+ if (!(panel_ptr == NULL || RNA_pointer_is_null(panel_ptr))) {
+ if (RNA_struct_is_a(panel_ptr->type, &RNA_ShaderFx)) {
+ ShaderFxData *fx = panel_ptr->data;
+ RNA_string_set(op->ptr, "shaderfx", fx->name);
+ return true;
+ }
+
+ BLI_assert(r_retval != NULL); /* We need the return value in this case. */
+ if (r_retval != NULL) {
+ *r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
+ }
+ return false;
+ }
+ }
+
+ if (r_retval != NULL) {
+ *r_retval = OPERATOR_CANCELLED;
+ }
return false;
}
@@ -403,21 +442,30 @@ static int shaderfx_remove_exec(bContext *C, wmOperator *op)
Object *ob = ED_object_active_context(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
+ /* Store name temporarily for report. */
+ char name[MAX_NAME];
+ strcpy(name, fx->name);
+
if (!fx || !ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) {
return OPERATOR_CANCELLED;
}
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ if (RNA_boolean_get(op->ptr, "report")) {
+ BKE_reportf(op->reports, RPT_INFO, "Removed effect: %s", name);
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
-static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_shaderfx_invoke_properties(C, op)) {
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_remove_exec(C, op);
}
- return OPERATOR_CANCELLED;
+ return retval;
}
void OBJECT_OT_shaderfx_remove(wmOperatorType *ot)
@@ -433,6 +481,7 @@ void OBJECT_OT_shaderfx_remove(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
+ edit_shaderfx_report_property(ot);
}
/************************ move up shaderfx operator *********************/
@@ -447,17 +496,18 @@ static int shaderfx_move_up_exec(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
-static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_shaderfx_invoke_properties(C, op)) {
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_up_exec(C, op);
}
- return OPERATOR_CANCELLED;
+ return retval;
}
void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot)
@@ -487,17 +537,18 @@ static int shaderfx_move_down_exec(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
-static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_shaderfx_invoke_properties(C, op)) {
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_down_exec(C, op);
}
- return OPERATOR_CANCELLED;
+ return retval;
}
void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot)
@@ -533,17 +584,18 @@ static int shaderfx_move_to_index_exec(bContext *C, wmOperator *op)
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
-static int shaderfx_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+static int shaderfx_move_to_index_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
- if (edit_shaderfx_invoke_properties(C, op)) {
+ int retval;
+ if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_to_index_exec(C, op);
}
- return OPERATOR_CANCELLED;
+ return retval;
}
void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot)
diff --git a/source/blender/editors/physics/dynamicpaint_ops.c b/source/blender/editors/physics/dynamicpaint_ops.c
index 6922a03b12f..381bf317bee 100644
--- a/source/blender/editors/physics/dynamicpaint_ops.c
+++ b/source/blender/editors/physics/dynamicpaint_ops.c
@@ -102,7 +102,7 @@ void DPAINT_OT_surface_slot_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = surface_slot_add_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -151,7 +151,7 @@ void DPAINT_OT_surface_slot_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = surface_slot_remove_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -203,7 +203,7 @@ void DPAINT_OT_type_toggle(wmOperatorType *ot)
/* api callbacks */
ot->exec = type_toggle_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -286,7 +286,7 @@ void DPAINT_OT_output_toggle(wmOperatorType *ot)
/* api callbacks */
ot->exec = output_toggle_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -538,5 +538,5 @@ void DPAINT_OT_bake(wmOperatorType *ot)
/* api callbacks */
ot->exec = dynamicpaint_bake_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
}
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index e75169a476b..41e30adf724 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -104,7 +104,7 @@ void OBJECT_OT_particle_system_add(wmOperatorType *ot)
ot->description = "Add a particle system";
/* api callbacks */
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = particle_system_add_exec;
/* flags */
@@ -151,7 +151,7 @@ void OBJECT_OT_particle_system_remove(wmOperatorType *ot)
ot->description = "Remove the selected particle system";
/* api callbacks */
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = particle_system_remove_exec;
/* flags */
@@ -1210,7 +1210,7 @@ static bool copy_particle_systems_to_object(const bContext *C,
static bool copy_particle_systems_poll(bContext *C)
{
Object *ob;
- if (!ED_operator_object_active_editable(C)) {
+ if (!ED_operator_object_active_local_editable(C)) {
return false;
}
@@ -1311,7 +1311,7 @@ void PARTICLE_OT_copy_particle_systems(wmOperatorType *ot)
static bool duplicate_particle_systems_poll(bContext *C)
{
- if (!ED_operator_object_active_editable(C)) {
+ if (!ED_operator_object_active_local_editable(C)) {
return false;
}
Object *ob = ED_object_active_context(C);
diff --git a/source/blender/editors/physics/physics_fluid.c b/source/blender/editors/physics/physics_fluid.c
index 583c68cb284..26b5f7fb2af 100644
--- a/source/blender/editors/physics/physics_fluid.c
+++ b/source/blender/editors/physics/physics_fluid.c
@@ -546,6 +546,7 @@ static int fluid_bake_exec(struct bContext *C, struct wmOperator *op)
return OPERATOR_CANCELLED;
}
if (!fluid_validatepaths(job, op->reports)) {
+ fluid_bake_free(job);
return OPERATOR_CANCELLED;
}
WM_report_banners_cancel(job->bmain);
@@ -574,6 +575,7 @@ static int fluid_bake_invoke(struct bContext *C,
}
if (!fluid_validatepaths(job, op->reports)) {
+ fluid_bake_free(job);
return OPERATOR_CANCELLED;
}
@@ -651,6 +653,7 @@ static int fluid_free_exec(struct bContext *C, struct wmOperator *op)
job->name = op->type->name;
if (!fluid_validatepaths(job, op->reports)) {
+ fluid_bake_free(job);
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 754f8226684..149bae718a0 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -162,7 +162,7 @@ void OBJECT_OT_material_slot_add(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_add_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -207,7 +207,7 @@ void OBJECT_OT_material_slot_remove(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_remove_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -310,7 +310,7 @@ void OBJECT_OT_material_slot_assign(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_assign_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
@@ -564,6 +564,7 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot)
ot->description = "Move the active material up/down in the list";
/* api callbacks */
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = material_slot_move_exec;
/* flags */
@@ -638,7 +639,7 @@ void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot)
/* api callbacks */
ot->exec = material_slot_remove_unused_exec;
- ot->poll = ED_operator_object_active_editable;
+ ot->poll = ED_operator_object_active_local_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -707,6 +708,7 @@ void MATERIAL_OT_new(wmOperatorType *ot)
ot->description = "Add a new material";
/* api callbacks */
+ ot->poll = ED_operator_object_active_local_editable;
ot->exec = new_material_exec;
/* flags */
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index f32dd0c8703..b034fb186d2 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -360,6 +360,13 @@ bool ED_operator_object_active_editable(bContext *C)
return ED_operator_object_active_editable_ex(C, ob);
}
+/** Object must be editable and fully local (i.e. not an override). */
+bool ED_operator_object_active_local_editable(bContext *C)
+{
+ Object *ob = ED_object_active_context(C);
+ return ED_operator_object_active_editable_ex(C, ob) && !ID_IS_OVERRIDE_LIBRARY(ob);
+}
+
bool ED_operator_object_active_editable_mesh(bContext *C)
{
Object *ob = ED_object_active_context(C);
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 892aea6048f..be7b824fc3e 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -875,8 +875,12 @@ static bool paint_draw_alpha_overlay(UnifiedPaintSettings *ups,
return alpha_overlay_active;
}
-BLI_INLINE void draw_tri_point(
- uint pos, const float sel_col[4], float pivot_col[4], float *co, float width, bool selected)
+BLI_INLINE void draw_tri_point(uint pos,
+ const float sel_col[4],
+ const float pivot_col[4],
+ float *co,
+ float width,
+ bool selected)
{
immUniformColor4fv(selected ? sel_col : pivot_col);
@@ -905,8 +909,12 @@ BLI_INLINE void draw_tri_point(
immEnd();
}
-BLI_INLINE void draw_rect_point(
- uint pos, const float sel_col[4], float handle_col[4], float *co, float width, bool selected)
+BLI_INLINE void draw_rect_point(uint pos,
+ const float sel_col[4],
+ const float handle_col[4],
+ const float *co,
+ float width,
+ bool selected)
{
immUniformColor4fv(selected ? sel_col : handle_col);
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 16ccfaf8286..a7f09390a3d 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -257,7 +257,7 @@ static ushort *brush_painter_mask_ibuf_new(BrushPainter *painter, const int size
/* update rectangular section of the brush image */
static void brush_painter_mask_imbuf_update(BrushPainter *painter,
ImagePaintTile *tile,
- ushort *tex_mask_old,
+ const ushort *tex_mask_old,
int origx,
int origy,
int w,
@@ -1052,7 +1052,7 @@ static void paint_2d_lift_soften(ImagePaintState *s,
ImagePaintTile *tile,
ImBuf *ibuf,
ImBuf *ibufb,
- int *pos,
+ const int *pos,
const short paint_tile)
{
bool sharpen = (tile->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0));
@@ -1255,7 +1255,7 @@ static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short paint
}
}
-static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos)
+static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, const int *pos)
{
/* note: allocImbuf returns zero'd memory, so regions outside image will
* have zero alpha, and hence not be blended onto the image */
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 83620b4bc56..5af3a3f4241 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -1426,7 +1426,7 @@ static void insert_seam_vert_array(const ProjPaintState *ps,
* Be tricky with flags, first 4 bits are #PROJ_FACE_SEAM0 to 4,
* last 4 bits are #PROJ_FACE_NOSEAM0 to 4. `1 << i` - where i is `(0..3)`.
*
- * If we're multithreadng, make sure threads are locked when this is called.
+ * If we're multi-threading, make sure threads are locked when this is called.
*/
static void project_face_seams_init(const ProjPaintState *ps,
MemArena *arena,
@@ -3566,8 +3566,8 @@ static bool project_bucket_face_isect(ProjPaintState *ps,
int bucket_y,
const MLoopTri *lt)
{
- /* TODO - replace this with a tricker method that uses sideofline for all
- * screenCoords's edges against the closest bucket corner */
+ /* TODO - replace this with a trickier method that uses side-of-line for all
+ * #ProjPaintState.screenCoords edges against the closest bucket corner. */
const int lt_vtri[3] = {PS_LOOPTRI_AS_VERT_INDEX_3(ps, lt)};
rctf bucket_bounds;
float p1[2], p2[2], p3[2], p4[2];
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index be545600e6e..d65f158174f 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -246,7 +246,11 @@ static int palette_color_add_exec(bContext *C, wmOperator *UNUSED(op))
color = BKE_palette_color_add(palette);
palette->active_color = BLI_listbase_count(&palette->colors) - 1;
- if (ELEM(mode, PAINT_MODE_TEXTURE_3D, PAINT_MODE_TEXTURE_2D, PAINT_MODE_VERTEX)) {
+ if (ELEM(mode,
+ PAINT_MODE_TEXTURE_3D,
+ PAINT_MODE_TEXTURE_2D,
+ PAINT_MODE_VERTEX,
+ PAINT_MODE_SCULPT)) {
copy_v3_v3(color->rgb, BKE_brush_color_get(scene, brush));
color->value = 0.0;
}
diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
index 048f11c92d7..d05b1673c9a 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c
@@ -206,7 +206,7 @@ void PAINT_OT_vertex_color_from_weight(wmOperatorType *ot)
/** \name Smooth Vertex Colors Operator
* \{ */
-static void vertex_color_smooth_looptag(Mesh *me, bool *mlooptag)
+static void vertex_color_smooth_looptag(Mesh *me, const bool *mlooptag)
{
const bool use_face_sel = (me->editflag & ME_EDIT_PAINT_FACE_SEL) != 0;
const MPoly *mp;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 6d83e88b556..ffdf2c1e7c0 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -547,28 +547,81 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
}
}
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
+static bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index)
{
- switch (BKE_pbvh_type(ss->pbvh)) {
- case PBVH_FACES: {
- MeshElemMap *vert_map = &ss->pmap[index];
- int face_set = -1;
- for (int i = 0; i < ss->pmap[index].count; i++) {
- if (face_set == -1) {
- face_set = abs(ss->face_sets[vert_map->indices[i]]);
+ MeshElemMap *vert_map = &ss->pmap[index];
+ int face_set = -1;
+ for (int i = 0; i < ss->pmap[index].count; i++) {
+ if (face_set == -1) {
+ face_set = abs(ss->face_sets[vert_map->indices[i]]);
+ }
+ else {
+ if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/**
+ * Checks if the face sets of the adjacent faces to the edge between \a v1 and \a v2
+ * in the base mesh are equal.
+ */
+static bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2)
+{
+ MeshElemMap *vert_map = &ss->pmap[v1];
+ int p1 = -1, p2 = -1;
+ for (int i = 0; i < ss->pmap[v1].count; i++) {
+ MPoly *p = &ss->mpoly[vert_map->indices[i]];
+ for (int l = 0; l < p->totloop; l++) {
+ MLoop *loop = &ss->mloop[p->loopstart + l];
+ if (loop->v == v2) {
+ if (p1 == -1) {
+ p1 = vert_map->indices[i];
+ break;
}
- else {
- if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) {
- return false;
- }
+ else if (p2 == -1) {
+ p2 = vert_map->indices[i];
+ break;
}
}
- return true;
+ }
+ }
+
+ if (p1 != -1 && p2 != -1) {
+ return abs(ss->face_sets[p1]) == (ss->face_sets[p2]);
+ }
+ return true;
+}
+
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int 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 false;
- case PBVH_GRIDS:
- 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 vertex_index = index - grid_index * key->grid_area;
+ const SubdivCCGCoord coord = {.grid_index = grid_index,
+ .x = vertex_index % key->grid_size,
+ .y = vertex_index / key->grid_size};
+ int v1, v2;
+ const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
+ ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
+ switch (adjacency) {
+ case SUBDIV_CCG_ADJACENT_VERTEX:
+ return sculpt_check_unique_face_set_in_base_mesh(ss, v1);
+ case SUBDIV_CCG_ADJACENT_EDGE:
+ return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2);
+ case SUBDIV_CCG_ADJACENT_NONE:
+ return true;
+ }
+ }
}
return false;
}
@@ -735,44 +788,64 @@ void SCULPT_vertex_neighbors_get(SculptSession *ss,
}
}
+static bool sculpt_check_boundary_vertex_in_base_mesh(SculptSession *ss, const int index)
+{
+ const MeshElemMap *vert_map = &ss->pmap[index];
+ if (vert_map->count <= 1) {
+ return true;
+ }
+ for (int i = 0; i < vert_map->count; i++) {
+ const MPoly *p = &ss->mpoly[vert_map->indices[i]];
+ unsigned f_adj_v[2];
+ if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
+ int j;
+ for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
+ if (!(vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
bool SCULPT_vertex_is_boundary(SculptSession *ss, const int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
- const MeshElemMap *vert_map = &ss->pmap[index];
-
- if (vert_map->count <= 1) {
- return false;
- }
-
if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) {
- return false;
- }
-
- for (int i = 0; i < vert_map->count; i++) {
- const MPoly *p = &ss->mpoly[vert_map->indices[i]];
- unsigned f_adj_v[2];
- if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
- int j;
- for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
- if (!(vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2)) {
- return false;
- }
- }
- }
+ return true;
}
- return true;
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, index);
}
case PBVH_BMESH: {
BMVert *v = BM_vert_at_index(ss->bm, index);
return BM_vert_is_boundary(v);
}
- case PBVH_GRIDS:
- 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 vertex_index = index - grid_index * key->grid_area;
+ const SubdivCCGCoord coord = {.grid_index = grid_index,
+ .x = vertex_index % key->grid_size,
+ .y = vertex_index / key->grid_size};
+ int v1, v2;
+ const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
+ ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
+ switch (adjacency) {
+ case SUBDIV_CCG_ADJACENT_VERTEX:
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, v1);
+ case SUBDIV_CCG_ADJACENT_EDGE:
+ return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) &&
+ sculpt_check_boundary_vertex_in_base_mesh(ss, v2);
+ case SUBDIV_CCG_ADJACENT_NONE:
+ return false;
+ }
+ }
}
- return true;
+ return false;
}
/* Utilities */
@@ -2400,7 +2473,10 @@ bool SCULPT_search_sphere_cb(PBVHNode *node, void *data_v)
}
float t[3], bb_min[3], bb_max[3];
- if (data->ignore_fully_masked) {
+ if (data->ignore_fully_ineffective) {
+ if (BKE_pbvh_node_fully_hidden_get(node)) {
+ return false;
+ }
if (BKE_pbvh_node_fully_masked_get(node)) {
return false;
}
@@ -2436,7 +2512,7 @@ bool SCULPT_search_circle_cb(PBVHNode *node, void *data_v)
SculptSearchCircleData *data = data_v;
float bb_min[3], bb_max[3];
- if (data->ignore_fully_masked) {
+ if (data->ignore_fully_ineffective) {
if (BKE_pbvh_node_fully_masked_get(node)) {
return false;
}
@@ -2489,7 +2565,7 @@ static PBVHNode **sculpt_pbvh_gather_cursor_update(Object *ob,
.sd = sd,
.radius_squared = ss->cursor_radius,
.original = use_original,
- .ignore_fully_masked = false,
+ .ignore_fully_ineffective = false,
.center = NULL,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
@@ -2514,7 +2590,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
.sd = sd,
.radius_squared = square_f(ss->cache->radius * radius_scale),
.original = use_original,
- .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK,
+ .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK,
.center = NULL,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, r_totnode);
@@ -2530,7 +2606,7 @@ static PBVHNode **sculpt_pbvh_gather_generic(Object *ob,
ss->cursor_radius,
.original = use_original,
.dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc,
- .ignore_fully_masked = brush->sculpt_tool != SCULPT_TOOL_MASK,
+ .ignore_fully_ineffective = brush->sculpt_tool != SCULPT_TOOL_MASK,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_circle_cb, &data, &nodes, r_totnode);
}
@@ -5387,11 +5463,15 @@ 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 (!U.experimental.use_sculpt_vertex_colors) {
+ return;
+ }
}
if (brush->sculpt_tool == SCULPT_TOOL_SMEAR && type != PBVH_FACES) {
- return;
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ return;
+ }
}
/* Build a list of all nodes that are potentially within the brush's area of influence */
@@ -5411,7 +5491,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
.sd = sd,
.radius_squared = square_f(ss->cache->radius * (1.0 + brush->cloth_sim_limit)),
.original = false,
- .ignore_fully_masked = false,
+ .ignore_fully_ineffective = false,
.center = ss->cache->initial_location,
};
BKE_pbvh_search_gather(ss->pbvh, SCULPT_search_sphere_cb, &data, &nodes, &totnode);
@@ -6046,6 +6126,14 @@ bool SCULPT_mode_poll(bContext *C)
return ob && ob->mode & OB_MODE_SCULPT;
}
+bool SCULPT_vertex_colors_poll(bContext *C)
+{
+ if (!U.experimental.use_sculpt_vertex_colors) {
+ return false;
+ }
+ return SCULPT_mode_poll(C);
+}
+
bool SCULPT_mode_poll_view3d(bContext *C)
{
return (SCULPT_mode_poll(C) && CTX_wm_region_view3d(C));
@@ -8096,7 +8184,7 @@ static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
ot->idname = "SCULPT_OT_vertex_to_loop_colors";
/* api callbacks */
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_vertex_colors_poll;
ot->exec = vertex_to_loop_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -8159,7 +8247,7 @@ static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
ot->idname = "SCULPT_OT_loop_to_vertex_colors";
/* api callbacks */
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_vertex_colors_poll;
ot->exec = loop_to_vertex_colors_exec;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -8184,7 +8272,6 @@ static int sculpt_sample_color_invoke(bContext *C,
copy_v3_v3(color_srgb, active_vertex_color);
IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb);
BKE_brush_color_set(scene, brush, color_srgb);
- BKE_brush_alpha_set(scene, brush, active_vertex_color[3]);
WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush);
@@ -8200,7 +8287,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot)
/* api callbacks */
ot->invoke = sculpt_sample_color_invoke;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_vertex_colors_poll;
ot->flag = OPTYPE_REGISTER;
}
@@ -8209,7 +8296,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot)
/* This allows the sculpt tools to work on meshes with multiple connected components as they had
* only one connected component. When initialized and enabled, the sculpt API will return extra
* connectivity neighbors that are not in the real mesh. These neighbors are calculated for each
- * vertex using the minimun distance to a vertex that is in a different connected component. */
+ * vertex using the minimum distance to a vertex that is in a different connected component. */
/* The fake neighbors first need to be ensured to be initialized.
* After that tools which needs fake neighbors functionality need to
@@ -8224,7 +8311,7 @@ static void SCULPT_OT_sample_color(wmOperatorType *ot)
* }
*
* Such approach allows to keep all the connectivity information ready for reuse
- * (withouy having lag prior to every stroke), but also makes it so the affect
+ * (without having lag prior to every stroke), but also makes it so the affect
* is localized to a specific brushes and tools only. */
enum {
@@ -8681,6 +8768,11 @@ static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEven
BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
+ /* Color data is not available in Multires. */
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
+ return OPERATOR_CANCELLED;
+ }
+
if (!ss->vcol) {
return OPERATOR_CANCELLED;
}
@@ -8726,12 +8818,12 @@ static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
/* api callbacks */
ot->invoke = sculpt_mask_by_color_invoke;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_vertex_colors_poll;
ot->flag = OPTYPE_REGISTER;
ot->prop = RNA_def_boolean(
- ot->srna, "contiguous", false, "Contiguous", "Mask only contiguos color areas");
+ ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas");
ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask");
ot->prop = RNA_def_boolean(
@@ -8774,6 +8866,7 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_init);
WM_operatortype_append(SCULPT_OT_cloth_filter);
WM_operatortype_append(SCULPT_OT_face_sets_edit);
+
WM_operatortype_append(SCULPT_OT_sample_color);
WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c
index 7c79c1d7a2f..48b278e52b6 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c
@@ -209,7 +209,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
{
SculptSession *ss = ob->sculpt;
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
+ if (!ss->pmap) {
BLI_assert(!"Boundary Edges masking: pmap missing");
return NULL;
}
@@ -221,7 +221,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
- if (!SCULPT_vertex_is_boundary(ss, i)) {
+ if (SCULPT_vertex_is_boundary(ss, i)) {
edge_distance[i] = 0;
}
break;
diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c
index 6ca45ee0fb8..6a2137b6626 100644
--- a/source/blender/editors/sculpt_paint/sculpt_cloth.c
+++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c
@@ -437,13 +437,16 @@ static void do_cloth_brush_solve_simulation_task_cb_ex(
BKE_pbvh_vertex_iter_end;
}
-static void cloth_brush_build_nodes_constraints(Sculpt *sd,
- Object *ob,
- PBVHNode **nodes,
- int totnode,
- SculptClothSimulation *cloth_sim,
- float initial_location[3],
- const float radius)
+static void cloth_brush_build_nodes_constraints(
+ Sculpt *sd,
+ Object *ob,
+ PBVHNode **nodes,
+ int totnode,
+ SculptClothSimulation *cloth_sim,
+ /* Cannot be const, because it is assigned to a non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ float initial_location[3],
+ const float radius)
{
Brush *brush = BKE_paint_brush(&sd->paint);
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
index 5f7805af347..556b73b0ea5 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c
@@ -308,7 +308,7 @@ void SCULPT_OT_color_filter(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = sculpt_color_filter_invoke;
ot->modal = sculpt_color_filter_modal;
- ot->poll = SCULPT_mode_poll;
+ ot->poll = SCULPT_vertex_colors_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
index 494588d0996..27dd0ab3e71 100644
--- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
+++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
@@ -87,7 +87,7 @@ void SCULPT_filter_cache_init(Object *ob, Sculpt *sd, const int undo_type)
.original = true,
.center = center,
.radius_squared = FLT_MAX,
- .ignore_fully_masked = true,
+ .ignore_fully_ineffective = true,
};
BKE_pbvh_search_gather(pbvh,
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index c7e07721eb7..e943bd280a3 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -51,6 +51,8 @@ bool SCULPT_mode_poll_view3d(struct bContext *C);
bool SCULPT_poll(struct bContext *C);
bool SCULPT_poll_view3d(struct bContext *C);
+bool SCULPT_vertex_colors_poll(struct bContext *C);
+
/* Updates */
typedef enum SculptUpdateType {
@@ -676,7 +678,8 @@ typedef struct {
float radius_squared;
const float *center;
bool original;
- bool ignore_fully_masked;
+ /* This ignores fully masked and fully hidden nodes. */
+ bool ignore_fully_ineffective;
} SculptSearchSphereData;
typedef struct {
@@ -684,7 +687,7 @@ typedef struct {
struct SculptSession *ss;
float radius_squared;
bool original;
- bool ignore_fully_masked;
+ bool ignore_fully_ineffective;
struct DistRayAABB_Precalc *dist_ray_to_aabb_precalc;
} SculptSearchCircleData;
diff --git a/source/blender/editors/sculpt_paint/sculpt_paint_color.c b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
index 5cf6a053382..608ba1b934e 100644
--- a/source/blender/editors/sculpt_paint/sculpt_paint_color.c
+++ b/source/blender/editors/sculpt_paint/sculpt_paint_color.c
@@ -388,7 +388,17 @@ static void do_smear_brush_task_cb_exec(void *__restrict userdata,
float interp_color[4];
copy_v4_v4(interp_color, ss->cache->prev_colors[vd.index]);
- sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ switch (brush->smear_deform_type) {
+ case BRUSH_SMEAR_DEFORM_DRAG:
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ break;
+ case BRUSH_SMEAR_DEFORM_PINCH:
+ sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
+ break;
+ case BRUSH_SMEAR_DEFORM_EXPAND:
+ sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
+ break;
+ }
normalize_v3_v3(current_disp_norm, current_disp);
mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c
index e084d5fcdf2..49dbba5eba9 100644
--- a/source/blender/editors/space_api/spacetypes.c
+++ b/source/blender/editors/space_api/spacetypes.c
@@ -159,6 +159,7 @@ void ED_spacemacros_init(void)
* We need to have them go after python operators too */
ED_operatormacros_armature();
ED_operatormacros_mesh();
+ ED_operatormacros_uvedit();
ED_operatormacros_metaball();
ED_operatormacros_node();
ED_operatormacros_object();
diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c
index 71b86996989..88c2c6e82b6 100644
--- a/source/blender/editors/space_buttons/space_buttons.c
+++ b/source/blender/editors/space_buttons/space_buttons.c
@@ -422,6 +422,9 @@ static void buttons_area_listener(wmWindow *UNUSED(win),
buttons_area_redraw(area, BCONTEXT_CONSTRAINT);
buttons_area_redraw(area, BCONTEXT_BONE_CONSTRAINT);
break;
+ case ND_SHADERFX:
+ buttons_area_redraw(area, BCONTEXT_SHADERFX);
+ break;
case ND_PARTICLE:
if (wmn->action == NA_EDITED) {
buttons_area_redraw(area, BCONTEXT_PARTICLE);
@@ -435,13 +438,6 @@ static void buttons_area_listener(wmWindow *UNUSED(win),
/* Needed to refresh context path when changing active particle system index. */
buttons_area_redraw(area, BCONTEXT_PARTICLE);
break;
- case ND_SHADING:
- case ND_SHADING_DRAW:
- case ND_SHADING_LINKS:
- case ND_SHADING_PREVIEW:
- /* currently works by redraws... if preview is set, it (re)starts job */
- sbuts->preview = 1;
- break;
default:
/* Not all object RNA props have a ND_ notifier (yet) */
ED_area_tag_redraw(area);
diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c
index 35e0fdbfec3..68ebd6fed7a 100644
--- a/source/blender/editors/space_clip/clip_draw.c
+++ b/source/blender/editors/space_clip/clip_draw.c
@@ -1111,7 +1111,7 @@ static void draw_marker_texts(SpaceClip *sc,
pos[1] -= fontsize;
if (track->flag & TRACK_HAS_BUNDLE) {
- BLI_snprintf(str, sizeof(str), "Average error: %.3f", track->error);
+ BLI_snprintf(str, sizeof(str), "Average error: %.2f px", track->error);
BLF_position(fontid, pos[0], pos[1], 0.0f);
BLF_draw(fontid, str, sizeof(str));
pos[1] -= fontsize;
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index a69601316ef..9da75ab7e3c 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -1403,6 +1403,8 @@ static void do_sequence_proxy(void *pjv,
int build_count,
int *build_undistort_sizes,
int build_undistort_count,
+ /* Cannot be const, because it is assigned to a non-const variable.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
short *stop,
short *do_update,
float *progress)
diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c
index c18207d7045..1ed965c30d2 100644
--- a/source/blender/editors/space_clip/tracking_ops_solve.c
+++ b/source/blender/editors/space_clip/tracking_ops_solve.c
@@ -144,7 +144,7 @@ static void solve_camera_freejob(void *scv)
else {
BKE_reportf(scj->reports,
RPT_INFO,
- "Average re-projection error: %.3f",
+ "Average re-projection error: %.2f px",
tracking->reconstruction.error);
}
diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c
index cec7b371457..afbca48bd45 100644
--- a/source/blender/editors/space_clip/tracking_ops_track.c
+++ b/source/blender/editors/space_clip/tracking_ops_track.c
@@ -214,7 +214,13 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa
return true;
}
-static void track_markers_startjob(void *tmv, short *stop, short *do_update, float *progress)
+static void track_markers_startjob(
+ void *tmv,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
TrackMarkersJob *tmj = (TrackMarkersJob *)tmv;
int framenr = tmj->sfra;
diff --git a/source/blender/editors/space_clip/tracking_select.c b/source/blender/editors/space_clip/tracking_select.c
index 81cc858c69f..b6f9ca9589f 100644
--- a/source/blender/editors/space_clip/tracking_select.c
+++ b/source/blender/editors/space_clip/tracking_select.c
@@ -731,7 +731,9 @@ void CLIP_OT_select_lasso(wmOperatorType *ot)
/********************** circle select operator *********************/
-static int point_inside_ellipse(float point[2], float offset[2], float ellipse[2])
+static int point_inside_ellipse(const float point[2],
+ const float offset[2],
+ const float ellipse[2])
{
/* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
float x, y;
diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c
index cb785495ad5..3ce80c11160 100644
--- a/source/blender/editors/space_file/filelist.c
+++ b/source/blender/editors/space_file/filelist.c
@@ -2660,9 +2660,9 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
if (filelist->dir[0] == 0) {
/* make directories */
# ifdef WITH_FREESTYLE
- filelist->filelist.nbr_entries = 27;
+ filelist->filelist.nbr_entries = 27;
# else
- filelist->filelist.nbr_entries = 26;
+ filelist->filelist.nbr_entries = 26;
# endif
filelist_resize(filelist, filelist->filelist.nbr_entries);
@@ -2693,11 +2693,11 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
filelist->filelist.entries[20].entry->relpath = BLI_strdup("Action");
filelist->filelist.entries[21].entry->relpath = BLI_strdup("NodeTree");
filelist->filelist.entries[22].entry->relpath = BLI_strdup("Speaker");
- filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
- filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
- filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
+ filelist->filelist.entries[23].entry->relpath = BLI_strdup("Hair");
+ filelist->filelist.entries[24].entry->relpath = BLI_strdup("Point Cloud");
+ filelist->filelist.entries[25].entry->relpath = BLI_strdup("Volume");
# ifdef WITH_FREESTYLE
- filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle");
+ filelist->filelist.entries[26].entry->relpath = BLI_strdup("FreestyleLineStyle");
# endif
}
else {
@@ -2809,7 +2809,7 @@ static void filelist_readjob_main_recursive(Main *bmain, FileList *filelist)
static void filelist_readjob_do(const bool do_lib,
FileList *filelist,
const char *main_name,
- short *stop,
+ const short *stop,
short *do_update,
float *progress,
ThreadMutex *lock)
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index e6c40dce9c8..88c8c6b2939 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -374,7 +374,7 @@ void ED_fileselect_set_params_from_userdef(SpaceFile *sfile)
* pass its size here so we can store that in the preferences. Otherwise NULL.
*/
void ED_fileselect_params_to_userdef(SpaceFile *sfile,
- int temp_win_size[2],
+ const int temp_win_size[2],
const bool is_maximized)
{
UserDef_FileSpaceData *sfile_udata_new = &U.file_space_data;
diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c
index bff81201d60..4b329ab4524 100644
--- a/source/blender/editors/space_file/fsmenu.c
+++ b/source/blender/editors/space_file/fsmenu.c
@@ -754,8 +754,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
fsmenu, FS_CATEGORY_OTHER, &FOLDERID_SkyDrive, NULL, ICON_URL, FS_INSERT_LAST);
}
}
-#else
-# ifdef __APPLE__
+#elif defined(__APPLE__)
{
/* We store some known macOS system paths and corresponding icons
* and names in the FS_CATEGORY_OTHER (not displayed directly) category. */
@@ -774,9 +773,9 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
const char *home = BLI_getenv("HOME");
-# define FS_MACOS_PATH(path, name, icon) \
- BLI_snprintf(line, sizeof(line), path, home); \
- fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST);
+# define FS_MACOS_PATH(path, name, icon) \
+ BLI_snprintf(line, sizeof(line), path, home); \
+ fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST);
FS_MACOS_PATH("%s/", NULL, ICON_HOME)
FS_MACOS_PATH("%s/Desktop/", IFACE_("Desktop"), ICON_DESKTOP)
@@ -787,7 +786,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
FS_MACOS_PATH("%s/Pictures/", IFACE_("Pictures"), ICON_FILE_IMAGE)
FS_MACOS_PATH("%s/Library/Fonts/", IFACE_("Fonts"), ICON_FILE_FONT)
-# undef FS_MACOS_PATH
+# undef FS_MACOS_PATH
/* Get mounted volumes better method OSX 10.6 and higher, see:
* https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html
@@ -849,8 +848,8 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
/* kLSSharedFileListFavoriteItems is deprecated, but available till macOS 10.15.
* Will have to find a new method to sync the Finder Favorites with File Browser. */
-# pragma GCC diagnostic push
-# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+# pragma GCC diagnostic push
+# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
/* Finally get user favorite places */
if (read_bookmarks) {
UInt32 seed;
@@ -894,9 +893,9 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
CFRelease(pathesArray);
CFRelease(list);
}
-# pragma GCC diagnostic pop
+# pragma GCC diagnostic pop
}
-# else
+#else
/* unix */
{
const char *home = BLI_getenv("HOME");
@@ -932,7 +931,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
{
int found = 0;
-# ifdef __linux__
+# ifdef __linux__
/* loop over mount points */
struct mntent *mnt;
FILE *fp;
@@ -994,7 +993,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
}
BLI_filelist_free(dir, dir_len);
}
-# endif
+# endif
/* fallback */
if (!found) {
@@ -1003,7 +1002,6 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks)
}
}
}
-# endif
#endif
#if defined(WIN32) || defined(__APPLE__)
@@ -1131,10 +1129,13 @@ int fsmenu_get_active_indices(struct FSMenu *fsmenu, enum FSMenuCategory categor
* before being defined as unreachable by the OS, we need to validate the bookmarks in an async
* job...
*/
-static void fsmenu_bookmark_validate_job_startjob(void *fsmenuv,
- short *stop,
- short *do_update,
- float *UNUSED(progress))
+static void fsmenu_bookmark_validate_job_startjob(
+ void *fsmenuv,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *UNUSED(progress))
{
FSMenu *fsmenu = fsmenuv;
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index 179d73a38ba..0158e12c79c 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -1420,7 +1420,7 @@ void graph_buttons_register(ARegionType *art)
pt->poll = graph_panel_drivers_poll;
BLI_addtail(&art->paneltypes, pt);
- pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel drivers pover");
+ pt = MEM_callocN(sizeof(PanelType), "spacetype graph panel drivers popover");
strcpy(pt->idname, "GRAPH_PT_drivers_popover");
strcpy(pt->label, N_("Add/Edit Driver"));
strcpy(pt->category, "Drivers");
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index 24ec2393b69..1f8dd7cfe44 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -572,8 +572,12 @@ static void image_multiview_cb(bContext *C, void *rnd_pt, void *UNUSED(arg_v))
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
}
-static void uiblock_layer_pass_buttons(
- uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot)
+static void uiblock_layer_pass_buttons(uiLayout *layout,
+ Image *image,
+ RenderResult *rr,
+ ImageUser *iuser,
+ int w,
+ const short *render_slot)
{
struct ImageUI_Data rnd_pt_local, *rnd_pt = NULL;
uiBlock *block = uiLayoutGetBlock(layout);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 9040ca5e79c..a7fa7709c51 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -150,8 +150,8 @@ void ED_image_draw_info(Scene *scene,
const uchar cp[4],
const float fp[4],
const float linearcol[4],
- int *zp,
- float *zpf)
+ const int *zp,
+ const float *zpf)
{
rcti color_rect;
char str[256];
@@ -463,7 +463,7 @@ void ED_image_draw_info(Scene *scene,
/* image drawing */
static void sima_draw_zbuf_pixels(
- float x1, float y1, int rectx, int recty, int *rect, float zoomx, float zoomy)
+ float x1, float y1, int rectx, int recty, const int *rect, float zoomx, float zoomy)
{
float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
@@ -489,7 +489,7 @@ static void sima_draw_zbuffloat_pixels(Scene *scene,
float y1,
int rectx,
int recty,
- float *rect_float,
+ const float *rect_float,
float zoomx,
float zoomy)
{
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index d0d9f2f57bb..130167f1bd0 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -392,7 +392,7 @@ static void nla_panel_properties(const bContext *C, Panel *panel)
uiItemR(column, &strip_ptr, "blend_type", 0, NULL, ICON_NONE);
/* Blend in/out + auto-blending:
- * - blend in/out can only be set when autoblending is off
+ * - blend in/out can only be set when auto-blending is off.
*/
uiItemS(layout);
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index a56b7f8422e..96599fd92a7 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -645,8 +645,9 @@ static void nla_draw_strip_text(AnimData *adt,
UI_view2d_text_cache_add_rectf(v2d, &rect, str, str_len, col);
}
-/* add frame extents to cache of text-strings to draw in pixelspace
- * for now, only used when transforming strips
+/**
+ * Add frame extents to cache of text-strings to draw in pixel-space
+ * for now, only used when transforming strips.
*/
static void nla_draw_strip_frames_text(
NlaTrack *UNUSED(nlt), NlaStrip *strip, View2D *v2d, float UNUSED(yminc), float ymaxc)
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index f3207f2dc1e..780c3d6a217 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -860,11 +860,13 @@ static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), Poin
if (RNA_enum_get(ptr, "sky_type") == SHD_SKY_NISHITA) {
uiItemR(layout, ptr, "sun_disc", DEFAULT_FLAGS, NULL, 0);
+ uiLayout *col;
if (RNA_boolean_get(ptr, "sun_disc")) {
- uiItemR(layout, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, true);
+ uiItemR(col, ptr, "sun_size", DEFAULT_FLAGS, NULL, ICON_NONE);
+ uiItemR(col, ptr, "sun_intensity", DEFAULT_FLAGS, NULL, ICON_NONE);
}
- uiLayout *col;
col = uiLayoutColumn(layout, true);
uiItemR(col, ptr, "sun_elevation", DEFAULT_FLAGS, NULL, ICON_NONE);
uiItemR(col, ptr, "sun_rotation", DEFAULT_FLAGS, NULL, ICON_NONE);
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index 11d87148713..7af64e75656 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -238,7 +238,12 @@ static void compo_progressjob(void *cjv, float progress)
}
/* only this runs inside thread */
-static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress)
+static void compo_startjob(void *cjv,
+ /* Cannot be const, this function implements wm_jobs_start_callback.
+ * NOLINTNEXTLINE: readability-non-const-parameter. */
+ short *stop,
+ short *do_update,
+ float *progress)
{
CompoJob *cj = cjv;
bNodeTree *ntree = cj->localtree;
diff --git a/source/blender/editors/space_node/node_view.c b/source/blender/editors/space_node/node_view.c
index b1dbe3bc506..b376c6b29cf 100644
--- a/source/blender/editors/space_node/node_view.c
+++ b/source/blender/editors/space_node/node_view.c
@@ -419,7 +419,7 @@ static void sample_draw(const bContext *C, ARegion *region, void *arg_info)
* And here we've got recursion in the comments tips...
*/
bool ED_space_node_color_sample(
- Main *bmain, SpaceNode *snode, ARegion *region, int mval[2], float r_col[3])
+ Main *bmain, SpaceNode *snode, ARegion *region, const int mval[2], float r_col[3])
{
void *lock;
Image *ima;
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index a77b5424eb6..f0287984268 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -54,6 +54,7 @@
#include "BKE_context.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
+#include "BKE_idtype.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
@@ -739,22 +740,63 @@ static void id_local_cb(bContext *C,
}
}
+typedef struct OutlinerLibOverrideData {
+ bool do_hierarchy;
+} OutlinerLibOverrideData;
+
static void id_override_library_cb(bContext *C,
ReportList *UNUSED(reports),
Scene *UNUSED(scene),
- TreeElement *UNUSED(te),
+ TreeElement *te,
TreeStoreElem *UNUSED(tsep),
TreeStoreElem *tselem,
- void *UNUSED(user_data))
+ void *user_data)
{
- if (ID_IS_OVERRIDABLE_LIBRARY(tselem->id)) {
+ BLI_assert(TSE_IS_REAL_ID(tselem));
+ ID *id_root = tselem->id;
+
+ if (ID_IS_LINKED(id_root) &&
+ (BKE_idtype_get_info_from_id(id_root)->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0) {
Main *bmain = CTX_data_main(C);
+ OutlinerLibOverrideData *data = user_data;
+ const bool do_hierarchy = data->do_hierarchy;
+
+ id_root->tag |= LIB_TAG_DOIT;
+
+ printf("%s: Tagging root id %s\n", __func__, id_root->name);
+
/* For now, remapp all local usages of linked ID to local override one here. */
- BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true);
- ID *override_id = BKE_lib_override_library_create_from_id(bmain, tselem->id, true);
- if (override_id != NULL) {
- BKE_main_id_clear_newpoins(bmain);
+ ID *id_iter;
+ FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
+ if (ID_IS_LINKED(id_iter)) {
+ id_iter->tag &= ~LIB_TAG_DOIT;
+ }
+ else {
+ id_iter->tag |= LIB_TAG_DOIT;
+ }
}
+ FOREACH_MAIN_ID_END;
+
+ if (do_hierarchy) {
+ /* Tag all linked parents in tree hierarchy to be also overridden. */
+ while ((te = te->parent) != NULL) {
+ if (!TSE_IS_REAL_ID(te->store_elem)) {
+ continue;
+ }
+ if (!ID_IS_LINKED(te->store_elem->id)) {
+ break;
+ }
+ te->store_elem->id->tag |= LIB_TAG_DOIT;
+ printf("%s: Tagging parent id %s\n", __func__, te->store_elem->id->name);
+ }
+ BKE_lib_override_library_dependencies_tag(bmain, id_root, LIB_TAG_DOIT, true);
+ BKE_lib_override_library_create_from_tag(bmain);
+ }
+ else if (ID_IS_OVERRIDABLE_LIBRARY(id_root)) {
+ BKE_lib_override_library_create_from_id(bmain, id_root, true);
+ }
+
+ BKE_main_id_clear_newpoins(bmain);
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
}
}
@@ -1127,6 +1169,7 @@ static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem
{
bContext *C = (bContext *)Carg;
Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
ModifierData *md = (ModifierData *)te->directdata;
Object *ob = (Object *)outliner_search_back(te, ID_OB);
@@ -1141,7 +1184,7 @@ static void modifier_cb(int event, TreeElement *te, TreeStoreElem *UNUSED(tselem
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
}
else if (event == OL_MODIFIER_OP_DELETE) {
- ED_object_modifier_remove(NULL, bmain, ob, md);
+ ED_object_modifier_remove(NULL, bmain, scene, ob, md);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER | NA_REMOVED, ob);
te->store_elem->flag &= ~TSE_SELECTED;
}
@@ -1325,8 +1368,8 @@ static int outliner_object_operation_exec(bContext *C, wmOperator *op)
}
else if (event == OL_OP_REMAP) {
outliner_do_libdata_operation(C, op->reports, scene, soops, &soops->tree, id_remap_cb, NULL);
- /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth trick
- * does not work here). */
+ /* No undo push here, operator does it itself (since it's a modal one, the op_undo_depth
+ * trick does not work here). */
}
else if (event == OL_OP_LOCALIZED) { /* disabled, see above enum (ton) */
outliner_do_object_operation(C, op->reports, scene, soops, &soops->tree, id_local_cb);
@@ -1507,6 +1550,7 @@ typedef enum eOutlinerIdOpTypes {
OUTLINER_IDOP_UNLINK,
OUTLINER_IDOP_LOCAL,
OUTLINER_IDOP_OVERRIDE_LIBRARY,
+ OUTLINER_IDOP_OVERRIDE_LIBRARY_HIERARCHY,
OUTLINER_IDOP_SINGLE,
OUTLINER_IDOP_DELETE,
OUTLINER_IDOP_REMAP,
@@ -1530,6 +1574,11 @@ static const EnumPropertyItem prop_id_op_types[] = {
0,
"Add Library Override",
"Add a local override of this linked data-block"},
+ {OUTLINER_IDOP_OVERRIDE_LIBRARY_HIERARCHY,
+ "OVERRIDE_LIBRARY_HIERARCHY",
+ 0,
+ "Add Library Override Hierarchy",
+ "Add a local override of this linked data-block, and its hierarchy of dependencies"},
{OUTLINER_IDOP_SINGLE, "SINGLE", 0, "Make Single User", ""},
{OUTLINER_IDOP_DELETE, "DELETE", ICON_X, "Delete", ""},
{OUTLINER_IDOP_REMAP,
@@ -1562,7 +1611,7 @@ static bool outliner_id_operation_item_poll(bContext *C,
switch (enum_value) {
case OUTLINER_IDOP_OVERRIDE_LIBRARY:
- return BKE_lib_override_library_is_enabled();
+ return true;
case OUTLINER_IDOP_SINGLE:
if (!soops || ELEM(soops->outlinevis, SO_SCENES, SO_VIEW_LAYER)) {
return true;
@@ -1676,12 +1725,27 @@ static int outliner_id_operation_exec(bContext *C, wmOperator *op)
break;
}
case OUTLINER_IDOP_OVERRIDE_LIBRARY: {
- if (BKE_lib_override_library_is_enabled()) {
- /* make local */
- outliner_do_libdata_operation(
- C, op->reports, scene, soops, &soops->tree, id_override_library_cb, NULL);
- ED_undo_push(C, "Overridden Data");
- }
+ /* make local */
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ soops,
+ &soops->tree,
+ id_override_library_cb,
+ &(OutlinerLibOverrideData){.do_hierarchy = false});
+ ED_undo_push(C, "Overridden Data");
+ break;
+ }
+ case OUTLINER_IDOP_OVERRIDE_LIBRARY_HIERARCHY: {
+ /* make local */
+ outliner_do_libdata_operation(C,
+ op->reports,
+ scene,
+ soops,
+ &soops->tree,
+ id_override_library_cb,
+ &(OutlinerLibOverrideData){.do_hierarchy = true});
+ ED_undo_push(C, "Overridden Data");
break;
}
case OUTLINER_IDOP_SINGLE: {
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index 7178f32f182..0d8e0a87694 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -2607,6 +2607,8 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op))
MetaStack *ms;
bool nothing_selected = true;
+ BKE_sequencer_prefetch_stop(scene);
+
seq = BKE_sequencer_active_get(scene);
if (seq && seq->flag & SELECT) { /* Avoid a loop since this is likely to be selected. */
nothing_selected = false;
diff --git a/source/blender/editors/space_sequencer/sequencer_scopes.c b/source/blender/editors/space_sequencer/sequencer_scopes.c
index a20d9a2942c..80b1c7a5194 100644
--- a/source/blender/editors/space_sequencer/sequencer_scopes.c
+++ b/source/blender/editors/space_sequencer/sequencer_scopes.c
@@ -50,14 +50,14 @@ static void rgb_to_yuv_normalized(const float rgb[3], float yuv[3])
yuv[2] += 0.5f;
}
-static void scope_put_pixel(uchar *table, uchar *pos)
+static void scope_put_pixel(const uchar *table, uchar *pos)
{
uchar newval = table[*pos];
pos[0] = pos[1] = pos[2] = newval;
pos[3] = 255;
}
-static void scope_put_pixel_single(uchar *table, uchar *pos, int col)
+static void scope_put_pixel_single(const uchar *table, uchar *pos, int col)
{
char newval = table[pos[col]];
pos[col] = newval;
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index ed81d87f053..c88303daa16 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -825,6 +825,7 @@ static void view3d_main_region_listener(
case ND_POSE:
case ND_DRAW:
case ND_MODIFIER:
+ case ND_SHADERFX:
case ND_CONSTRAINT:
case ND_KEYS:
case ND_PARTICLE:
@@ -1381,6 +1382,7 @@ static void view3d_buttons_region_listener(wmWindow *UNUSED(win),
case ND_DRAW:
case ND_KEYS:
case ND_MODIFIER:
+ case ND_SHADERFX:
ED_region_tag_redraw(region);
break;
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 33625a8b775..fab98857c99 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -509,8 +509,8 @@ static void viewops_data_create(bContext *C,
negate_v3_v3(my_origin, rv3d->ofs); /* ofs is flipped */
- /* Set the dist value to be the distance from this 3d point this means youll
- * always be able to zoom into it and panning wont go bad when dist was zero */
+ /* Set the dist value to be the distance from this 3d point this means you'll
+ * always be able to zoom into it and panning wont go bad when dist was zero. */
/* remove dist value */
upvec[0] = upvec[1] = 0;
diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c
index c13990a4391..cc19ecf35a8 100644
--- a/source/blender/editors/space_view3d/view3d_fly.c
+++ b/source/blender/editors/space_view3d/view3d_fly.c
@@ -200,7 +200,7 @@ typedef struct FlyInfo {
float grid;
/* compare between last state */
- /** Used to accelerate when using the mousewheel a lot. */
+ /** Used to accelerate when using the mouse-wheel a lot. */
double time_lastwheel;
/** Time between draws. */
double time_lastdraw;
@@ -614,8 +614,8 @@ static void flyEvent(FlyInfo *fly, const wmEvent *event)
fly->axis = -1;
}
else {
- /* flip speed rather than stopping, game like motion,
- * else increase like mousewheel if we're already moving in that direction */
+ /* Flip speed rather than stopping, game like motion,
+ * else increase like mouse-wheel if we're already moving in that direction. */
if (fly->speed < 0.0f) {
fly->speed = -fly->speed;
}
diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c
index b7d857fb172..fc763d91e3d 100644
--- a/source/blender/editors/space_view3d/view3d_select.c
+++ b/source/blender/editors/space_view3d/view3d_select.c
@@ -1572,7 +1572,7 @@ void VIEW3D_OT_select_menu(wmOperatorType *ot)
static Base *object_mouse_select_menu(bContext *C,
ViewContext *vc,
- uint *buffer,
+ const uint *buffer,
int hits,
const int mval[2],
bool extend,
diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c
index d3e509fe96b..0d121b23c71 100644
--- a/source/blender/editors/space_view3d/view3d_utils.c
+++ b/source/blender/editors/space_view3d/view3d_utils.c
@@ -1028,8 +1028,11 @@ void ED_view3d_autodist_init(Depsgraph *depsgraph, ARegion *region, View3D *v3d,
}
/* no 4x4 sampling, run #ED_view3d_autodist_init first */
-bool ED_view3d_autodist_simple(
- ARegion *region, const int mval[2], float mouse_worldloc[3], int margin, float *force_depth)
+bool ED_view3d_autodist_simple(ARegion *region,
+ const int mval[2],
+ float mouse_worldloc[3],
+ int margin,
+ const float *force_depth)
{
float depth;
@@ -1458,7 +1461,7 @@ bool ED_view3d_lock(RegionView3D *rv3d)
* \param quat: The view rotation, quaternion normally from RegionView3D.viewquat.
* \param dist: The view distance from ofs, normally from RegionView3D.dist.
*/
-void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], float *dist)
+void ED_view3d_from_m4(const float mat[4][4], float ofs[3], float quat[4], const float *dist)
{
float nmat[3][3];
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index d68489759fe..70004d27dfc 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -27,6 +27,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_kdtree.h"
+#include "BLI_linklist_stack.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
@@ -409,59 +410,66 @@ void transform_autoik_update(TransInfo *t, short mode)
/** \name Curve Surface
* \{ */
-void calc_distanceCurveVerts(TransData *head, TransData *tail)
+void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic)
{
- TransData *td, *td_near = NULL;
+ TransData *td;
+ BLI_LINKSTACK_DECLARE(queue, TransData *);
+ BLI_LINKSTACK_INIT(queue);
for (td = head; td <= tail; td++) {
if (td->flag & TD_SELECTED) {
- td_near = td;
td->dist = 0.0f;
+ BLI_LINKSTACK_PUSH(queue, td);
}
- else if (td_near) {
- float dist;
- float vec[3];
+ else {
+ td->dist = FLT_MAX;
+ }
+ }
+
+ while ((td = BLI_LINKSTACK_POP(queue))) {
+ float dist;
+ float vec[3];
+
+ TransData *next_td = NULL;
- sub_v3_v3v3(vec, td_near->center, td->center);
+ if (td + 1 <= tail) {
+ next_td = td + 1;
+ }
+ else if (cyclic) {
+ next_td = head;
+ }
+
+ if (next_td != NULL && !(next_td->flag & TD_NOTCONNECTED)) {
+ sub_v3_v3v3(vec, next_td->center, td->center);
mul_m3_v3(head->mtx, vec);
- dist = len_v3(vec);
+ dist = len_v3(vec) + td->dist;
- if (dist < (td - 1)->dist) {
- td->dist = (td - 1)->dist;
- }
- else {
- td->dist = dist;
+ if (dist < next_td->dist) {
+ next_td->dist = dist;
+ BLI_LINKSTACK_PUSH(queue, next_td);
}
}
- else {
- td->dist = FLT_MAX;
- td->flag |= TD_NOTCONNECTED;
+
+ next_td = NULL;
+
+ if (td - 1 >= head) {
+ next_td = td - 1;
}
- }
- td_near = NULL;
- for (td = tail; td >= head; td--) {
- if (td->flag & TD_SELECTED) {
- td_near = td;
- td->dist = 0.0f;
+ else if (cyclic) {
+ next_td = tail;
}
- else if (td_near) {
- float dist;
- float vec[3];
- sub_v3_v3v3(vec, td_near->center, td->center);
+ if (next_td != NULL && !(next_td->flag & TD_NOTCONNECTED)) {
+ sub_v3_v3v3(vec, next_td->center, td->center);
mul_m3_v3(head->mtx, vec);
- dist = len_v3(vec);
+ dist = len_v3(vec) + td->dist;
- if (td->flag & TD_NOTCONNECTED || dist < td->dist || (td + 1)->dist < td->dist) {
- td->flag &= ~TD_NOTCONNECTED;
- if (dist < (td + 1)->dist) {
- td->dist = (td + 1)->dist;
- }
- else {
- td->dist = dist;
- }
+ if (dist < next_td->dist) {
+ next_td->dist = dist;
+ BLI_LINKSTACK_PUSH(queue, next_td);
}
}
}
+ BLI_LINKSTACK_FREE(queue);
}
/* Utility function for getting the handle data from bezier's */
@@ -1270,6 +1278,9 @@ void createTransData(bContext *C, TransInfo *t)
set_prop_dist(t, false);
}
}
+ else if (convert_type == TC_MESH_UV && t->flag & T_PROP_CONNECTED) {
+ /* Already calculated by uv_set_connectivity_distance. */
+ }
else if (convert_type == TC_CURVE_VERTS && t->obedit_type == OB_CURVE) {
set_prop_dist(t, false);
}
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index f7eea286983..ee478ad8567 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -87,7 +87,7 @@ void transform_around_single_fallback_ex(TransInfo *t, int data_len_all);
void transform_around_single_fallback(TransInfo *t);
void posttrans_fcurve_clean(struct FCurve *fcu, const int sel_flag, const bool use_handle);
bool constraints_list_needinv(TransInfo *t, ListBase *list);
-void calc_distanceCurveVerts(TransData *head, TransData *tail);
+void calc_distanceCurveVerts(TransData *head, TransData *tail, bool cyclic);
struct TransDataCurveHandleFlags *initTransDataCurveHandles(TransData *td, struct BezTriple *bezt);
char transform_convert_frame_side_dir_get(TransInfo *t, float cframe);
bool FrameOnMouseSide(char side, float frame, float cframe);
diff --git a/source/blender/editors/transform/transform_convert_curve.c b/source/blender/editors/transform/transform_convert_curve.c
index 37e37072ed7..65b2c9f9382 100644
--- a/source/blender/editors/transform/transform_convert_curve.c
+++ b/source/blender/editors/transform/transform_convert_curve.c
@@ -350,12 +350,14 @@ void createTransCurveVerts(TransInfo *t)
(void)hdata; /* quiet warning */
}
else if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
- head = tail;
+ tail->flag |= TD_NOTCONNECTED;
+ td++;
+ tail++;
}
}
if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
+ bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
+ calc_distanceCurveVerts(head, tail - 1, cyclic);
}
/* TODO - in the case of tilt and radius we can also avoid allocating the
@@ -425,12 +427,14 @@ void createTransCurveVerts(TransInfo *t)
}
}
else if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
- head = tail;
+ tail->flag |= TD_NOTCONNECTED;
+ td++;
+ tail++;
}
}
if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
+ bool cyclic = (nu->flagu & CU_NURB_CYCLIC) != 0;
+ calc_distanceCurveVerts(head, tail - 1, cyclic);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c
index 22261b9bbd8..0eb12aeabed 100644
--- a/source/blender/editors/transform/transform_convert_gpencil.c
+++ b/source/blender/editors/transform/transform_convert_gpencil.c
@@ -354,7 +354,7 @@ void createTransGPencil(bContext *C, TransInfo *t)
/* March over these points, and calculate the proportional editing distances */
if (is_prop_edit && (head != tail)) {
- calc_distanceCurveVerts(head, tail - 1);
+ calc_distanceCurveVerts(head, tail - 1, false);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_mesh.c b/source/blender/editors/transform/transform_convert_mesh.c
index 90e45253afa..f067dd60c19 100644
--- a/source/blender/editors/transform/transform_convert_mesh.c
+++ b/source/blender/editors/transform/transform_convert_mesh.c
@@ -1067,7 +1067,7 @@ static void create_trans_vert_customdata_layer(BMVert *v,
BLI_ghash_insert(tcld->origverts, v, r_tcld_vert);
}
-static void trans_mesh_customdata_correction_init_container(TransInfo *t, TransDataContainer *tc)
+static void trans_mesh_customdata_correction_init_container(TransDataContainer *tc)
{
if (tc->custom.type.data) {
/* Custom data correction has initiated before. */
@@ -1075,23 +1075,6 @@ static void trans_mesh_customdata_correction_init_container(TransInfo *t, TransD
return;
}
- if (!ELEM(t->mode,
- TFM_TRANSLATION,
- TFM_ROTATION,
- TFM_RESIZE,
- TFM_TOSPHERE,
- TFM_SHEAR,
- TFM_BEND,
- TFM_SHRINKFATTEN,
- TFM_TRACKBALL,
- TFM_PUSHPULL,
- TFM_ALIGN,
- TFM_EDGE_SLIDE,
- TFM_VERT_SLIDE)) {
- /* Currently only modes that change the position of vertices are supported. */
- return;
- }
-
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
BMesh *bm = em->bm;
@@ -1173,13 +1156,63 @@ static void trans_mesh_customdata_correction_init_container(TransInfo *t, TransD
void trans_mesh_customdata_correction_init(TransInfo *t)
{
+ if (!ELEM(t->mode,
+ TFM_TRANSLATION,
+ TFM_ROTATION,
+ TFM_RESIZE,
+ TFM_TOSPHERE,
+ TFM_SHEAR,
+ TFM_BEND,
+ TFM_SHRINKFATTEN,
+ TFM_TRACKBALL,
+ TFM_PUSHPULL,
+ TFM_ALIGN,
+ TFM_EDGE_SLIDE,
+ TFM_VERT_SLIDE)) {
+ /* Currently only modes that change the position of vertices are supported. */
+ return;
+ }
+
const char uvcalc_correct_flag = ELEM(t->mode, TFM_VERT_SLIDE, TFM_EDGE_SLIDE) ?
UVCALC_TRANSFORM_CORRECT_SLIDE :
UVCALC_TRANSFORM_CORRECT;
if (t->settings->uvcalc_flag & uvcalc_correct_flag) {
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- trans_mesh_customdata_correction_init_container(t, tc);
+ trans_mesh_customdata_correction_init_container(tc);
+ }
+ }
+}
+
+static void trans_mesh_customdata_correction_restore(struct TransDataContainer *tc)
+{
+ struct TransCustomDataLayer *tcld = tc->custom.type.data;
+ if (!tcld) {
+ return;
+ }
+
+ BMesh *bm = tcld->bm;
+ struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0];
+ for (int i = tcld->data_len; i--; tcld_vert_iter++) {
+ BMLoop *l;
+ BMIter liter;
+ BMVert *v = tcld_vert_iter->v;
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ /* Pop the key to not restore the face again. */
+ BMFace *f_copy = BLI_ghash_popkey(tcld->origfaces, l->f, NULL);
+ if (f_copy) {
+ BMLoop *l_iter_a, *l_first_a;
+ BMLoop *l_iter_b, *l_first_b;
+ l_iter_a = l_first_a = BM_FACE_FIRST_LOOP(f_copy);
+ l_iter_b = l_first_b = BM_FACE_FIRST_LOOP(l->f);
+ do {
+ BM_elem_attrs_copy(tcld->bm_origfaces, bm, l_iter_a, l_iter_b);
+ } while (((l_iter_a = l_iter_a->next) != l_first_a) &&
+ ((l_iter_b = l_iter_b->next) != l_first_b));
+
+ BM_elem_attrs_copy_ex(
+ tcld->bm_origfaces, bm, f_copy, l->f, BM_ELEM_SELECT, CD_MASK_NORMAL);
+ }
}
}
}
@@ -1195,7 +1228,7 @@ static const float *trans_vert_orig_co_get(struct TransCustomDataLayer *tcld, BM
static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLayer *tcld,
struct TransCustomDataLayerVert *tcld_vert,
- bool is_final)
+ bool do_loop_mdisps)
{
BMesh *bm = tcld->bm;
BMVert *v = tcld_vert->v;
@@ -1295,8 +1328,8 @@ static void trans_mesh_customdata_correction_apply_vert(struct TransCustomDataLa
* Interpolate from every other loop (not ideal)
* However values will only be taken from loops which overlap other mdisps.
* */
- const bool do_loop_mdisps = is_moved && is_final && (tcld->cd_loop_mdisp_offset != -1);
- if (do_loop_mdisps) {
+ const bool update_loop_mdisps = is_moved && do_loop_mdisps && (tcld->cd_loop_mdisp_offset != -1);
+ if (update_loop_mdisps) {
float(*faces_center)[3] = BLI_array_alloca(faces_center, l_num);
BMLoop *l;
@@ -1332,12 +1365,12 @@ static void trans_mesh_customdata_correction_apply(struct TransDataContainer *tc
return;
}
- const bool has_mdisps = (tcld->cd_loop_mdisp_offset != -1);
+ const bool do_loop_mdisps = is_final && (tcld->cd_loop_mdisp_offset != -1);
struct TransCustomDataLayerVert *tcld_vert_iter = &tcld->data[0];
for (int i = tcld->data_len; i--; tcld_vert_iter++) {
- if (tcld_vert_iter->cd_loop_groups || has_mdisps) {
- trans_mesh_customdata_correction_apply_vert(tcld, tcld_vert_iter, is_final);
+ if (tcld_vert_iter->cd_loop_groups || do_loop_mdisps) {
+ trans_mesh_customdata_correction_apply_vert(tcld, tcld_vert_iter, do_loop_mdisps);
}
}
}
@@ -1388,8 +1421,9 @@ static void transform_apply_to_mirror(TransInfo *t)
void recalcData_mesh(TransInfo *t)
{
+ bool is_cancelling = t->state == TRANS_CANCEL;
/* mirror modifier clipping? */
- if (t->state != TRANS_CANCEL) {
+ if (!is_cancelling) {
/* apply clipping after so we never project past the clip plane [#25423] */
applyProject(t);
clipMirrorModifier(t);
@@ -1400,7 +1434,12 @@ void recalcData_mesh(TransInfo *t)
}
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- trans_mesh_customdata_correction_apply(tc, false);
+ if (is_cancelling) {
+ trans_mesh_customdata_correction_restore(tc);
+ }
+ else {
+ trans_mesh_customdata_correction_apply(tc, false);
+ }
DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */
BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
@@ -1416,15 +1455,15 @@ void recalcData_mesh(TransInfo *t)
void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t)
{
- const bool canceled = (t->state == TRANS_CANCEL);
- const bool use_automerge = !canceled && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0;
+ const bool is_cancelling = (t->state == TRANS_CANCEL);
+ const bool use_automerge = !is_cancelling && (t->flag & (T_AUTOMERGE | T_AUTOSPLIT)) != 0;
- if (TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.type.data != NULL) {
+ if (!is_cancelling && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.type.data != NULL) {
/* Handle multires re-projection, done
* on transform completion since it's
* really slow -joeedh. */
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
- trans_mesh_customdata_correction_apply(tc, !canceled);
+ trans_mesh_customdata_correction_apply(tc, true);
}
}
diff --git a/source/blender/editors/transform/transform_convert_mesh_uv.c b/source/blender/editors/transform/transform_convert_mesh_uv.c
index 41c09cd8ea2..56fa2d90fb2 100644
--- a/source/blender/editors/transform/transform_convert_mesh_uv.c
+++ b/source/blender/editors/transform/transform_convert_mesh_uv.c
@@ -26,6 +26,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_bitmap.h"
+#include "BLI_linklist_stack.h"
#include "BLI_math.h"
#include "BKE_context.h"
@@ -51,12 +52,13 @@ static void UVsToTransData(const float aspect[2],
TransData2D *td2d,
float *uv,
const float *center,
+ float calc_dist,
bool selected)
{
- /* uv coords are scaled by aspects. this is needed for rotations and
- * proportional editing to be consistent with the stretched uv coords
- * that are displayed. this also means that for display and numinput,
- * and when the uv coords are flushed, these are converted each time */
+ /* UV coords are scaled by aspects. this is needed for rotations and
+ * proportional editing to be consistent with the stretched UV coords
+ * that are displayed. this also means that for display and number-input,
+ * and when the UV coords are flushed, these are converted each time. */
td2d->loc[0] = uv[0] * aspect[0];
td2d->loc[1] = uv[1] * aspect[1];
td2d->loc[2] = 0.0f;
@@ -79,12 +81,182 @@ static void UVsToTransData(const float aspect[2],
td->dist = 0.0;
}
else {
- td->dist = FLT_MAX;
+ td->dist = calc_dist;
}
unit_m3(td->mtx);
unit_m3(td->smtx);
}
+/**
+ * \param dists: Store the closest connected distance to selected vertices.
+ */
+static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float aspect[2])
+{
+ /* Mostly copied from editmesh_set_connectivity_distance. */
+ BLI_LINKSTACK_DECLARE(queue, BMLoop *);
+
+ /* Any BM_ELEM_TAG'd loop is added to 'queue_next', this makes sure that we don't add things
+ * twice. */
+ BLI_LINKSTACK_DECLARE(queue_next, BMLoop *);
+
+ BLI_LINKSTACK_INIT(queue);
+ BLI_LINKSTACK_INIT(queue_next);
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ BMIter fiter, liter;
+ BMVert *f;
+ BMLoop *l;
+
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ /* Visable faces was tagged in createTransUVs. */
+ if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ float dist;
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+ bool uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
+
+ if (uv_vert_sel) {
+ BLI_LINKSTACK_PUSH(queue, l);
+ dist = 0.0f;
+ }
+ else {
+ dist = FLT_MAX;
+ }
+
+ /* Make sure all loops are in a clean tag state. */
+ BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
+
+ int loop_idx = BM_elem_index_get(l);
+
+ dists[loop_idx] = dist;
+ }
+ }
+
+ /* Need to be very careful of feedback loops here, store previous dist's to avoid feedback. */
+ float *dists_prev = MEM_dupallocN(dists);
+
+ do {
+ while ((l = BLI_LINKSTACK_POP(queue))) {
+ BLI_assert(dists[BM_elem_index_get(l)] != FLT_MAX);
+
+ BMLoop *l_other, *l_connected;
+ BMIter l_connected_iter;
+
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ float l_uv[2];
+
+ copy_v2_v2(l_uv, luv->uv);
+ mul_v2_v2(l_uv, aspect);
+
+ BM_ITER_ELEM (l_other, &liter, l->f, BM_LOOPS_OF_FACE) {
+ if (l_other == l) {
+ continue;
+ }
+ float other_uv[2], edge_vec[2];
+ MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_other, cd_loop_uv_offset);
+
+ copy_v2_v2(other_uv, luv_other->uv);
+ mul_v2_v2(other_uv, aspect);
+
+ sub_v2_v2v2(edge_vec, l_uv, other_uv);
+
+ const int i = BM_elem_index_get(l);
+ const int i_other = BM_elem_index_get(l_other);
+ float dist = len_v2(edge_vec) + dists_prev[i];
+
+ if (dist < dists[i_other]) {
+ dists[i_other] = dist;
+ }
+ else {
+ /* The face loop already has a shorter path to it. */
+ continue;
+ }
+
+ float connected_uv[2];
+ float uvdiff[2];
+
+ bool other_vert_sel, connected_vert_sel;
+
+ other_vert_sel = luv_other->flag & MLOOPUV_VERTSEL;
+
+ BM_ITER_ELEM (l_connected, &l_connected_iter, l_other->v, BM_LOOPS_OF_VERT) {
+ if (l_connected == l_other) {
+ continue;
+ }
+ /* Visable faces was tagged in createTransUVs. */
+ if (!BM_elem_flag_test(l_connected->f, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ MLoopUV *luv_connected = BM_ELEM_CD_GET_VOID_P(l_connected, cd_loop_uv_offset);
+ connected_vert_sel = luv_connected->flag & MLOOPUV_VERTSEL;
+ copy_v2_v2(connected_uv, luv_connected->uv);
+ mul_v2_v2(connected_uv, aspect);
+
+ sub_v2_v2v2(uvdiff, connected_uv, other_uv);
+ /* Check if this loop is connected in UV space.
+ * If the uv loops share the same selection state (if not, they are not connected as
+ * they have been ripped or other edit commands have separated them). */
+ bool connected = other_vert_sel == connected_vert_sel &&
+ fabsf(uvdiff[0]) < STD_UV_CONNECT_LIMIT &&
+ fabsf(uvdiff[1]) < STD_UV_CONNECT_LIMIT;
+ if (!connected) {
+ continue;
+ }
+
+ /* The loop vert is occupying the same space, so it has the same distance. */
+ const int i_connected = BM_elem_index_get(l_connected);
+ dists[i_connected] = dist;
+
+ if (BM_elem_flag_test(l_connected, BM_ELEM_TAG) == 0) {
+ BM_elem_flag_enable(l_connected, BM_ELEM_TAG);
+ BLI_LINKSTACK_PUSH(queue_next, l_connected);
+ }
+ }
+ }
+ }
+
+ /* Clear elem flags for the next loop. */
+ for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
+ BMLoop *l_link = lnk->link;
+ const int i = BM_elem_index_get(l_link);
+
+ BM_elem_flag_disable(l_link, BM_ELEM_TAG);
+
+ /* Store all new dist values. */
+ dists_prev[i] = dists[i];
+ }
+
+ BLI_LINKSTACK_SWAP(queue, queue_next);
+
+ } while (BLI_LINKSTACK_SIZE(queue));
+
+#ifndef NDEBUG
+ /* Check that we didn't leave any loops tagged */
+ BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
+ /* Visable faces was tagged in createTransUVs. */
+ if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
+ continue;
+ }
+
+ BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
+ BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
+ }
+ }
+#endif
+
+ BLI_LINKSTACK_FREE(queue);
+ BLI_LINKSTACK_FREE(queue_next);
+
+ MEM_freeN(dists_prev);
+}
+
void createTransUVs(bContext *C, TransInfo *t)
{
SpaceImage *sima = CTX_wm_space_image(C);
@@ -103,12 +275,11 @@ void createTransUVs(bContext *C, TransInfo *t)
BMFace *efa;
BMIter iter, liter;
UvElementMap *elementmap = NULL;
- BLI_bitmap *island_enabled = NULL;
struct {
float co[2];
int co_num;
} *island_center = NULL;
- int count = 0, countsel = 0, count_rejected = 0;
+ int count = 0, countsel = 0;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
if (!ED_space_image_show_uvedit(sima, tc->obedit)) {
@@ -116,22 +287,15 @@ void createTransUVs(bContext *C, TransInfo *t)
}
/* count */
- if (is_prop_connected || is_island_center) {
+ if (is_island_center) {
/* create element map with island information */
const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0;
- const bool use_uvsel = !is_prop_connected;
- elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, use_uvsel, false, true);
+ elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true);
if (elementmap == NULL) {
continue;
}
- if (is_prop_connected) {
- island_enabled = BLI_BITMAP_NEW(elementmap->totalIslands, "TransIslandData(UV Editing)");
- }
-
- if (is_island_center) {
- island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
- }
+ island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
}
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -147,20 +311,14 @@ void createTransUVs(bContext *C, TransInfo *t)
if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
countsel++;
- if (is_prop_connected || island_center) {
+ if (island_center) {
UvElement *element = BM_uv_element_get(elementmap, efa, l);
- if (is_prop_connected) {
- BLI_BITMAP_ENABLE(island_enabled, element->island);
- }
-
- if (is_island_center) {
- if (element->flag == false) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- add_v2_v2(island_center[element->island].co, luv->uv);
- island_center[element->island].co_num++;
- element->flag = true;
- }
+ if (element->flag == false) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ add_v2_v2(island_center[element->island].co, luv->uv);
+ island_center[element->island].co_num++;
+ element->flag = true;
}
}
}
@@ -198,6 +356,14 @@ void createTransUVs(bContext *C, TransInfo *t)
td = tc->data;
td2d = tc->data_2d;
+ float *prop_dists = NULL;
+
+ if (is_prop_connected) {
+ prop_dists = MEM_callocN(em->bm->totloop * sizeof(float), "TransObPropDists(UV Editing)");
+
+ uv_set_connectivity_distance(em->bm, prop_dists, t->aspect);
+ }
+
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BMLoop *l;
@@ -209,52 +375,41 @@ void createTransUVs(bContext *C, TransInfo *t)
const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
MLoopUV *luv;
const float *center = NULL;
+ float prop_distance = FLT_MAX;
if (!is_prop_edit && !selected) {
continue;
}
- if (is_prop_connected || is_island_center) {
+ if (is_prop_connected) {
+ const int idx = BM_elem_index_get(l);
+ prop_distance = prop_dists[idx];
+ }
+
+ if (is_island_center) {
UvElement *element = BM_uv_element_get(elementmap, efa, l);
if (element) {
- if (is_prop_connected) {
- if (!BLI_BITMAP_TEST(island_enabled, element->island)) {
- count_rejected++;
- continue;
- }
- }
-
- if (is_island_center) {
- center = island_center[element->island].co;
- }
+ center = island_center[element->island].co;
}
}
- BM_elem_flag_enable(l, BM_ELEM_TAG);
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, selected);
+ UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, prop_distance, selected);
}
}
- if (is_prop_connected) {
- tc->data_len -= count_rejected;
- }
-
if (sima->flag & SI_LIVE_UNWRAP) {
ED_uvedit_live_unwrap_begin(t->scene, tc->obedit);
}
finally:
- if (is_prop_connected || is_island_center) {
+ if (is_prop_connected) {
+ MEM_freeN(prop_dists);
+ }
+ if (is_island_center) {
BM_uv_element_map_free(elementmap);
- if (is_prop_connected) {
- MEM_freeN(island_enabled);
- }
-
- if (island_center) {
- MEM_freeN(island_center);
- }
+ MEM_freeN(island_center);
}
}
}
diff --git a/source/blender/editors/transform/transform_convert_particle.c b/source/blender/editors/transform/transform_convert_particle.c
index 3fa722d14cf..fbe9c07ebe9 100644
--- a/source/blender/editors/transform/transform_convert_particle.c
+++ b/source/blender/editors/transform/transform_convert_particle.c
@@ -183,7 +183,7 @@ void createTransParticleVerts(bContext *C, TransInfo *t)
tail++;
}
if (is_prop_edit && head != tail) {
- calc_distanceCurveVerts(head, tail - 1);
+ calc_distanceCurveVerts(head, tail - 1, false);
}
}
}
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index aee05197f10..cde06a9eaac 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -67,15 +67,27 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
dist = len_v3(t->num.val);
}
else {
+ int i = 0;
float dvec[3];
-
- copy_v3_v3(dvec, vec);
- applyAspectRatio(t, dvec);
+ if (!(t->flag & T_2D_EDIT) && t->con.mode & CON_APPLY) {
+ zero_v3(dvec);
+ if (t->con.mode & CON_AXIS0) {
+ dvec[i++] = vec[0];
+ }
+ if (t->con.mode & CON_AXIS1) {
+ dvec[i++] = vec[1];
+ }
+ if (t->con.mode & CON_AXIS2) {
+ dvec[i++] = vec[2];
+ }
+ }
+ else {
+ copy_v3_v3(dvec, vec);
+ applyAspectRatio(t, dvec);
+ }
dist = len_v3(vec);
if (!(t->flag & T_2D_EDIT) && t->scene->unit.system) {
- int i;
-
for (i = 0; i < 3; i++) {
bUnit_AsString2(&tvec[NUM_STR_REP_LEN * i],
NUM_STR_REP_LEN,
diff --git a/source/blender/editors/transform/transform_mode_vert_slide.c b/source/blender/editors/transform/transform_mode_vert_slide.c
index 9e810b9c629..4bcc4cc6383 100644
--- a/source/blender/editors/transform/transform_mode_vert_slide.c
+++ b/source/blender/editors/transform/transform_mode_vert_slide.c
@@ -500,6 +500,10 @@ static void doVertSlide(TransInfo *t, float perc)
FOREACH_TRANS_DATA_CONTAINER (t, tc) {
VertSlideData *sld = tc->custom.mode.data;
+ if (sld == NULL) {
+ continue;
+ }
+
TransDataVertSlideVert *svlist = sld->sv, *sv;
int i;
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index 3af176f5aef..6dbf80ed4b9 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -1106,11 +1106,13 @@ static bool raycastObjects(SnapObjectContext *sctx,
const float ray_start[3],
const float ray_dir[3],
/* read/write args */
- float *ray_depth,
+ /* Parameters below cannot be const, because they are assigned to a
+ * non-const variable (readability-non-const-parameter). */
+ float *ray_depth /* NOLINT */,
/* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index,
+ float r_loc[3] /* NOLINT */,
+ float r_no[3] /* NOLINT */,
+ int *r_index /* NOLINT */,
Object **r_ob,
float r_obmat[4][4],
ListBase *r_hit_list)
@@ -2789,11 +2791,13 @@ static short snapObjectsRay(SnapObjectContext *sctx,
SnapData *snapdata,
const struct SnapObjectParams *params,
/* read/write args */
- float *dist_px,
+ /* Parameters below cannot be const, because they are assigned to a
+ * non-const variable (readability-non-const-parameter). */
+ float *dist_px /* NOLINT */,
/* return args */
- float r_loc[3],
- float r_no[3],
- int *r_index,
+ float r_loc[3] /* NOLINT */,
+ float r_no[3] /* NOLINT */,
+ int *r_index /* NOLINT */,
Object **r_ob,
float r_obmat[4][4])
{
diff --git a/source/blender/editors/uvedit/CMakeLists.txt b/source/blender/editors/uvedit/CMakeLists.txt
index b40b82c50fb..a39234561c2 100644
--- a/source/blender/editors/uvedit/CMakeLists.txt
+++ b/source/blender/editors/uvedit/CMakeLists.txt
@@ -40,6 +40,8 @@ set(SRC
uvedit_draw.c
uvedit_ops.c
uvedit_parametrizer.c
+ uvedit_path.c
+ uvedit_rip.c
uvedit_select.c
uvedit_smart_stitch.c
uvedit_unwrap_ops.c
@@ -49,6 +51,7 @@ set(SRC
)
set(LIB
+ bf_bmesh
)
if(WITH_INTERNATIONAL)
diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c
index c6b8b0cd02d..9f716739b1c 100644
--- a/source/blender/editors/uvedit/uvedit_draw.c
+++ b/source/blender/editors/uvedit/uvedit_draw.c
@@ -413,7 +413,7 @@ static void draw_uvs(SpaceImage *sima,
UI_GetThemeColor3fv(TH_EDGE_SELECT, col2);
col2[3] = overlay_alpha;
- float dash_width = (sima->dt_uv & SI_UVDT_DASH) ? (4.0f * UI_DPI_FAC) : 9999.0f;
+ const float dash_width = (sima->dt_uv == SI_UVDT_DASH) ? (4.0f * UI_DPI_FAC) : 9999.0f;
eGPUBuiltinShader shader = (interpedges) ? GPU_SHADER_2D_UV_EDGES_SMOOTH :
GPU_SHADER_2D_UV_EDGES;
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index 31384d6df17..6a5f5162dff 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -106,8 +106,13 @@ void UV_OT_pack_islands(struct wmOperatorType *ot);
void UV_OT_reset(struct wmOperatorType *ot);
void UV_OT_sphere_project(struct wmOperatorType *ot);
void UV_OT_unwrap(struct wmOperatorType *ot);
+void UV_OT_rip(struct wmOperatorType *ot);
void UV_OT_stitch(struct wmOperatorType *ot);
+/* uvedit_path.c */
+void UV_OT_shortest_path_pick(struct wmOperatorType *ot);
+void UV_OT_shortest_path_select(struct wmOperatorType *ot);
+
/* uvedit_select.c */
bool uvedit_select_is_any_selected(struct Scene *scene, struct Object *obedit);
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 8d85de3b141..6003d4d5d95 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -2087,7 +2087,10 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_align);
+ WM_operatortype_append(UV_OT_rip);
WM_operatortype_append(UV_OT_stitch);
+ WM_operatortype_append(UV_OT_shortest_path_pick);
+ WM_operatortype_append(UV_OT_shortest_path_select);
WM_operatortype_append(UV_OT_seams_from_islands);
WM_operatortype_append(UV_OT_mark_seam);
@@ -2111,6 +2114,21 @@ void ED_operatortypes_uvedit(void)
WM_operatortype_append(UV_OT_cursor_set);
}
+void ED_operatormacros_uvedit(void)
+{
+ wmOperatorType *ot;
+ wmOperatorTypeMacro *otmacro;
+
+ ot = WM_operatortype_append_macro("UV_OT_rip_move",
+ "UV Rip Move",
+ "unstitch UV's and move the result",
+ OPTYPE_UNDO | OPTYPE_REGISTER);
+ WM_operatortype_macro_define(ot, "UV_OT_rip");
+ otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
+ RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
+ RNA_boolean_set(otmacro->ptr, "mirror", false);
+}
+
void ED_keymap_uvedit(wmKeyConfig *keyconf)
{
wmKeyMap *keymap;
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index 921b96f6023..a4ee9a294fe 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -487,7 +487,7 @@ static void p_chart_uv_scale_xy(PChart *chart, float x, float y)
}
}
-static void p_chart_uv_translate(PChart *chart, float trans[2])
+static void p_chart_uv_translate(PChart *chart, const float trans[2])
{
PVert *v;
@@ -805,7 +805,7 @@ static PVert *p_vert_copy(PChart *chart, PVert *v)
return nv;
}
-static PEdge *p_edge_lookup(PHandle *handle, PHashKey *vkeys)
+static PEdge *p_edge_lookup(PHandle *handle, const PHashKey *vkeys)
{
PHashKey key = PHASH_edge(vkeys[0], vkeys[1]);
PEdge *e = (PEdge *)phash_lookup(handle->hash_edges, key);
@@ -1146,14 +1146,14 @@ static PFace *p_face_add(PHandle *handle)
static PFace *p_face_add_construct(PHandle *handle,
ParamKey key,
- ParamKey *vkeys,
+ const ParamKey *vkeys,
float *co[4],
float *uv[4],
int i1,
int i2,
int i3,
- ParamBool *pin,
- ParamBool *select)
+ const ParamBool *pin,
+ const ParamBool *select)
{
PFace *f = p_face_add(handle);
PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next;
diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c
new file mode 100644
index 00000000000..e9615b62523
--- /dev/null
+++ b/source/blender/editors/uvedit/uvedit_path.c
@@ -0,0 +1,771 @@
+/*
+ * 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 eduv
+ *
+ * \note The logic in this file closely follows editmesh_path.c
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "BLI_linklist.h"
+#include "DNA_windowmanager_types.h"
+#include "MEM_guardedalloc.h"
+
+#include "BLI_ghash.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_math.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_image_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_editmesh.h"
+#include "BKE_layer.h"
+#include "BKE_mesh.h"
+#include "BKE_report.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "ED_screen.h"
+#include "ED_transform.h"
+#include "ED_uvedit.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_view2d.h"
+
+#include "intern/bmesh_marking.h"
+#include "uvedit_intern.h"
+
+#include "bmesh_tools.h"
+
+/* TODO(campbell): region filling, matching mesh selection. */
+// #define USE_FILL
+
+/* -------------------------------------------------------------------- */
+/** \name Local Utilities
+ * \{ */
+
+/**
+ * Support edge-path using vert-path calculation code.
+ *
+ * Cheat! Pick 2 closest loops and do vertex path,
+ * in practices only obscure/contrived cases will make give noticeably worse behavior.
+ *
+ * While the code below is a bit awkward, it's significantly less overhead than
+ * adding full edge selection which is nearly the same as vertex path in the case of UV's.
+ */
+static void bm_loop_calc_vert_pair_from_edge_pair(const int cd_loop_uv_offset,
+ const float aspect_y,
+ BMElem **ele_src_p,
+ BMElem **ele_dst_p,
+ BMElem **r_ele_dst_final)
+{
+ BMLoop *l_src = (BMLoop *)*ele_src_p;
+ BMLoop *l_dst = (BMLoop *)*ele_dst_p;
+
+ const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset);
+ const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset);
+ const MLoopUV *luv_dst_v1 = BM_ELEM_CD_GET_VOID_P(l_dst, cd_loop_uv_offset);
+ const MLoopUV *luv_dst_v2 = BM_ELEM_CD_GET_VOID_P(l_dst->next, cd_loop_uv_offset);
+
+ const float uv_src_v1[2] = {luv_src_v1->uv[0], luv_src_v1->uv[1] / aspect_y};
+ const float uv_src_v2[2] = {luv_src_v2->uv[0], luv_src_v2->uv[1] / aspect_y};
+ const float uv_dst_v1[2] = {luv_dst_v1->uv[0], luv_dst_v1->uv[1] / aspect_y};
+ const float uv_dst_v2[2] = {luv_dst_v2->uv[0], luv_dst_v2->uv[1] / aspect_y};
+
+ struct {
+ int src_index;
+ int dst_index;
+ float len_sq;
+ } tests[4] = {
+ {0, 0, len_squared_v2v2(uv_src_v1, uv_dst_v1)},
+ {0, 1, len_squared_v2v2(uv_src_v1, uv_dst_v2)},
+ {1, 0, len_squared_v2v2(uv_src_v2, uv_dst_v1)},
+ {1, 1, len_squared_v2v2(uv_src_v2, uv_dst_v2)},
+ };
+ int i_best = 0;
+ for (int i = 1; i < ARRAY_SIZE(tests); i++) {
+ if (tests[i].len_sq < tests[i_best].len_sq) {
+ i_best = i;
+ }
+ }
+
+ *ele_src_p = (BMElem *)(tests[i_best].src_index ? l_src->next : l_src);
+ *ele_dst_p = (BMElem *)(tests[i_best].dst_index ? l_dst->next : l_dst);
+
+ /* Ensure the edge is selected, not just the vertices up until we hit it. */
+ *r_ele_dst_final = (BMElem *)(tests[i_best].dst_index ? l_dst : l_dst->next);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Path Select Struct & Properties
+ * \{ */
+
+struct PathSelectParams {
+ /** ensure the active element is the last selected item (handy for picking) */
+ bool track_active;
+ bool use_topology_distance;
+ bool use_face_step;
+#ifdef USE_FILL
+ bool use_fill;
+#endif
+ struct CheckerIntervalParams interval_params;
+};
+
+struct UserData_UV {
+ Scene *scene;
+ uint cd_loop_uv_offset;
+};
+
+static void path_select_properties(wmOperatorType *ot)
+{
+ RNA_def_boolean(ot->srna,
+ "use_face_step",
+ false,
+ "Face Stepping",
+ "Traverse connected faces (includes diagonals and edge-rings)");
+ RNA_def_boolean(ot->srna,
+ "use_topology_distance",
+ false,
+ "Topology Distance",
+ "Find the minimum number of steps, ignoring spatial distance");
+#ifdef USE_FILL
+ RNA_def_boolean(ot->srna,
+ "use_fill",
+ false,
+ "Fill Region",
+ "Select all paths between the source/destination elements");
+#endif
+
+ WM_operator_properties_checker_interval(ot, true);
+}
+
+static void path_select_params_from_op(wmOperator *op, struct PathSelectParams *op_params)
+{
+ op_params->track_active = false;
+ op_params->use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
+#ifdef USE_FILL
+ op_params->use_fill = RNA_boolean_get(op->ptr, "use_fill");
+#endif
+ op_params->use_topology_distance = RNA_boolean_get(op->ptr, "use_topology_distance");
+ WM_operator_properties_checker_interval_from_op(op, &op_params->interval_params);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Vert Path
+ * \{ */
+
+/* callbacks */
+static bool looptag_filter_cb(BMLoop *l, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ return uvedit_face_visible_test(user_data->scene, l->f);
+}
+static bool looptag_test_cb(BMLoop *l, void *user_data_v)
+{
+ /* All connected loops are selected or we return false. */
+ struct UserData_UV *user_data = user_data_v;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
+ if (looptag_filter_cb(l_iter, user_data)) {
+ const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if (equals_v2v2(luv->uv, luv_iter->uv)) {
+ if ((luv_iter->flag & MLOOPUV_VERTSEL) == 0) {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) {
+ if (looptag_filter_cb(l_iter, user_data)) {
+ MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if (equals_v2v2(luv->uv, luv_iter->uv)) {
+ SET_FLAG_FROM_TEST(luv_iter->flag, val, MLOOPUV_VERTSEL);
+ }
+ }
+ }
+}
+
+static void mouse_mesh_uv_shortest_path_vert(Scene *scene,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMLoop *l_src,
+ BMLoop *l_dst,
+ BMLoop *l_dst_add_to_path,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ const ToolSettings *ts = scene->toolsettings;
+ const bool use_fake_edge_select = (ts->uv_selectmode & UV_SELECT_EDGE);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+
+ struct UserData_UV user_data = {
+ .scene = scene,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ const struct BMCalcPathUVParams params = {
+ .use_topology_distance = op_params->use_topology_distance,
+ .use_step_face = op_params->use_face_step,
+ .aspect_y = aspect_y,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+ LinkNode *path = BM_mesh_calc_path_uv_vert(
+ bm, l_src, l_dst, &params, looptag_filter_cb, &user_data);
+ /* TODO: false when we support region selection. */
+ bool is_path_ordered = true;
+
+ BMLoop *l_dst_last = l_dst;
+
+ if (path) {
+ if ((l_dst_add_to_path != NULL) && (BLI_linklist_index(path, l_dst_add_to_path) == -1)) {
+ /* Append, this isn't optimal compared to #BLI_linklist_append, it's a one-off lookup. */
+ LinkNode *path_last = BLI_linklist_find_last(path);
+ BLI_linklist_insert_after(&path_last, l_dst_add_to_path);
+ BLI_assert(BLI_linklist_find_last(path)->link == l_dst_add_to_path);
+ }
+
+ /* toggle the flag */
+ bool all_set = true;
+ LinkNode *node = path;
+ do {
+ if (!looptag_test_cb((BMLoop *)node->link, &user_data)) {
+ all_set = false;
+ break;
+ }
+ } while ((node = node->next));
+
+ int depth = -1;
+ node = path;
+ do {
+ if ((is_path_ordered == false) ||
+ WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
+ looptag_set_cb((BMLoop *)node->link, !all_set, &user_data);
+ if (is_path_ordered) {
+ l_dst_last = node->link;
+ }
+ }
+ } while ((void)depth++, (node = node->next));
+
+ BLI_linklist_free(path, NULL);
+ }
+ else {
+ const bool is_act = !looptag_test_cb(l_dst, &user_data);
+ looptag_set_cb(l_dst, is_act, &user_data); /* switch the face option */
+ }
+
+ if (op_params->track_active) {
+ /* Fake edge selection. */
+ if (use_fake_edge_select) {
+ ED_uvedit_active_edge_loop_set(bm, l_dst_last);
+ }
+ else {
+ ED_uvedit_active_vert_loop_set(bm, l_dst_last);
+ }
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Face Path
+ * \{ */
+
+/* callbacks */
+static bool facetag_filter_cb(BMFace *f, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ return uvedit_face_visible_test(user_data->scene, f);
+}
+static bool facetag_test_cb(BMFace *f, void *user_data_v)
+{
+ /* All connected loops are selected or we return false. */
+ struct UserData_UV *user_data = user_data_v;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
+ const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if ((luv_iter->flag & MLOOPUV_VERTSEL) == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+static void facetag_set_cb(BMFace *f, bool val, void *user_data_v)
+{
+ struct UserData_UV *user_data = user_data_v;
+ const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset;
+ BMIter iter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &iter, f, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ SET_FLAG_FROM_TEST(luv_iter->flag, val, MLOOPUV_VERTSEL);
+ }
+}
+
+static void mouse_mesh_uv_shortest_path_face(Scene *scene,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMFace *f_src,
+ BMFace *f_dst,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+
+ struct UserData_UV user_data = {
+ .scene = scene,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+
+ const struct BMCalcPathUVParams params = {
+ .use_topology_distance = op_params->use_topology_distance,
+ .use_step_face = op_params->use_face_step,
+ .aspect_y = aspect_y,
+ .cd_loop_uv_offset = cd_loop_uv_offset,
+ };
+ LinkNode *path = BM_mesh_calc_path_uv_face(
+ bm, f_src, f_dst, &params, facetag_filter_cb, &user_data);
+ /* TODO: false when we support region selection. */
+ bool is_path_ordered = true;
+
+ BMFace *f_dst_last = f_dst;
+
+ if (path) {
+ /* toggle the flag */
+ bool all_set = true;
+ LinkNode *node = path;
+ do {
+ if (!facetag_test_cb((BMFace *)node->link, &user_data)) {
+ all_set = false;
+ break;
+ }
+ } while ((node = node->next));
+
+ int depth = -1;
+ node = path;
+ do {
+ if ((is_path_ordered == false) ||
+ WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) {
+ facetag_set_cb((BMFace *)node->link, !all_set, &user_data);
+ if (is_path_ordered) {
+ f_dst_last = node->link;
+ }
+ }
+ } while ((void)depth++, (node = node->next));
+
+ BLI_linklist_free(path, NULL);
+ }
+ else {
+ const bool is_act = !facetag_test_cb(f_dst, &user_data);
+ facetag_set_cb(f_dst, is_act, &user_data); /* switch the face option */
+ }
+
+ if (op_params->track_active) {
+ /* Unlike other types, we can track active without it being selected. */
+ BM_mesh_active_face_set(bm, f_dst_last);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Main Operator for vert/edge/face tag
+ * \{ */
+
+static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op);
+
+static bool uv_shortest_path_pick_ex(Scene *scene,
+ Depsgraph *depsgraph,
+ Object *obedit,
+ const struct PathSelectParams *op_params,
+ BMElem *ele_src,
+ BMElem *ele_dst,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ bool ok = false;
+
+ if (ELEM(NULL, ele_src, ele_dst) || (ele_src->head.htype != ele_dst->head.htype)) {
+ /* pass */
+ }
+ else if (ele_src->head.htype == BM_FACE) {
+ mouse_mesh_uv_shortest_path_face(scene,
+ obedit,
+ op_params,
+ (BMFace *)ele_src,
+ (BMFace *)ele_dst,
+ aspect_y,
+ cd_loop_uv_offset);
+ ok = true;
+ }
+ else if (ele_src->head.htype == BM_LOOP) {
+ const ToolSettings *ts = scene->toolsettings;
+ BMElem *ele_dst_final = NULL;
+ if (ts->uv_selectmode & UV_SELECT_EDGE) {
+ bm_loop_calc_vert_pair_from_edge_pair(
+ cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final);
+ }
+ mouse_mesh_uv_shortest_path_vert(scene,
+ obedit,
+ op_params,
+ (BMLoop *)ele_src,
+ (BMLoop *)ele_dst,
+ (BMLoop *)ele_dst_final,
+ aspect_y,
+ cd_loop_uv_offset);
+ ok = true;
+ }
+
+ if (ok) {
+ Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
+ BKE_mesh_batch_cache_dirty_tag(obedit_eval->data, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT);
+ /* Only for region redraw. */
+ WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
+ }
+
+ return ok;
+}
+
+static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Scene *scene = CTX_data_scene(C);
+ const ToolSettings *ts = scene->toolsettings;
+
+ /* We could support this, it needs further testing. */
+ if (ts->uv_flag & UV_SYNC_SELECTION) {
+ BKE_report(op->reports, RPT_ERROR, "Sync selection doesn't support path select");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (RNA_struct_property_is_set(op->ptr, "index")) {
+ return uv_shortest_path_pick_exec(C, op);
+ }
+
+ struct PathSelectParams op_params;
+ path_select_params_from_op(op, &op_params);
+
+ /* Set false if we support edge tagging. */
+ op_params.track_active = true;
+
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+
+ float co[2];
+
+ const ARegion *region = CTX_wm_region(C);
+
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ float aspect_y;
+ {
+ float aspx, aspy;
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ }
+
+ UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
+
+ BMElem *ele_src = NULL, *ele_dst = NULL;
+
+ if (ts->uv_selectmode & UV_SELECT_FACE) {
+ UvNearestHit hit = UV_NEAREST_HIT_INIT;
+ if (!uv_find_nearest_face(scene, obedit, co, &hit)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BMFace *f_src = BM_mesh_active_face_get(bm, false, false);
+ /* Check selection? */
+
+ ele_src = (BMElem *)f_src;
+ ele_dst = (BMElem *)hit.efa;
+ }
+ else if (ts->uv_selectmode & UV_SELECT_EDGE) {
+ UvNearestHit hit = UV_NEAREST_HIT_INIT;
+ if (!uv_find_nearest_edge(scene, obedit, co, &hit)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BMLoop *l_src = ED_uvedit_active_edge_loop_get(bm);
+ const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset);
+ const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset);
+ if ((luv_src_v1->flag & MLOOPUV_VERTSEL) == 0 && (luv_src_v2->flag & MLOOPUV_VERTSEL) == 0) {
+ l_src = NULL;
+ }
+
+ ele_src = (BMElem *)l_src;
+ ele_dst = (BMElem *)hit.l;
+ }
+ else {
+ UvNearestHit hit = UV_NEAREST_HIT_INIT;
+ if (!uv_find_nearest_vert(scene, obedit, co, 0.0f, &hit)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BMLoop *l_src = ED_uvedit_active_vert_loop_get(bm);
+ const MLoopUV *luv_src = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset);
+ if ((luv_src->flag & MLOOPUV_VERTSEL) == 0) {
+ l_src = NULL;
+ }
+
+ ele_src = (BMElem *)l_src;
+ ele_dst = (BMElem *)hit.l;
+ }
+
+ if (ele_src == NULL || ele_dst == NULL) {
+ return OPERATOR_CANCELLED;
+ }
+
+ uv_shortest_path_pick_ex(
+ scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset);
+
+ /* To support redo. */
+ int index;
+ if (ts->uv_selectmode & UV_SELECT_FACE) {
+ BM_mesh_elem_index_ensure(bm, BM_FACE);
+ index = BM_elem_index_get(ele_dst);
+ }
+ else if (ts->uv_selectmode & UV_SELECT_EDGE) {
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+ index = BM_elem_index_get(ele_dst);
+ }
+ else {
+ BM_mesh_elem_index_ensure(bm, BM_LOOP);
+ index = BM_elem_index_get(ele_dst);
+ }
+ RNA_int_set(op->ptr, "index", index);
+
+ return OPERATOR_FINISHED;
+}
+
+static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ const ToolSettings *ts = scene->toolsettings;
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+ float aspect_y;
+ {
+ float aspx, aspy;
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ }
+
+ const int index = RNA_int_get(op->ptr, "index");
+
+ BMElem *ele_src, *ele_dst;
+
+ if (ts->uv_selectmode & UV_SELECT_FACE) {
+ if (index < 0 || index >= bm->totface) {
+ return OPERATOR_CANCELLED;
+ }
+ if (!(ele_src = (BMElem *)BM_mesh_active_face_get(bm, false, false)) ||
+ !(ele_dst = (BMElem *)BM_face_at_index_find_or_table(bm, index))) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else if (ts->uv_selectmode & UV_SELECT_EDGE) {
+ if (index < 0 || index >= bm->totloop) {
+ return OPERATOR_CANCELLED;
+ }
+ if (!(ele_src = (BMElem *)ED_uvedit_active_edge_loop_get(bm)) ||
+ !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index))) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+ else {
+ if (index < 0 || index >= bm->totloop) {
+ return OPERATOR_CANCELLED;
+ }
+ if (!(ele_src = (BMElem *)ED_uvedit_active_vert_loop_get(bm)) ||
+ !(ele_dst = (BMElem *)BM_loop_at_index_find(bm, index))) {
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ struct PathSelectParams op_params;
+ path_select_params_from_op(op, &op_params);
+ op_params.track_active = true;
+
+ if (!uv_shortest_path_pick_ex(
+ scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void UV_OT_shortest_path_pick(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Pick Shortest Path";
+ ot->idname = "UV_OT_shortest_path_pick";
+ ot->description = "Select shortest path between two selections";
+
+ /* api callbacks */
+ ot->invoke = uv_shortest_path_pick_invoke;
+ ot->exec = uv_shortest_path_pick_exec;
+ ot->poll = ED_operator_uvedit;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ path_select_properties(ot);
+
+ /* use for redo */
+ prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Select Path Between Existing Selection
+ * \{ */
+
+static int uv_shortest_path_select_exec(bContext *C, wmOperator *op)
+{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ Scene *scene = CTX_data_scene(C);
+ const ToolSettings *ts = scene->toolsettings;
+ bool found_valid_elements = false;
+
+ float aspect_y;
+ {
+ Object *obedit = CTX_data_edit_object(C);
+ float aspx, aspy;
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ aspect_y = aspx / aspy;
+ }
+
+ 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);
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ BMElem *ele_src = NULL, *ele_dst = NULL;
+
+ /* Find 2x elements. */
+ {
+ BMElem **ele_array = NULL;
+ int ele_array_len = 0;
+ if (ts->uv_selectmode & UV_SELECT_FACE) {
+ ele_array = (BMElem **)ED_uvedit_selected_faces(scene, bm, 3, &ele_array_len);
+ }
+ else if (ts->uv_selectmode & UV_SELECT_EDGE) {
+ ele_array = (BMElem **)ED_uvedit_selected_edges(scene, bm, 3, &ele_array_len);
+ }
+ else {
+ ele_array = (BMElem **)ED_uvedit_selected_verts(scene, bm, 3, &ele_array_len);
+ }
+
+ if (ele_array_len == 2) {
+ ele_src = ele_array[0];
+ ele_dst = ele_array[1];
+ }
+ MEM_freeN(ele_array);
+ }
+
+ if (ele_src && ele_dst) {
+ struct PathSelectParams op_params;
+ path_select_params_from_op(op, &op_params);
+
+ uv_shortest_path_pick_ex(
+ scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset);
+
+ found_valid_elements = true;
+ }
+ }
+ MEM_freeN(objects);
+
+ if (!found_valid_elements) {
+ BKE_report(
+ op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected");
+ return OPERATOR_CANCELLED;
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void UV_OT_shortest_path_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Shortest Path";
+ ot->idname = "UV_OT_shortest_path_select";
+ ot->description = "Selected shortest path between two vertices/edges/faces";
+
+ /* api callbacks */
+ ot->exec = uv_shortest_path_select_exec;
+ ot->poll = ED_operator_editmesh;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ path_select_properties(ot);
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_rip.c b/source/blender/editors/uvedit/uvedit_rip.c
new file mode 100644
index 00000000000..562f0ce84c1
--- /dev/null
+++ b/source/blender/editors/uvedit/uvedit_rip.c
@@ -0,0 +1,981 @@
+/*
+ * 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 eduv
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_ghash.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_math.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "DNA_image_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_editmesh.h"
+#include "BKE_layer.h"
+#include "BKE_report.h"
+
+#include "DEG_depsgraph.h"
+
+#include "ED_screen.h"
+#include "ED_transform.h"
+#include "ED_uvedit.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "UI_view2d.h"
+
+#include "uvedit_intern.h"
+
+/* -------------------------------------------------------------------- */
+/** \name UV Loop Rip Data Struct
+ * \{ */
+
+/** Unordered loop data, stored in #BMLoop.head.index. */
+typedef struct ULData {
+ /** When this UV is selected as well as the next UV. */
+ uint is_select_edge : 1;
+ /**
+ * When only this UV is selected and none of the other UV's
+ * around the connected fan are attached to an edge.
+ *
+ * In this case there is no need to detect contiguous loops,
+ * each isolated case is handled on it's own, no need to walk over selected edges.
+ *
+ * \note This flag isn't flushed to other loops which could also have this enabled.
+ * Currently it's not necessary since we can start off on any one of these loops,
+ * then walk onto the other loops around the uv-fan, without having the flag to be
+ * set on all loops.
+ */
+ uint is_select_vert_single : 1;
+ /** This could be a face-tag. */
+ uint is_select_all : 1;
+ /** Use when building the rip-pairs stack. */
+ uint in_stack : 1;
+ /** Set once this has been added into a #UVRipPairs. */
+ uint in_rip_pairs : 1;
+ /** The side this loop is part of. */
+ uint side : 1;
+ /**
+ * Paranoid check to ensure we don't enter eternal loop swapping sides,
+ * this could happen with float precision error, making a swap to measure as slightly better
+ * depending on the order of addition.
+ */
+ uint side_was_swapped : 1;
+} ULData;
+
+/** Ensure this fits in an int (loop index). */
+BLI_STATIC_ASSERT(sizeof(ULData) <= sizeof(int), "");
+
+BLI_INLINE ULData *UL(BMLoop *l)
+{
+ return (ULData *)&l->head.index;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Utilities
+ * \{ */
+
+static BMLoop *bm_loop_find_other_radial_loop_with_visible_face(BMLoop *l_src,
+ const int cd_loop_uv_offset)
+{
+ BMLoop *l_other = NULL;
+ BMLoop *l_iter = l_src->radial_next;
+ if (l_iter != l_src) {
+ do {
+ if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) && UL(l_iter)->is_select_edge &&
+ BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) {
+ /* Check UV's are contiguous. */
+ if (l_other == NULL) {
+ l_other = l_iter;
+ }
+ else {
+ /* Only use when there is a single alternative. */
+ l_other = NULL;
+ break;
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_src);
+ }
+ return l_other;
+}
+
+static BMLoop *bm_loop_find_other_fan_loop_with_visible_face(BMLoop *l_src,
+ BMVert *v_src,
+ const int cd_loop_uv_offset)
+{
+ BLI_assert(BM_vert_in_edge(l_src->e, v_src));
+ BMLoop *l_other = NULL;
+ BMLoop *l_iter = l_src->radial_next;
+ if (l_iter != l_src) {
+ do {
+ if (BM_elem_flag_test(l_iter->f, BM_ELEM_TAG) &&
+ BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) {
+ /* Check UV's are contiguous. */
+ if (l_other == NULL) {
+ l_other = l_iter;
+ }
+ else {
+ /* Only use when there is a single alternative. */
+ l_other = NULL;
+ break;
+ }
+ }
+ } while ((l_iter = l_iter->radial_next) != l_src);
+ }
+ if (l_other != NULL) {
+ if (l_other->v == v_src) {
+ /* do nothing. */
+ }
+ else if (l_other->next->v == v_src) {
+ l_other = l_other->next;
+ }
+ else if (l_other->prev->v == v_src) {
+ l_other = l_other->prev;
+ }
+ else {
+ BLI_assert(0);
+ }
+ }
+ return l_other;
+}
+
+/**
+ * A version of #BM_vert_step_fan_loop that checks UV's.
+ */
+static BMLoop *bm_vert_step_fan_loop_uv(BMLoop *l, BMEdge **e_step, const int cd_loop_uv_offset)
+{
+ BMEdge *e_prev = *e_step;
+ BMLoop *l_next;
+ if (l->e == e_prev) {
+ l_next = l->prev;
+ }
+ else if (l->prev->e == e_prev) {
+ l_next = l;
+ }
+ else {
+ BLI_assert(0);
+ return NULL;
+ }
+
+ *e_step = l_next->e;
+
+ return bm_loop_find_other_fan_loop_with_visible_face(l_next, l->v, cd_loop_uv_offset);
+}
+
+static void bm_loop_uv_select_single_vert_validate(BMLoop *l_init, const int cd_loop_uv_offset)
+{
+ const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset);
+ BMIter liter;
+ BMLoop *l;
+ bool is_single_vert = true;
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ if (UL(l->prev)->is_select_edge || UL(l)->is_select_edge) {
+ is_single_vert = false;
+ break;
+ }
+ }
+ }
+ if (is_single_vert == false) {
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ if (UL(l)->is_select_vert_single) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ UL(l)->is_select_vert_single = false;
+ }
+ }
+ }
+ }
+}
+
+/**
+ * The corner return values calculate the angle between both loops,
+ * the edge values pick the closest to the either edge (ignoring the center).
+ *
+ * \param dir: Direction to calculate the angle to (normalized and aspect corrected).
+ */
+static void bm_loop_calc_uv_angle_from_dir(BMLoop *l,
+ const float dir[2],
+ const float aspect_y,
+ const int cd_loop_uv_offset,
+ float *r_corner_angle,
+ float *r_edge_angle,
+ int *r_edge_index)
+{
+ /* Calculate 3 directions, return the shortest angle. */
+ float dir_test[3][2];
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ sub_v2_v2v2(dir_test[0], luv->uv, luv_prev->uv);
+ sub_v2_v2v2(dir_test[2], luv->uv, luv_next->uv);
+ dir_test[0][1] /= aspect_y;
+ dir_test[2][1] /= aspect_y;
+
+ normalize_v2(dir_test[0]);
+ normalize_v2(dir_test[2]);
+
+ /* Calculate the orthogonal line (same as negating one, then adding). */
+ sub_v2_v2v2(dir_test[1], dir_test[0], dir_test[2]);
+ normalize_v2(dir_test[1]);
+
+ /* Rotate 90 degrees. */
+ SWAP(float, dir_test[1][0], dir_test[1][1]);
+ dir_test[1][1] *= -1.0f;
+
+ if (BM_face_uv_calc_cross(l->f, cd_loop_uv_offset) > 0.0f) {
+ negate_v2(dir_test[1]);
+ }
+
+ const float angles[3] = {
+ angle_v2v2(dir, dir_test[0]),
+ angle_v2v2(dir, dir_test[1]),
+ angle_v2v2(dir, dir_test[2]),
+ };
+
+ /* Set the corner values. */
+ *r_corner_angle = angles[1];
+
+ /* Set the edge values. */
+ if (angles[0] < angles[2]) {
+ *r_edge_angle = angles[0];
+ *r_edge_index = -1;
+ }
+ else {
+ *r_edge_angle = angles[2];
+ *r_edge_index = 1;
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Single
+ * \{ */
+
+typedef struct UVRipSingle {
+ /** Walk around the selected UV point, store #BMLoop. */
+ GSet *loops;
+} UVRipSingle;
+
+/**
+ * Handle single loop, the following cases:
+ *
+ * - An isolated fan, without a shared UV edge to other fans which share the same coordinate,
+ * in this case we just need to pick the closest fan to \a co.
+ *
+ * - In the case of contiguous loops (part of the same fan).
+ * Rip away the loops connected to the closest edge.
+ *
+ * - In the case of 2 contiguous loops.
+ * Rip the closest loop away.
+ *
+ * \note This matches the behavior of edit-mesh rip tool.
+ */
+static UVRipSingle *uv_rip_single_from_loop(BMLoop *l_init_orig,
+ const float co[2],
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ UVRipSingle *rip = MEM_callocN(sizeof(*rip), __func__);
+ const float *co_center =
+ (((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_init_orig, cd_loop_uv_offset))->uv);
+ rip->loops = BLI_gset_ptr_new(__func__);
+
+ /* Track the closest loop, start walking from this so in the event we have multiple
+ * disconnected fans, we can rip away loops connected to this one. */
+ BMLoop *l_init = NULL;
+ BMLoop *l_init_edge = NULL;
+ float corner_angle_best = FLT_MAX;
+ float edge_angle_best = FLT_MAX;
+ int edge_index_best = 0; /* -1 or +1 (never center). */
+
+ /* Calculate the direction from the cursor with aspect correction. */
+ float dir_co[2];
+ sub_v2_v2v2(dir_co, co_center, co);
+ dir_co[1] /= aspect_y;
+ if (UNLIKELY(normalize_v2(dir_co) == 0.0)) {
+ dir_co[1] = 1.0f;
+ }
+
+ int uv_fan_count_all = 0;
+ {
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, l_init_orig->v, BM_LOOPS_OF_VERT) {
+ if (BM_elem_flag_test(l->f, BM_ELEM_TAG)) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(co_center, luv->uv)) {
+ uv_fan_count_all += 1;
+ /* Clear at the same time. */
+ UL(l)->is_select_vert_single = true;
+ UL(l)->side = 0;
+ BLI_gset_add(rip->loops, l);
+
+ /* Update `l_init_close` */
+ float corner_angle_test;
+ float edge_angle_test;
+ int edge_index_test;
+ bm_loop_calc_uv_angle_from_dir(l,
+ dir_co,
+ aspect_y,
+ cd_loop_uv_offset,
+ &corner_angle_test,
+ &edge_angle_test,
+ &edge_index_test);
+ if ((corner_angle_best == FLT_MAX) || (corner_angle_test < corner_angle_best)) {
+ corner_angle_best = corner_angle_test;
+ l_init = l;
+ }
+
+ /* Trick so we don't consider concave corners further away than they should be. */
+ edge_angle_test = min_ff(corner_angle_test, edge_angle_test);
+
+ if ((edge_angle_best == FLT_MAX) || (edge_angle_test < edge_angle_best)) {
+ edge_angle_best = edge_angle_test;
+ edge_index_best = edge_index_test;
+ l_init_edge = l;
+ }
+ }
+ }
+ }
+ }
+
+ /* Walk around the `l_init` in both directions of the UV fan. */
+ int uv_fan_count_contiguous = 1;
+ UL(l_init)->side = 1;
+ for (int i = 0; i < 2; i += 1) {
+ BMEdge *e_prev = i ? l_init->e : l_init->prev->e;
+ BMLoop *l_iter = l_init;
+ while (((l_iter = bm_vert_step_fan_loop_uv(l_iter, &e_prev, cd_loop_uv_offset)) != l_init) &&
+ (l_iter != NULL) && (UL(l_iter)->side == 0)) {
+ uv_fan_count_contiguous += 1;
+ /* Keep. */
+ UL(l_iter)->side = 1;
+ }
+ /* May be useful to know if the fan is closed, currently it's not needed. */
+#if 0
+ if (l_iter == l_init) {
+ is_closed = true;
+ }
+#endif
+ }
+
+ if (uv_fan_count_contiguous != uv_fan_count_all) {
+ /* Simply rip off the current fan, all tagging is done. */
+ }
+ else {
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l = BLI_gsetIterator_getKey(&gs_iter);
+ UL(l)->side = 0;
+ }
+
+ if (uv_fan_count_contiguous <= 2) {
+ /* Simple case, rip away the closest loop. */
+ UL(l_init)->side = 1;
+ }
+ else {
+ /* Rip away from the closest edge. */
+ BMLoop *l_radial_init = (edge_index_best == -1) ? l_init_edge->prev : l_init_edge;
+ BMLoop *l_radial_iter = l_radial_init;
+ do {
+ if (BM_loop_uv_share_edge_check(l_radial_init, l_radial_iter, cd_loop_uv_offset)) {
+ BMLoop *l = (l_radial_iter->v == l_init->v) ? l_radial_iter : l_radial_iter->next;
+ BLI_assert(l->v == l_init->v);
+ /* Keep. */
+ UL(l)->side = 1;
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_init);
+ }
+ }
+
+ return rip;
+}
+
+static void uv_rip_single_free(UVRipSingle *rip)
+{
+ BLI_gset_free(rip->loops, NULL);
+ MEM_freeN(rip);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Loop Pairs
+ * \{ */
+
+typedef struct UVRipPairs {
+ /** Walk along the UV selection, store #BMLoop. */
+ GSet *loops;
+} UVRipPairs;
+
+static void uv_rip_pairs_add(UVRipPairs *rip, BMLoop *l)
+{
+ ULData *ul = UL(l);
+ BLI_assert(!BLI_gset_haskey(rip->loops, l));
+ BLI_assert(ul->in_rip_pairs == false);
+ ul->in_rip_pairs = true;
+ BLI_gset_add(rip->loops, l);
+}
+
+static void uv_rip_pairs_remove(UVRipPairs *rip, BMLoop *l)
+{
+ ULData *ul = UL(l);
+ BLI_assert(BLI_gset_haskey(rip->loops, l));
+ BLI_assert(ul->in_rip_pairs == true);
+ ul->in_rip_pairs = false;
+ BLI_gset_remove(rip->loops, l, NULL);
+}
+
+/**
+ * \note While this isn't especially efficient,
+ * this is only needed for rip-pairs end-points (only two per contiguous selection loop).
+ */
+static float uv_rip_pairs_calc_uv_angle(BMLoop *l_init,
+ uint side,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ BMIter liter;
+ const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset);
+ float angle_of_side = 0.0f;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ if (UL(l)->in_rip_pairs) {
+ if (UL(l)->side == side) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ float dir_prev[2], dir_next[2];
+ sub_v2_v2v2(dir_prev, luv_prev->uv, luv->uv);
+ sub_v2_v2v2(dir_next, luv_next->uv, luv->uv);
+ dir_prev[1] /= aspect_y;
+ dir_next[1] /= aspect_y;
+ const float luv_angle = angle_v2v2(dir_prev, dir_next);
+ if (LIKELY(isfinite(luv_angle))) {
+ angle_of_side += luv_angle;
+ }
+ }
+ }
+ }
+ }
+ return angle_of_side;
+}
+
+static int uv_rip_pairs_loop_count_on_side(BMLoop *l_init, uint side, const int cd_loop_uv_offset)
+{
+ const MLoopUV *luv_init = BM_ELEM_CD_GET_VOID_P(l_init, cd_loop_uv_offset);
+ int count = 0;
+ BMIter liter;
+ BMLoop *l;
+ BM_ITER_ELEM (l, &liter, l_init->v, BM_LOOPS_OF_VERT) {
+ if (UL(l)->in_rip_pairs) {
+ if (UL(l)->side == side) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (equals_v2v2(luv_init->uv, luv->uv)) {
+ count += 1;
+ }
+ }
+ }
+ }
+ return count;
+}
+
+static bool uv_rip_pairs_loop_change_sides_test(BMLoop *l_switch,
+ BMLoop *l_target,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ const int side_a = UL(l_switch)->side;
+ const int side_b = UL(l_target)->side;
+
+ BLI_assert(UL(l_switch)->side != UL(l_target)->side);
+
+ /* First, check if this is a simple grid topology,
+ * in that case always choose the adjacent edge. */
+ const int count_a = uv_rip_pairs_loop_count_on_side(l_switch, side_a, cd_loop_uv_offset);
+ const int count_b = uv_rip_pairs_loop_count_on_side(l_target, side_b, cd_loop_uv_offset);
+ if (count_a + count_b == 4) {
+ return count_a > count_b;
+ }
+ else {
+ const float angle_a_before = uv_rip_pairs_calc_uv_angle(
+ l_switch, side_a, aspect_y, cd_loop_uv_offset);
+ const float angle_b_before = uv_rip_pairs_calc_uv_angle(
+ l_target, side_b, aspect_y, cd_loop_uv_offset);
+
+ UL(l_switch)->side = side_b;
+
+ const float angle_a_after = uv_rip_pairs_calc_uv_angle(
+ l_switch, side_a, aspect_y, cd_loop_uv_offset);
+ const float angle_b_after = uv_rip_pairs_calc_uv_angle(
+ l_target, side_b, aspect_y, cd_loop_uv_offset);
+
+ UL(l_switch)->side = side_a;
+
+ return fabsf(angle_a_before - angle_b_before) > fabsf(angle_a_after - angle_b_after);
+ }
+}
+
+/**
+ * Create 2x sides of a UV rip-pairs, the result is unordered, supporting non-contiguous rails.
+ *
+ * \param l_init: A loop on a boundary which can be used to initialize flood-filling.
+ * This will always be added to the first side. Other loops will be added to the second side.
+ *
+ * \note We could have more than two sides, however in practice this almost never happens.
+ */
+static UVRipPairs *uv_rip_pairs_from_loop(BMLoop *l_init,
+ const float aspect_y,
+ const int cd_loop_uv_offset)
+{
+ UVRipPairs *rip = MEM_callocN(sizeof(*rip), __func__);
+ rip->loops = BLI_gset_ptr_new(__func__);
+
+ /* We can rely on this stack being small, as we're walking down two sides of an edge loop,
+ * so the stack wont be much larger than the total number of fans at any one vertex. */
+ BLI_SMALLSTACK_DECLARE(stack, BMLoop *);
+
+ /* Needed for cases when we walk onto loops which already have a side assigned,
+ * in this case we need to pick a better side (see #uv_rip_pairs_loop_change_sides_test)
+ * and put the loop back in the stack,
+ * which is needed in the case adjacent loops should also switch sides. */
+#define UV_SET_SIDE_AND_REMOVE_FROM_RAIL(loop, side_value) \
+ { \
+ BLI_assert(UL(loop)->side_was_swapped == false); \
+ BLI_assert(UL(loop)->side != side_value); \
+ if (!UL(loop)->in_stack) { \
+ BLI_SMALLSTACK_PUSH(stack, loop); \
+ UL(loop)->in_stack = true; \
+ } \
+ if (UL(loop)->in_rip_pairs) { \
+ uv_rip_pairs_remove(rip, loop); \
+ } \
+ UL(loop)->side = side_value; \
+ UL(loop)->side_was_swapped = true; \
+ }
+
+ /* Initialize the stack. */
+ BLI_SMALLSTACK_PUSH(stack, l_init);
+ UL(l_init)->in_stack = true;
+
+ BMLoop *l_step;
+ while ((l_step = BLI_SMALLSTACK_POP(stack))) {
+ int side = UL(l_step)->side;
+ UL(l_step)->in_stack = false;
+
+ /* Note that we could add all loops into the rip-pairs when adding into the stack,
+ * however this complicates removal, so add into the rip-pairs when popping from the stack. */
+ uv_rip_pairs_add(rip, l_step);
+
+ /* Add to the other side if it exists. */
+ if (UL(l_step)->is_select_edge) {
+ BMLoop *l_other = bm_loop_find_other_radial_loop_with_visible_face(l_step,
+ cd_loop_uv_offset);
+ if (l_other != NULL) {
+ if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
+ BLI_SMALLSTACK_PUSH(stack, l_other);
+ UL(l_other)->in_stack = true;
+ UL(l_other)->side = !side;
+ }
+ else {
+ if (UL(l_other)->side == side) {
+ if (UL(l_other)->side_was_swapped == false) {
+ UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, !side);
+ }
+ }
+ }
+ }
+
+ /* Add the next loop along the edge on the same side. */
+ l_other = l_step->next;
+ if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
+ BLI_SMALLSTACK_PUSH(stack, l_other);
+ UL(l_other)->in_stack = true;
+ UL(l_other)->side = side;
+ }
+ else {
+ if (UL(l_other)->side != side) {
+ if ((UL(l_other)->side_was_swapped == false) &&
+ uv_rip_pairs_loop_change_sides_test(l_other, l_step, aspect_y, cd_loop_uv_offset)) {
+ UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, side);
+ }
+ }
+ }
+ }
+
+ /* Walk over the fan of loops, starting from `l_step` in both directions. */
+ for (int i = 0; i < 2; i++) {
+ BMLoop *l_radial_first = i ? l_step : l_step->prev;
+ if (l_radial_first != l_radial_first->radial_next) {
+ BMEdge *e_radial = l_radial_first->e;
+ BMLoop *l_radial_iter = l_radial_first->radial_next;
+ do {
+ /* Not a boundary and visible. */
+ if (!UL(l_radial_iter)->is_select_edge &&
+ BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) {
+ BMLoop *l_other = (l_radial_iter->v == l_step->v) ? l_radial_iter :
+ l_radial_iter->next;
+ BLI_assert(l_other->v == l_step->v);
+ if (BM_edge_uv_share_vert_check(e_radial, l_other, l_step, cd_loop_uv_offset)) {
+ if (!UL(l_other)->in_rip_pairs && !UL(l_other)->in_stack) {
+ BLI_SMALLSTACK_PUSH(stack, l_other);
+ UL(l_other)->in_stack = true;
+ UL(l_other)->side = side;
+ }
+ else {
+ if (UL(l_other)->side != side) {
+ if ((UL(l_other)->side_was_swapped == false) &&
+ uv_rip_pairs_loop_change_sides_test(
+ l_other, l_step, aspect_y, cd_loop_uv_offset)) {
+ UV_SET_SIDE_AND_REMOVE_FROM_RAIL(l_other, side);
+ }
+ }
+ }
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first);
+ }
+ }
+ }
+
+#undef UV_SET_SIDE_AND_REMOVE_FROM_RAIL
+
+ return rip;
+}
+
+static void uv_rip_pairs_free(UVRipPairs *rip)
+{
+ BLI_gset_free(rip->loops, NULL);
+ MEM_freeN(rip);
+}
+
+/**
+ * This is an approximation, it's easily good enough for our purpose.
+ */
+static bool uv_rip_pairs_calc_center_and_direction(UVRipPairs *rip,
+ const int cd_loop_uv_offset,
+ float r_center[2],
+ float r_dir_side[2][2])
+{
+ zero_v2(r_center);
+ int center_total = 0;
+ int side_total[2] = {0, 0};
+
+ for (int i = 0; i < 2; i++) {
+ zero_v2(r_dir_side[i]);
+ }
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l = BLI_gsetIterator_getKey(&gs_iter);
+ int side = UL(l)->side;
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ add_v2_v2(r_center, luv->uv);
+
+ float dir[2];
+ if (!UL(l)->is_select_edge) {
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ sub_v2_v2v2(dir, luv_next->uv, luv->uv);
+ add_v2_v2(r_dir_side[side], dir);
+ }
+ if (!UL(l->prev)->is_select_edge) {
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ sub_v2_v2v2(dir, luv_prev->uv, luv->uv);
+ add_v2_v2(r_dir_side[side], dir);
+ }
+ side_total[side] += 1;
+ }
+ center_total += BLI_gset_len(rip->loops);
+
+ for (int i = 0; i < 2; i++) {
+ normalize_v2(r_dir_side[i]);
+ }
+ mul_v2_fl(r_center, 1.0f / center_total);
+
+ /* If only a single side is selected, don't handle this rip-pairs. */
+ return side_total[0] && side_total[1];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Main Function
+ * \{ */
+
+/**
+ * \return true when a change was made.
+ */
+static bool uv_rip_object(Scene *scene, Object *obedit, const float co[2], const float aspect_y)
+{
+ Mesh *me = (Mesh *)obedit->data;
+ BMEditMesh *em = me->edit_mesh;
+ BMesh *bm = em->bm;
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ BMFace *efa;
+ BMIter iter, liter;
+ BMLoop *l;
+
+ const ULData ul_clear = {0};
+
+ bool changed = false;
+
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_set(efa, BM_ELEM_TAG, uvedit_face_visible_test(scene, efa));
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ ULData *ul = UL(l);
+ *ul = ul_clear;
+ }
+ }
+ bm->elem_index_dirty |= BM_LOOP;
+
+ bool is_select_all_any = false;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+ bool is_all = true;
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_VERTSEL) {
+ const MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ if (luv_next->flag & MLOOPUV_VERTSEL) {
+ UL(l)->is_select_edge = true;
+ }
+ else {
+ if ((luv_prev->flag & MLOOPUV_VERTSEL) == 0) {
+ /* #bm_loop_uv_select_single_vert_validate validates below. */
+ UL(l)->is_select_vert_single = true;
+ }
+ }
+ }
+ else {
+ is_all = false;
+ }
+ }
+ if (is_all) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ UL(l)->is_select_all = true;
+ }
+ is_select_all_any = true;
+ }
+ }
+ }
+
+ /* Remove #ULData.is_select_vert_single when connected to selected edges. */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (UL(l)->is_select_vert_single) {
+ bm_loop_uv_select_single_vert_validate(l, cd_loop_uv_offset);
+ }
+ }
+ }
+ }
+
+ /* Special case: if we have selected faces, isolated them.
+ * This isn't a rip, however it's useful for users as a quick way
+ * to detach the selection.
+ *
+ * We could also extract an edge loop from the boundary
+ * however in practice it's not that useful, see T78751. */
+ if (is_select_all_any) {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (!UL(l)->is_select_all) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_VERTSEL) {
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ changed = true;
+ }
+ }
+ }
+ }
+ return changed;
+ }
+
+ /* Extract loop pairs or single loops. */
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ if (UL(l)->is_select_edge) {
+ if (!UL(l)->in_rip_pairs) {
+ UVRipPairs *rip = uv_rip_pairs_from_loop(l, aspect_y, cd_loop_uv_offset);
+ float center[2];
+ float dir_cursor[2];
+ float dir_side[2][2];
+ int side_from_cursor = -1;
+ if (uv_rip_pairs_calc_center_and_direction(rip, cd_loop_uv_offset, center, dir_side)) {
+ for (int i = 0; i < 2; i++) {
+ sub_v2_v2v2(dir_cursor, center, co);
+ normalize_v2(dir_cursor);
+ }
+ side_from_cursor = (dot_v2v2(dir_side[0], dir_cursor) -
+ dot_v2v2(dir_side[1], dir_cursor)) < 0.0f;
+ }
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter);
+ ULData *ul = UL(l_iter);
+ if (ul->side == side_from_cursor) {
+ uvedit_uv_select_disable(em, scene, l_iter, cd_loop_uv_offset);
+ changed = true;
+ }
+ /* Ensure we don't operate on these again. */
+ *ul = ul_clear;
+ }
+ uv_rip_pairs_free(rip);
+ }
+ }
+ else if (UL(l)->is_select_vert_single) {
+ UVRipSingle *rip = uv_rip_single_from_loop(l, co, aspect_y, cd_loop_uv_offset);
+ /* We only ever use one side. */
+ const int side_from_cursor = 0;
+ GSetIterator gs_iter;
+ GSET_ITER (gs_iter, rip->loops) {
+ BMLoop *l_iter = BLI_gsetIterator_getKey(&gs_iter);
+ ULData *ul = UL(l_iter);
+ if (ul->side == side_from_cursor) {
+ uvedit_uv_select_disable(em, scene, l_iter, cd_loop_uv_offset);
+ changed = true;
+ }
+ /* Ensure we don't operate on these again. */
+ *ul = ul_clear;
+ }
+ uv_rip_single_free(rip);
+ }
+ }
+ }
+ }
+ return changed;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name UV Rip Operator
+ * \{ */
+
+static int uv_rip_exec(bContext *C, wmOperator *op)
+{
+ SpaceImage *sima = CTX_wm_space_image(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+
+ bool changed_multi = false;
+
+ float co[2];
+ RNA_float_get_array(op->ptr, "location", co);
+
+ float aspx, aspy;
+ {
+ /* Note that we only want to run this on the */
+ Object *obedit = CTX_data_edit_object(C);
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
+ }
+ const float aspect_y = aspx / aspy;
+
+ uint objects_len = 0;
+ Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
+ view_layer, ((View3D *)NULL), &objects_len);
+
+ for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+ Object *obedit = objects[ob_index];
+
+ if (uv_rip_object(scene, obedit, co, aspect_y)) {
+ changed_multi = true;
+ uvedit_live_unwrap_update(sima, scene, obedit);
+ DEG_id_tag_update(obedit->data, 0);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+ }
+ }
+ MEM_freeN(objects);
+
+ if (!changed_multi) {
+ BKE_report(op->reports, RPT_ERROR, "Rip failed");
+ return OPERATOR_CANCELLED;
+ }
+ return OPERATOR_FINISHED;
+}
+
+static int uv_rip_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ ARegion *ar = CTX_wm_region(C);
+ float co[2];
+
+ UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
+ RNA_float_set_array(op->ptr, "location", co);
+
+ return uv_rip_exec(C, op);
+}
+
+void UV_OT_rip(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "UV Rip";
+ ot->description = "Rip selected vertices or a selected region";
+ ot->idname = "UV_OT_rip";
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* api callbacks */
+ ot->exec = uv_rip_exec;
+ ot->invoke = uv_rip_invoke;
+ ot->poll = ED_operator_uvedit;
+
+ /* translation data */
+ Transform_Properties(ot, P_MIRROR_DUMMY);
+
+ /* properties */
+ RNA_def_float_vector(
+ ot->srna,
+ "location",
+ 2,
+ NULL,
+ -FLT_MAX,
+ FLT_MAX,
+ "Location",
+ "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
+ -100.0f,
+ 100.0f);
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index b701e94cd77..ddb276a663a 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -88,6 +88,59 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
Object *obedit);
/* -------------------------------------------------------------------- */
+/** \name Active Selection Tracking
+ *
+ * Currently we don't store loops in the selection history,
+ * store face/edge/vert combinations (needed for UV path selection).
+ * \{ */
+
+void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l)
+{
+ BM_select_history_clear(bm);
+ BM_select_history_remove(bm, (BMElem *)l->f);
+ BM_select_history_remove(bm, (BMElem *)l->v);
+ BM_select_history_store_notest(bm, (BMElem *)l->f);
+ BM_select_history_store_notest(bm, (BMElem *)l->v);
+}
+
+BMLoop *ED_uvedit_active_vert_loop_get(BMesh *bm)
+{
+ BMEditSelection *ese = bm->selected.last;
+ if (ese && ese->prev) {
+ BMEditSelection *ese_prev = ese->prev;
+ if ((ese->htype == BM_VERT) && (ese_prev->htype == BM_FACE)) {
+ /* May be NULL. */
+ return BM_face_vert_share_loop((BMFace *)ese_prev->ele, (BMVert *)ese->ele);
+ }
+ }
+ return NULL;
+}
+
+void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
+{
+ BM_select_history_clear(bm);
+ BM_select_history_remove(bm, (BMElem *)l->f);
+ BM_select_history_remove(bm, (BMElem *)l->e);
+ BM_select_history_store_notest(bm, (BMElem *)l->f);
+ BM_select_history_store_notest(bm, (BMElem *)l->e);
+}
+
+BMLoop *ED_uvedit_active_edge_loop_get(BMesh *bm)
+{
+ BMEditSelection *ese = bm->selected.last;
+ if (ese && ese->prev) {
+ BMEditSelection *ese_prev = ese->prev;
+ if ((ese->htype == BM_EDGE) && (ese_prev->htype == BM_FACE)) {
+ /* May be NULL. */
+ return BM_face_edge_share_loop((BMFace *)ese_prev->ele, (BMEdge *)ese->ele);
+ }
+ }
+ return NULL;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Visibility and Selection Utilities
* \{ */
@@ -1424,7 +1477,7 @@ void UV_OT_select_all(wmOperatorType *ot)
* \{ */
static bool uv_sticky_select(
- float *limit, int hitv[], int v, float *hituv[], float *uv, int sticky, int hitlen)
+ const float *limit, const int hitv[], int v, float *hituv[], float *uv, int sticky, int hitlen)
{
int i;
@@ -1531,6 +1584,11 @@ static int uv_mouse_select_multi(bContext *C,
hituv[hit.lindex] = hit.luv->uv;
hitlen = hit.efa->len;
+
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
+ BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
+ ED_uvedit_active_vert_loop_set(bm, hit.l);
+ }
}
}
else if (selectmode == UV_SELECT_EDGE) {
@@ -1550,6 +1608,11 @@ static int uv_mouse_select_multi(bContext *C,
hituv[(hit.lindex + 1) % hit.efa->len] = hit.luv_next->uv;
hitlen = hit.efa->len;
+
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
+ BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
+ ED_uvedit_active_edge_loop_set(bm, hit.l);
+ }
}
}
else if (selectmode == UV_SELECT_FACE) {
@@ -2061,6 +2124,7 @@ void UV_OT_select_linked_pick(wmOperatorType *ot)
*/
static int uv_select_split_exec(bContext *C, wmOperator *op)
{
+ Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const ToolSettings *ts = scene->toolsettings;
@@ -2127,6 +2191,7 @@ static int uv_select_split_exec(bContext *C, wmOperator *op)
if (changed) {
changed_multi = true;
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
+ uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
}
MEM_freeN(objects);
@@ -3354,3 +3419,154 @@ void UV_OT_select_overlap(wmOperatorType *ot)
}
/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Selected Elements as Arrays (Vertex, Edge & Faces)
+ *
+ * These functions return single elements per connected vertex/edge.
+ * So an edge that has two connected edge loops only assigns one loop in the array.
+ * \{ */
+
+BMFace **ED_uvedit_selected_faces(Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
+{
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ CLAMP_MAX(len_max, bm->totface);
+ int faces_len = 0;
+ BMFace **faces = MEM_mallocN(sizeof(*faces) * len_max, __func__);
+
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (uvedit_face_visible_test(scene, f)) {
+ if (uvedit_face_select_test(scene, f, cd_loop_uv_offset)) {
+ faces[faces_len++] = f;
+ if (faces_len == len_max) {
+ goto finally;
+ }
+ }
+ }
+ }
+
+finally:
+ *r_faces_len = faces_len;
+ if (faces_len != len_max) {
+ faces = MEM_reallocN(faces, sizeof(*faces) * faces_len);
+ }
+ return faces;
+}
+
+BMLoop **ED_uvedit_selected_edges(Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
+{
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ CLAMP_MAX(len_max, bm->totloop);
+ int edges_len = 0;
+ BMLoop **edges = MEM_mallocN(sizeof(*edges) * len_max, __func__);
+
+ BMIter iter;
+ BMFace *f;
+
+ /* Clear tag. */
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
+ }
+ }
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (uvedit_face_visible_test(scene, f)) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
+ const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset);
+ if ((luv_curr->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) {
+ BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
+
+ edges[edges_len++] = l_iter;
+ if (edges_len == len_max) {
+ goto finally;
+ }
+
+ /* Tag other connected loops so we don't consider them separate edges. */
+ if (l_iter != l_iter->radial_next) {
+ BMLoop *l_radial_iter = l_iter->radial_next;
+ do {
+ if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
+ }
+ }
+ }
+ }
+ }
+ }
+
+finally:
+ *r_edges_len = edges_len;
+ if (edges_len != len_max) {
+ edges = MEM_reallocN(edges, sizeof(*edges) * edges_len);
+ }
+ return edges;
+}
+
+BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
+{
+ const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+ CLAMP_MAX(len_max, bm->totloop);
+ int verts_len = 0;
+ BMLoop **verts = MEM_mallocN(sizeof(*verts) * len_max, __func__);
+
+ BMIter iter;
+ BMFace *f;
+
+ /* Clear tag. */
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
+ }
+ }
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (uvedit_face_visible_test(scene, f)) {
+ BMIter liter;
+ BMLoop *l_iter;
+ BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
+ if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
+ if ((luv->flag & MLOOPUV_VERTSEL)) {
+ BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
+
+ verts[verts_len++] = l_iter;
+ if (verts_len == len_max) {
+ goto finally;
+ }
+
+ /* Tag other connected loops so we don't consider them separate vertices. */
+ BMIter liter_disk;
+ BMLoop *l_disk_iter;
+ BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
+ if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+finally:
+ *r_verts_len = verts_len;
+ if (verts_len != len_max) {
+ verts = MEM_reallocN(verts, sizeof(*verts) * verts_len);
+ }
+ return verts;
+}
+
+/** \} */
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index 38bd928e7b1..29e10f03e3c 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -534,7 +534,7 @@ static void stitch_island_calculate_edge_rotation(UvEdge *edge,
StitchStateContainer *ssc,
StitchState *state,
UVVertAverage *uv_average,
- uint *uvfinal_map,
+ const uint *uvfinal_map,
IslandStitchData *island_stitch_data)
{
BMesh *bm = state->em->bm;
@@ -1935,7 +1935,7 @@ static StitchState *stitch_init(bContext *C,
return NULL;
}
- ED_uvedit_get_aspect(scene, obedit, em->bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(obedit, &aspx, &aspy);
state->aspect = aspx / aspy;
/* Count 'unique' uvs */
diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
index aff73308fb5..6fcfb0e0bfc 100644
--- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c
+++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c
@@ -210,15 +210,16 @@ static bool uvedit_have_selection_multi(const Scene *scene,
return have_select;
}
-void ED_uvedit_get_aspect(
- const Scene *UNUSED(scene), Object *ob, BMesh *bm, float *r_aspx, float *r_aspy)
+void ED_uvedit_get_aspect(Object *ob, float *r_aspx, float *r_aspy)
{
+ BMEditMesh *em = BKE_editmesh_from_object(ob);
+ BLI_assert(em != NULL);
bool sloppy = true;
bool selected = false;
BMFace *efa;
Image *ima;
- efa = BM_mesh_active_face_get(bm, sloppy, selected);
+ efa = BM_mesh_active_face_get(em->bm, sloppy, selected);
if (efa) {
ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
@@ -285,7 +286,7 @@ static ParamHandle *construct_param_handle(const Scene *scene,
if (options->correct_aspect) {
float aspx, aspy;
- ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx != aspy) {
param_aspect_ratio(handle, aspx, aspy);
@@ -354,11 +355,9 @@ static ParamHandle *construct_param_handle_multi(const Scene *scene,
if (options->correct_aspect) {
Object *ob = objects[0];
- BMEditMesh *em = BKE_editmesh_from_object(ob);
- BMesh *bm = em->bm;
float aspx, aspy;
- ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx != aspy) {
param_aspect_ratio(handle, aspx, aspy);
}
@@ -500,7 +499,7 @@ static ParamHandle *construct_param_handle_subsurfed(const Scene *scene,
if (options->correct_aspect) {
float aspx, aspy;
- ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx != aspy) {
param_aspect_ratio(handle, aspx, aspy);
@@ -1413,7 +1412,7 @@ static void uv_transform_properties(wmOperatorType *ot, int radius)
}
}
-static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em)
+static void correct_uv_aspect(Object *ob, BMEditMesh *em)
{
BMLoop *l;
BMIter iter, liter;
@@ -1423,7 +1422,7 @@ static void correct_uv_aspect(const Scene *scene, Object *ob, BMEditMesh *em)
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- ED_uvedit_get_aspect(scene, ob, em->bm, &aspx, &aspy);
+ ED_uvedit_get_aspect(ob, &aspx, &aspy);
if (aspx == aspy) {
return;
@@ -1491,10 +1490,7 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
"Scale UV coordinates to bounds after unwrapping");
}
-static void uv_map_clip_correct_multi(const Scene *scene,
- Object **objects,
- uint objects_len,
- wmOperator *op)
+static void uv_map_clip_correct_multi(Object **objects, uint objects_len, wmOperator *op)
{
BMFace *efa;
BMLoop *l;
@@ -1515,7 +1511,7 @@ static void uv_map_clip_correct_multi(const Scene *scene,
/* correct for image aspect ratio */
if (correct_aspect) {
- correct_uv_aspect(scene, ob, em);
+ correct_uv_aspect(ob, em);
}
if (scale_to_bounds) {
@@ -1580,9 +1576,9 @@ static void uv_map_clip_correct_multi(const Scene *scene,
}
}
-static void uv_map_clip_correct(const Scene *scene, Object *ob, wmOperator *op)
+static void uv_map_clip_correct(Object *ob, wmOperator *op)
{
- uv_map_clip_correct_multi(scene, &ob, 1, op);
+ uv_map_clip_correct_multi(&ob, 1, op);
}
/** \} */
@@ -1973,7 +1969,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
}
if (changed_multi) {
- uv_map_clip_correct_multi(scene, objects, objects_len, op);
+ uv_map_clip_correct_multi(objects, objects_len, op);
}
MEM_freeN(objects);
@@ -2174,7 +2170,7 @@ static int sphere_project_exec(bContext *C, wmOperator *op)
uv_map_mirror(em, efa);
}
- uv_map_clip_correct(scene, obedit, op);
+ uv_map_clip_correct(obedit, op);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
@@ -2272,7 +2268,7 @@ static int cylinder_project_exec(bContext *C, wmOperator *op)
uv_map_mirror(em, efa);
}
- uv_map_clip_correct(scene, obedit, op);
+ uv_map_clip_correct(obedit, op);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
@@ -2395,7 +2391,7 @@ static int cube_project_exec(bContext *C, wmOperator *op)
uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
- uv_map_clip_correct(scene, obedit, op);
+ uv_map_clip_correct(obedit, op);
DEG_id_tag_update(obedit->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);