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:
authorDalai Felinto <dfelinto@gmail.com>2016-10-20 20:26:16 +0300
committerDalai Felinto <dfelinto@gmail.com>2016-10-20 20:26:16 +0300
commitd0989e538effc1c8b5e948224beaad25ab4bd569 (patch)
treea4ee58fea6f190943cbb79d1985be66a779ae847 /source/blender/editors
parent418b24551e1b8a4458deceedca945a631f0684ac (diff)
parent7830ec54186e8b05a366775e02c6457eb83814a3 (diff)
Merge commit '7830ec54186e8b05a366775e02c6457eb83814a3' into pbr-merge
Conflicts: source/blender/nodes/shader/nodes/node_shader_texture.c
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/anim_channels_defines.c7
-rw-r--r--source/blender/editors/animation/anim_channels_edit.c12
-rw-r--r--source/blender/editors/animation/anim_filter.c198
-rw-r--r--source/blender/editors/animation/keyframes_draw.c171
-rw-r--r--source/blender/editors/animation/keyframes_edit.c10
-rw-r--r--source/blender/editors/animation/keyframing.c13
-rw-r--r--source/blender/editors/armature/armature_utils.c12
-rw-r--r--source/blender/editors/armature/pose_edit.c2
-rw-r--r--source/blender/editors/armature/pose_lib.c4
-rw-r--r--source/blender/editors/curve/editcurve.c9
-rw-r--r--source/blender/editors/curve/editcurve_paint.c97
-rw-r--r--source/blender/editors/curve/editcurve_select.c19
-rw-r--r--source/blender/editors/curve/editfont.c2
-rw-r--r--source/blender/editors/curve/editfont_undo.c2
-rw-r--r--source/blender/editors/gpencil/drawgpencil.c561
-rw-r--r--source/blender/editors/gpencil/gpencil_brush.c399
-rw-r--r--source/blender/editors/gpencil/gpencil_convert.c45
-rw-r--r--source/blender/editors/gpencil/gpencil_data.c1799
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c327
-rw-r--r--source/blender/editors/gpencil/gpencil_intern.h120
-rw-r--r--source/blender/editors/gpencil/gpencil_ops.c74
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c434
-rw-r--r--source/blender/editors/gpencil/gpencil_select.c119
-rw-r--r--source/blender/editors/gpencil/gpencil_undo.c8
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c373
-rw-r--r--source/blender/editors/include/ED_anim_api.h14
-rw-r--r--source/blender/editors/include/ED_gpencil.h12
-rw-r--r--source/blender/editors/include/ED_keyframes_draw.h26
-rw-r--r--source/blender/editors/include/ED_keyframing.h8
-rw-r--r--source/blender/editors/include/ED_view3d.h2
-rw-r--r--source/blender/editors/include/UI_icons.h5
-rw-r--r--source/blender/editors/include/UI_interface.h6
-rw-r--r--source/blender/editors/include/UI_resources.h2
-rw-r--r--source/blender/editors/interface/interface.c2
-rw-r--r--source/blender/editors/interface/interface_draw.c54
-rw-r--r--source/blender/editors/interface/interface_eyedropper.c2
-rw-r--r--source/blender/editors/interface/interface_handlers.c183
-rw-r--r--source/blender/editors/interface/interface_icons.c37
-rw-r--r--source/blender/editors/interface/interface_intern.h2
-rw-r--r--source/blender/editors/interface/interface_ops.c2
-rw-r--r--source/blender/editors/interface/interface_regions.c39
-rw-r--r--source/blender/editors/interface/interface_templates.c27
-rw-r--r--source/blender/editors/interface/interface_widgets.c2
-rw-r--r--source/blender/editors/interface/resources.c34
-rw-r--r--source/blender/editors/io/io_collada.c2
-rw-r--r--source/blender/editors/mesh/editmesh_extrude.c3
-rw-r--r--source/blender/editors/mesh/editmesh_intersect.c2
-rw-r--r--source/blender/editors/mesh/editmesh_rip.c11
-rw-r--r--source/blender/editors/mesh/editmesh_tools.c195
-rw-r--r--source/blender/editors/mesh/editmesh_undo.c2
-rw-r--r--source/blender/editors/mesh/editmesh_utils.c10
-rw-r--r--source/blender/editors/mesh/mesh_data.c6
-rw-r--r--source/blender/editors/mesh/mesh_intern.h1
-rw-r--r--source/blender/editors/mesh/mesh_ops.c1
-rw-r--r--source/blender/editors/mesh/meshtools.c17
-rw-r--r--source/blender/editors/object/object_add.c78
-rw-r--r--source/blender/editors/object/object_constraint.c2
-rw-r--r--source/blender/editors/object/object_data_transfer.c6
-rw-r--r--source/blender/editors/object/object_edit.c12
-rw-r--r--source/blender/editors/object/object_group.c2
-rw-r--r--source/blender/editors/object/object_modifier.c4
-rw-r--r--source/blender/editors/object/object_ops.c18
-rw-r--r--source/blender/editors/object/object_relations.c99
-rw-r--r--source/blender/editors/object/object_select.c1
-rw-r--r--source/blender/editors/object/object_shapekey.c9
-rw-r--r--source/blender/editors/object/object_transform.c134
-rw-r--r--source/blender/editors/object/object_vgroup.c29
-rw-r--r--source/blender/editors/physics/particle_edit.c2
-rw-r--r--source/blender/editors/physics/particle_object.c6
-rw-r--r--source/blender/editors/render/render_internal.c2
-rw-r--r--source/blender/editors/render/render_opengl.c97
-rw-r--r--source/blender/editors/render/render_preview.c6
-rw-r--r--source/blender/editors/render/render_shading.c14
-rw-r--r--source/blender/editors/screen/area.c7
-rw-r--r--source/blender/editors/screen/screen_context.c46
-rw-r--r--source/blender/editors/screen/screen_edit.c1
-rw-r--r--source/blender/editors/screen/screen_ops.c16
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c4
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c8
-rw-r--r--source/blender/editors/sculpt_paint/paint_ops.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_stroke.c29
-rw-r--r--source/blender/editors/sculpt_paint/paint_vertex.c18
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c33
-rw-r--r--source/blender/editors/sound/sound_ops.c2
-rw-r--r--source/blender/editors/space_action/action_buttons.c2
-rw-r--r--source/blender/editors/space_action/action_data.c2
-rw-r--r--source/blender/editors/space_action/action_draw.c64
-rw-r--r--source/blender/editors/space_action/action_edit.c8
-rw-r--r--source/blender/editors/space_action/action_select.c24
-rw-r--r--source/blender/editors/space_clip/clip_editor.c2
-rw-r--r--source/blender/editors/space_clip/clip_ops.c2
-rw-r--r--source/blender/editors/space_clip/clip_toolbar.c2
-rw-r--r--source/blender/editors/space_clip/space_clip.c2
-rw-r--r--source/blender/editors/space_file/filesel.c2
-rw-r--r--source/blender/editors/space_graph/graph_buttons.c7
-rw-r--r--source/blender/editors/space_graph/graph_draw.c20
-rw-r--r--source/blender/editors/space_graph/space_graph.c236
-rw-r--r--source/blender/editors/space_image/image_buttons.c73
-rw-r--r--source/blender/editors/space_image/image_ops.c175
-rw-r--r--source/blender/editors/space_logic/logic_buttons.c2
-rw-r--r--source/blender/editors/space_logic/logic_ops.c6
-rw-r--r--source/blender/editors/space_nla/nla_buttons.c2
-rw-r--r--source/blender/editors/space_nla/nla_draw.c62
-rw-r--r--source/blender/editors/space_nla/nla_edit.c6
-rw-r--r--source/blender/editors/space_node/drawnode.c2
-rw-r--r--source/blender/editors/space_node/node_buttons.c2
-rw-r--r--source/blender/editors/space_node/node_group.c47
-rw-r--r--source/blender/editors/space_node/node_relationships.c2
-rw-r--r--source/blender/editors/space_outliner/outliner_draw.c212
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c14
-rw-r--r--source/blender/editors/space_outliner/outliner_tools.c25
-rw-r--r--source/blender/editors/space_outliner/outliner_tree.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c5
-rw-r--r--source/blender/editors/space_sequencer/sequencer_buttons.c2
-rw-r--r--source/blender/editors/space_text/text_header.c2
-rw-r--r--source/blender/editors/space_text/text_ops.c6
-rw-r--r--source/blender/editors/space_time/space_time.c4
-rw-r--r--source/blender/editors/space_view3d/drawmesh.c44
-rw-r--r--source/blender/editors/space_view3d/drawobject.c6
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c18
-rw-r--r--source/blender/editors/space_view3d/view3d_fly.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_intern.h1
-rw-r--r--source/blender/editors/space_view3d/view3d_ops.c1
-rw-r--r--source/blender/editors/space_view3d/view3d_project.c55
-rw-r--r--source/blender/editors/space_view3d/view3d_ruler.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_snap.c68
-rw-r--r--source/blender/editors/space_view3d/view3d_view.c4
-rw-r--r--source/blender/editors/space_view3d/view3d_walk.c5
-rw-r--r--source/blender/editors/transform/transform.c180
-rw-r--r--source/blender/editors/transform/transform.h1
-rw-r--r--source/blender/editors/transform/transform_constraints.c9
-rw-r--r--source/blender/editors/transform/transform_conversions.c90
-rw-r--r--source/blender/editors/transform/transform_generics.c6
-rw-r--r--source/blender/editors/transform/transform_manipulator.c61
-rw-r--r--source/blender/editors/transform/transform_snap.c9
-rw-r--r--source/blender/editors/transform/transform_snap_object.c1734
-rw-r--r--source/blender/editors/util/ed_util.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c6
143 files changed, 7086 insertions, 2466 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c
index ea2f7fc5588..752544f65e1 100644
--- a/source/blender/editors/animation/anim_channels_defines.c
+++ b/source/blender/editors/animation/anim_channels_defines.c
@@ -4208,6 +4208,8 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
offset += ICON_WIDTH;
}
else if (ale->type == ANIMTYPE_GPLAYER) {
+#if 0
+ /* XXX: Maybe need a better design */
/* color swatch for layer color */
bGPDlayer *gpl = (bGPDlayer *)ale->data;
PointerRNA ptr;
@@ -4216,7 +4218,6 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
RNA_pointer_create(ale->id, &RNA_GPencilLayer, ale->data, &ptr);
UI_block_align_begin(block);
-
UI_block_emboss_set(block, RNA_boolean_get(&ptr, "is_stroke_visible") ? UI_EMBOSS : UI_EMBOSS_NONE);
uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset, yminc, w, ICON_WIDTH,
&ptr, "color", -1,
@@ -4226,11 +4227,11 @@ void ANIM_channel_draw_widgets(const bContext *C, bAnimContext *ac, bAnimListEle
uiDefButR(block, UI_BTYPE_COLOR, 1, "", offset + w, yminc, w, ICON_WIDTH,
&ptr, "fill_color", -1,
0, 0, 0, 0, gpl->info);
-
UI_block_emboss_set(block, UI_EMBOSS_NONE);
UI_block_align_end(block);
-
+
offset += ICON_WIDTH;
+#endif
}
}
diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c
index c98470fb194..af9b0a176f5 100644
--- a/source/blender/editors/animation/anim_channels_edit.c
+++ b/source/blender/editors/animation/anim_channels_edit.c
@@ -2356,7 +2356,7 @@ static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selec
}
else {
ymin = 0.0f;
- ymax = (float)(-ACHANNEL_HEIGHT);
+ ymax = (float)(-ACHANNEL_HEIGHT(ac));
}
/* convert border-region to view coordinates */
@@ -2372,7 +2372,7 @@ static void borderselect_anim_channels(bAnimContext *ac, rcti *rect, short selec
if (ac->datatype == ANIMCONT_NLA)
ymin = ymax - NLACHANNEL_STEP(snla);
else
- ymin = ymax - ACHANNEL_STEP;
+ ymin = ymax - ACHANNEL_STEP(ac);
/* if channel is within border-select region, alter it */
if (!((ymax < rectf.ymin) || (ymin > rectf.ymax))) {
@@ -2573,7 +2573,7 @@ static int animchannels_channel_get(bAnimContext *ac, const int mval[2])
UI_view2d_listview_view_to_cell(v2d, NLACHANNEL_NAMEWIDTH, NLACHANNEL_STEP(snla), 0, (float)NLACHANNEL_HEIGHT_HALF(snla), x, y, NULL, &channel_index);
}
else {
- UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
+ UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index);
}
return channel_index;
@@ -2705,6 +2705,10 @@ static int mouse_anim_channels(bContext *C, bAnimContext *ac, int channel_index,
if ((adt) && (adt->flag & ADT_UI_SELECTED))
adt->flag |= ADT_UI_ACTIVE;
+ /* ensure we exit editmode on whatever object was active before to avoid getting stuck there - T48747 */
+ if (ob != sce->obedit)
+ ED_object_editmode_exit(C, EM_FREEDATA | EM_FREEUNDO | EM_WAITCURSOR | EM_DO_UNDO);
+
notifierFlags |= (ND_ANIMCHAN | NA_SELECTED);
break;
}
@@ -2988,7 +2992,7 @@ static int animchannels_mouseclick_invoke(bContext *C, wmOperator *op, const wmE
* ACHANNEL_HEIGHT_HALF.
*/
UI_view2d_region_to_view(v2d, event->mval[0], event->mval[1], &x, &y);
- UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
+ UI_view2d_listview_view_to_cell(v2d, ACHANNEL_NAMEWIDTH, ACHANNEL_STEP(&ac), 0, (float)ACHANNEL_HEIGHT_HALF(&ac), x, y, NULL, &channel_index);
/* handle mouse-click in the relevant channel then */
notifierFlags = mouse_anim_channels(C, &ac, channel_index, selectmode);
diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c
index 910e195173c..88d96c531e0 100644
--- a/source/blender/editors/animation/anim_filter.c
+++ b/source/blender/editors/animation/anim_filter.c
@@ -71,6 +71,7 @@
#include "DNA_world_types.h"
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
+#include "DNA_userdef_types.h"
#include "MEM_guardedalloc.h"
@@ -97,9 +98,30 @@
#include "ED_anim_api.h"
#include "ED_markers.h"
+#include "UI_resources.h" /* for TH_KEYFRAME_SCALE lookup */
+
/* ************************************************************ */
/* Blender Context <-> Animation Context mapping */
+/* ----------- Private Stuff - General -------------------- */
+
+/* Get vertical scaling factor (i.e. typically used for keyframe size) */
+static void animedit_get_yscale_factor(bAnimContext *ac)
+{
+ bTheme *btheme = UI_GetTheme();
+
+ /* grab scale factor directly from action editor setting
+ * NOTE: This theme setting doesn't have an ID, as it cannot be accessed normally
+ * since it is a float, and the theem settings methods can only handle chars.
+ */
+ ac->yscale_fac = btheme->tact.keyframe_scale_fac;
+
+ /* clamp to avoid problems with uninitialised values... */
+ if (ac->yscale_fac < 0.1f)
+ ac->yscale_fac = 1.0f;
+ //printf("yscale_fac = %f\n", ac->yscale_fac);
+}
+
/* ----------- Private Stuff - Action Editor ------------- */
/* Get shapekey data being edited (for Action Editor -> ShapeKey mode) */
@@ -352,6 +374,9 @@ bool ANIM_animdata_get_context(const bContext *C, bAnimContext *ac)
ac->spacetype = (sa) ? sa->spacetype : 0;
ac->regiontype = (ar) ? ar->regiontype : 0;
+ /* initialise default y-scale factor */
+ animedit_get_yscale_factor(ac);
+
/* get data context info */
// XXX: if the below fails, try to grab this info from context instead... (to allow for scripting)
return ANIM_animdata_context_getdata(ac);
@@ -1255,7 +1280,7 @@ static size_t animfilter_action(bAnimContext *ac, ListBase *anim_data, bDopeShee
/* don't include anything from this action if it is linked in from another file,
* and we're getting stuff for editing...
*/
- if ((filter_mode & ANIMFILTER_FOREDIT) && (act->id.lib))
+ if ((filter_mode & ANIMFILTER_FOREDIT) && ID_IS_LINKED_DATABLOCK(act))
return 0;
/* do groups */
@@ -2705,11 +2730,97 @@ static size_t animdata_filter_dopesheet_scene(bAnimContext *ac, ListBase *anim_d
return items;
}
+
+/* Helper for animdata_filter_dopesheet() - For checking if an object should be included or not */
+static bool animdata_filter_base_is_ok(bDopeSheet *ads, Scene *scene, Base *base, int filter_mode)
+{
+ Object *ob = base->object;
+
+ if (base->object == NULL)
+ return false;
+
+ /* firstly, check if object can be included, by the following factors:
+ * - if only visible, must check for layer and also viewport visibility
+ * --> while tools may demand only visible, user setting takes priority
+ * as user option controls whether sets of channels get included while
+ * tool-flag takes into account collapsed/open channels too
+ * - if only selected, must check if object is selected
+ * - there must be animation data to edit (this is done recursively as we
+ * try to add the channels)
+ */
+ if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
+ /* layer visibility - we check both object and base, since these may not be in sync yet */
+ if ((scene->lay & (ob->lay | base->lay)) == 0)
+ return false;
+
+ /* outliner restrict-flag */
+ if (ob->restrictflag & OB_RESTRICT_VIEW)
+ return false;
+ }
+
+ /* if only F-Curves with visible flags set can be shown, check that
+ * datablock hasn't been set to invisible
+ */
+ if (filter_mode & ANIMFILTER_CURVE_VISIBLE) {
+ if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE))
+ return false;
+ }
+
+ /* check selection and object type filters */
+ if ((ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == sce->basact)*/)) {
+ /* only selected should be shown */
+ return false;
+ }
+
+ /* check if object belongs to the filtering group if option to filter
+ * objects by the grouped status is on
+ * - used to ease the process of doing multiple-character choreographies
+ */
+ if (ads->filterflag & ADS_FILTER_ONLYOBGROUP) {
+ if (BKE_group_object_exists(ads->filter_grp, ob) == 0)
+ return false;
+ }
+
+ /* no reason to exclude this object... */
+ return true;
+}
+
+/* Helper for animdata_filter_ds_sorted_bases() - Comparison callback for two Base pointers... */
+static int ds_base_sorting_cmp(const void *base1_ptr, const void *base2_ptr)
+{
+ const Base *b1 = *((const Base **)base1_ptr);
+ const Base *b2 = *((const Base **)base2_ptr);
+
+ return strcmp(b1->object->id.name + 2, b2->object->id.name + 2);
+}
+
+/* Get a sorted list of all the bases - for inclusion in dopesheet (when drawing channels) */
+static Base **animdata_filter_ds_sorted_bases(bDopeSheet *ads, Scene *scene, int filter_mode, size_t *r_usable_bases)
+{
+ /* Create an array with space for all the bases, but only containing the usable ones */
+ size_t tot_bases = BLI_listbase_count(&scene->base);
+ size_t num_bases = 0;
+
+ Base **sorted_bases = MEM_mallocN(sizeof(Base *) * tot_bases, "Dopesheet Usable Sorted Bases");
+ for (Base *base = scene->base.first; base; base = base->next) {
+ if (animdata_filter_base_is_ok(ads, scene, base, filter_mode)) {
+ sorted_bases[num_bases++] = base;
+ }
+ }
+
+ /* Sort this list of pointers (based on the names) */
+ qsort(sorted_bases, num_bases, sizeof(Base *), ds_base_sorting_cmp);
+
+ /* Return list of sorted bases */
+ *r_usable_bases = num_bases;
+ return sorted_bases;
+}
+
+
// TODO: implement pinning... (if and when pinning is done, what we need to do is to provide freeing mechanisms - to protect against data that was deleted)
static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, bDopeSheet *ads, int filter_mode)
{
- Scene *sce = (Scene *)ads->source;
- Base *base;
+ Scene *scene = (Scene *)ads->source;
size_t items = 0;
/* check that we do indeed have a scene */
@@ -2729,56 +2840,45 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, ListBase *anim_data, b
}
/* scene-linked animation - e.g. world, compositing nodes, scene anim (including sequencer currently) */
- items += animdata_filter_dopesheet_scene(ac, anim_data, ads, sce, filter_mode);
-
- /* loop over all bases (i.e.objects) in the scene */
- for (base = sce->base.first; base; base = base->next) {
- /* check if there's an object (all the relevant checks are done in the ob-function) */
- if (base->object) {
- Object *ob = base->object;
-
- /* firstly, check if object can be included, by the following factors:
- * - if only visible, must check for layer and also viewport visibility
- * --> while tools may demand only visible, user setting takes priority
- * as user option controls whether sets of channels get included while
- * tool-flag takes into account collapsed/open channels too
- * - if only selected, must check if object is selected
- * - there must be animation data to edit (this is done recursively as we
- * try to add the channels)
- */
- if ((filter_mode & ANIMFILTER_DATA_VISIBLE) && !(ads->filterflag & ADS_FILTER_INCL_HIDDEN)) {
- /* layer visibility - we check both object and base, since these may not be in sync yet */
- if ((sce->lay & (ob->lay | base->lay)) == 0) continue;
-
- /* outliner restrict-flag */
- if (ob->restrictflag & OB_RESTRICT_VIEW) continue;
- }
-
- /* if only F-Curves with visible flags set can be shown, check that
- * datablock hasn't been set to invisible
- */
- if (filter_mode & ANIMFILTER_CURVE_VISIBLE) {
- if ((ob->adt) && (ob->adt->flag & ADT_CURVES_NOT_VISIBLE))
- continue;
+ items += animdata_filter_dopesheet_scene(ac, anim_data, ads, scene, filter_mode);
+
+ /* If filtering for channel drawing, we want the objects in alphabetical order,
+ * to make it easier to predict where items are in the hierarchy
+ * - This order only really matters if we need to show all channels in the list (e.g. for drawing)
+ * (XXX: What about lingering "active" flags? The order may now become unpredictable)
+ * - Don't do this if this behaviour has been turned off (i.e. due to it being too slow)
+ * - Don't do this if there's just a single object
+ */
+ if ((filter_mode & ANIMFILTER_LIST_CHANNELS) && !(ads->flag & ADS_FLAG_NO_DB_SORT) &&
+ (scene->base.first != scene->base.last))
+ {
+ /* Filter list of bases (i.e. objects), sort them, then add their contents normally... */
+ // TODO: Cache the old sorted order - if the set of bases hasn't changed, don't re-sort...
+ Base **sorted_bases;
+ size_t num_bases;
+
+ sorted_bases = animdata_filter_ds_sorted_bases(ads, scene, filter_mode, &num_bases);
+ if (sorted_bases) {
+ /* Add the necessary channels for these bases... */
+ for (size_t i = 0; i < num_bases; i++) {
+ items += animdata_filter_dopesheet_ob(ac, anim_data, ads, sorted_bases[i], filter_mode);
}
- /* check selection and object type filters */
- if ( (ads->filterflag & ADS_FILTER_ONLYSEL) && !((base->flag & SELECT) /*|| (base == sce->basact)*/) ) {
- /* only selected should be shown */
- continue;
- }
+ // TODO: store something to validate whether any changes are needed?
- /* check if object belongs to the filtering group if option to filter
- * objects by the grouped status is on
- * - used to ease the process of doing multiple-character choreographies
- */
- if (ads->filterflag & ADS_FILTER_ONLYOBGROUP) {
- if (BKE_group_object_exists(ads->filter_grp, ob) == 0)
- continue;
+ /* free temporary data */
+ MEM_freeN(sorted_bases);
+ }
+ }
+ else {
+ /* Filter and add contents of each base (i.e. object) without them sorting first
+ * NOTE: This saves performance in cases where order doesn't matter
+ */
+ for (Base *base = scene->base.first; base; base = base->next) {
+ if (animdata_filter_base_is_ok(ads, scene, base, filter_mode)) {
+ /* since we're still here, this object should be usable */
+ items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode);
}
-
- /* since we're still here, this object should be usable */
- items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode);
}
}
diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c
index 6f1883cff55..6e776953356 100644
--- a/source/blender/editors/animation/keyframes_draw.c
+++ b/source/blender/editors/animation/keyframes_draw.c
@@ -40,6 +40,7 @@
#include "MEM_guardedalloc.h"
#include "BLI_dlrbTree.h"
+#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "DNA_anim_types.h"
@@ -282,6 +283,9 @@ static ActKeyBlock *bezts_to_new_actkeyblock(BezTriple *prev, BezTriple *beztn)
ab->sel = (BEZT_ISSEL_ANY(prev) || BEZT_ISSEL_ANY(beztn)) ? SELECT : 0;
ab->modified = 1;
+ if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD)
+ ab->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD;
+
return ab;
}
@@ -305,16 +309,28 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, BezTriple *first_bezt
}
- /* check if block needed - same value(s)?
- * -> firstly, handles must have same central value as each other
- * -> secondly, handles which control that section of the curve must be constant
- */
+ /* check if block needed */
if (prev == NULL) return;
- if (IS_EQF(beztn->vec[1][1], prev->vec[1][1]) == 0) return;
-
- if (IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) == 0) return;
- if (IS_EQF(prev->vec[1][1], prev->vec[2][1]) == 0) return;
+ if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD) {
+ /* Animator tagged a "moving hold"
+ * - Previous key must also be tagged as a moving hold, otherwise
+ * we're just dealing with the first of a pair, and we don't
+ * want to be creating any phantom holds...
+ */
+ if (BEZKEYTYPE(prev) != BEZT_KEYTYPE_MOVEHOLD)
+ return;
+ }
+ else {
+ /* Check for same values...
+ * - Handles must have same central value as each other
+ * - Handles which control that section of the curve must be constant
+ */
+ if (IS_EQF(beztn->vec[1][1], prev->vec[1][1]) == 0) return;
+
+ if (IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) == 0) return;
+ if (IS_EQF(prev->vec[1][1], prev->vec[2][1]) == 0) return;
+ }
/* if there are no blocks already, just add as root */
if (blocks->root == NULL) {
@@ -340,7 +356,13 @@ static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, BezTriple *first_bezt
*/
if (IS_EQT(ab->start, prev->vec[1][0], BEZT_BINARYSEARCH_THRESH)) {
/* set selection status and 'touched' status */
- if (BEZT_ISSEL_ANY(beztn)) ab->sel = SELECT;
+ if (BEZT_ISSEL_ANY(beztn))
+ ab->sel = SELECT;
+
+ /* XXX: only when the first one was a moving hold? */
+ if (BEZKEYTYPE(beztn) == BEZT_KEYTYPE_MOVEHOLD)
+ ab->flag |= ACTKEYBLOCK_FLAG_MOVING_HOLD;
+
ab->modified++;
/* done... no need to insert */
@@ -485,7 +507,27 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel,
/* tweak size of keyframe shape according to type of keyframe
* - 'proper' keyframes have key_type = 0, so get drawn at full size
*/
- hsize -= 0.5f * key_type;
+ switch (key_type) {
+ case BEZT_KEYTYPE_KEYFRAME: /* must be full size */
+ break;
+
+ case BEZT_KEYTYPE_BREAKDOWN: /* slightly smaller than normal keyframe */
+ hsize *= 0.85f;
+ break;
+
+ case BEZT_KEYTYPE_MOVEHOLD: /* slightly smaller than normal keyframes (but by less than for breakdowns) */
+ //hsize *= 0.72f;
+ hsize *= 0.95f;
+ break;
+
+ case BEZT_KEYTYPE_EXTREME: /* slightly larger */
+ hsize *= 1.2f;
+ break;
+
+ default:
+ hsize -= 0.5f * key_type;
+ break;
+ }
/* adjust view transform before starting */
glTranslatef(x, y, 0.0f);
@@ -518,6 +560,15 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel,
else UI_GetThemeColor4fv(TH_KEYTYPE_JITTER, inner_col);
break;
}
+ case BEZT_KEYTYPE_MOVEHOLD: /* similar to traditional keyframes, but different... */
+ {
+ /* XXX: Should these get their own theme options instead? */
+ if (sel) UI_GetThemeColorShade4fv(TH_STRIP_SELECT, 35, inner_col);
+ else UI_GetThemeColorShade4fv(TH_STRIP, 50, inner_col);
+
+ inner_col[3] = 1.0f; /* full opacity, to avoid problems with visual glitches */
+ break;
+ }
case BEZT_KEYTYPE_KEYFRAME: /* traditional yellowish frames (default theme) */
default:
{
@@ -557,13 +608,16 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel,
glTranslatef(-x, -y, 0.0f);
}
-static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, float ypos, short channelLocked)
+static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, float ypos, float yscale_fac, bool channelLocked)
{
ActKeyColumn *ak;
ActKeyBlock *ab;
float alpha;
float xscale;
- float iconsize = U.widget_unit / 4.0f;
+
+ const float iconsize = (U.widget_unit / 4.0f) * yscale_fac;
+ const float mhsize = iconsize * 0.7f;
+
glEnable(GL_BLEND);
/* get View2D scaling factor */
@@ -576,6 +630,7 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, floa
/* draw keyblocks */
if (blocks) {
float sel_color[4], unsel_color[4];
+ float sel_mhcol[4], unsel_mhcol[4];
/* cache colours first */
UI_GetThemeColor4fv(TH_STRIP_SELECT, sel_color);
@@ -584,16 +639,32 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, floa
sel_color[3] *= alpha;
unsel_color[3] *= alpha;
+ copy_v4_v4(sel_mhcol, sel_color);
+ sel_mhcol[3] *= 0.8f;
+ copy_v4_v4(unsel_mhcol, unsel_color);
+ unsel_mhcol[3] *= 0.8f;
+
/* NOTE: the tradeoff for changing colors between each draw is dwarfed by the cost of checking validity */
for (ab = blocks->first; ab; ab = ab->next) {
if (actkeyblock_is_valid(ab, keys)) {
- /* draw block */
- if (ab->sel)
- glColor4fv(sel_color);
- else
- glColor4fv(unsel_color);
-
- glRectf(ab->start, ypos - iconsize, ab->end, ypos + iconsize);
+ if (ab->flag & ACTKEYBLOCK_FLAG_MOVING_HOLD) {
+ /* draw "moving hold" long-keyframe block - slightly smaller */
+ if (ab->sel)
+ glColor4fv(sel_mhcol);
+ else
+ glColor4fv(unsel_mhcol);
+
+ glRectf(ab->start, ypos - mhsize, ab->end, ypos + mhsize);
+ }
+ else {
+ /* draw standard long-keyframe block */
+ if (ab->sel)
+ glColor4fv(sel_color);
+ else
+ glColor4fv(unsel_color);
+
+ glRectf(ab->start, ypos - iconsize, ab->end, ypos + iconsize);
+ }
}
}
}
@@ -619,7 +690,7 @@ static void draw_keylist(View2D *v2d, DLRBT_Tree *keys, DLRBT_Tree *blocks, floa
/* *************************** Channel Drawing Funcs *************************** */
-void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos)
+void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos, float yscale_fac)
{
DLRBT_Tree keys, blocks;
@@ -631,13 +702,13 @@ void draw_summary_channel(View2D *v2d, bAnimContext *ac, float ypos)
BLI_dlrbTree_linkedlist_sync(&keys);
BLI_dlrbTree_linkedlist_sync(&blocks);
- draw_keylist(v2d, &keys, &blocks, ypos, 0);
+ draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, false);
BLI_dlrbTree_free(&keys);
BLI_dlrbTree_free(&blocks);
}
-void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos)
+void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos, float yscale_fac)
{
DLRBT_Tree keys, blocks;
@@ -649,13 +720,13 @@ void draw_scene_channel(View2D *v2d, bDopeSheet *ads, Scene *sce, float ypos)
BLI_dlrbTree_linkedlist_sync(&keys);
BLI_dlrbTree_linkedlist_sync(&blocks);
- draw_keylist(v2d, &keys, &blocks, ypos, 0);
+ draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, false);
BLI_dlrbTree_free(&keys);
BLI_dlrbTree_free(&blocks);
}
-void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos)
+void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos, float yscale_fac)
{
DLRBT_Tree keys, blocks;
@@ -667,19 +738,19 @@ void draw_object_channel(View2D *v2d, bDopeSheet *ads, Object *ob, float ypos)
BLI_dlrbTree_linkedlist_sync(&keys);
BLI_dlrbTree_linkedlist_sync(&blocks);
- draw_keylist(v2d, &keys, &blocks, ypos, 0);
+ draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, false);
BLI_dlrbTree_free(&keys);
BLI_dlrbTree_free(&blocks);
}
-void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos)
+void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos, float yscale_fac)
{
DLRBT_Tree keys, blocks;
- short locked = (fcu->flag & FCURVE_PROTECTED) ||
- ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) ||
- ((adt && adt->action) && (adt->action->id.lib));
+ bool locked = (fcu->flag & FCURVE_PROTECTED) ||
+ ((fcu->grp) && (fcu->grp->flag & AGRP_PROTECTED)) ||
+ ((adt && adt->action) && ID_IS_LINKED_DATABLOCK(adt->action));
BLI_dlrbTree_init(&keys);
BLI_dlrbTree_init(&blocks);
@@ -689,18 +760,18 @@ void draw_fcurve_channel(View2D *v2d, AnimData *adt, FCurve *fcu, float ypos)
BLI_dlrbTree_linkedlist_sync(&keys);
BLI_dlrbTree_linkedlist_sync(&blocks);
- draw_keylist(v2d, &keys, &blocks, ypos, locked);
+ draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, locked);
BLI_dlrbTree_free(&keys);
BLI_dlrbTree_free(&blocks);
}
-void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos)
+void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float ypos, float yscale_fac)
{
DLRBT_Tree keys, blocks;
- short locked = (agrp->flag & AGRP_PROTECTED) ||
- ((adt && adt->action) && (adt->action->id.lib));
+ bool locked = (agrp->flag & AGRP_PROTECTED) ||
+ ((adt && adt->action) && ID_IS_LINKED_DATABLOCK(adt->action));
BLI_dlrbTree_init(&keys);
BLI_dlrbTree_init(&blocks);
@@ -710,17 +781,17 @@ void draw_agroup_channel(View2D *v2d, AnimData *adt, bActionGroup *agrp, float y
BLI_dlrbTree_linkedlist_sync(&keys);
BLI_dlrbTree_linkedlist_sync(&blocks);
- draw_keylist(v2d, &keys, &blocks, ypos, locked);
+ draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, locked);
BLI_dlrbTree_free(&keys);
BLI_dlrbTree_free(&blocks);
}
-void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos)
+void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos, float yscale_fac)
{
DLRBT_Tree keys, blocks;
- short locked = (act && act->id.lib != NULL);
+ bool locked = (act && ID_IS_LINKED_DATABLOCK(act));
BLI_dlrbTree_init(&keys);
BLI_dlrbTree_init(&blocks);
@@ -730,13 +801,13 @@ void draw_action_channel(View2D *v2d, AnimData *adt, bAction *act, float ypos)
BLI_dlrbTree_linkedlist_sync(&keys);
BLI_dlrbTree_linkedlist_sync(&blocks);
- draw_keylist(v2d, &keys, &blocks, ypos, locked);
+ draw_keylist(v2d, &keys, &blocks, ypos, yscale_fac, locked);
BLI_dlrbTree_free(&keys);
BLI_dlrbTree_free(&blocks);
}
-void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos)
+void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos, float yscale_fac)
{
DLRBT_Tree keys;
@@ -746,38 +817,42 @@ void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos
BLI_dlrbTree_linkedlist_sync(&keys);
- draw_keylist(v2d, &keys, NULL, ypos, 0);
+ draw_keylist(v2d, &keys, NULL, ypos, yscale_fac, false);
BLI_dlrbTree_free(&keys);
}
-void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos)
+void draw_gpl_channel(View2D *v2d, bDopeSheet *ads, bGPDlayer *gpl, float ypos, float yscale_fac)
{
DLRBT_Tree keys;
+ bool locked = (gpl->flag & GP_LAYER_LOCKED) != 0;
+
BLI_dlrbTree_init(&keys);
gpl_to_keylist(ads, gpl, &keys);
BLI_dlrbTree_linkedlist_sync(&keys);
- draw_keylist(v2d, &keys, NULL, ypos, (gpl->flag & GP_LAYER_LOCKED));
+ draw_keylist(v2d, &keys, NULL, ypos, yscale_fac, locked);
BLI_dlrbTree_free(&keys);
}
-void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, float ypos)
+void draw_masklay_channel(View2D *v2d, bDopeSheet *ads, MaskLayer *masklay, float ypos, float yscale_fac)
{
DLRBT_Tree keys;
-
+
+ bool locked = (masklay->flag & MASK_LAYERFLAG_LOCKED) != 0;
+
BLI_dlrbTree_init(&keys);
-
+
mask_to_keylist(ads, masklay, &keys);
-
+
BLI_dlrbTree_linkedlist_sync(&keys);
-
- draw_keylist(v2d, &keys, NULL, ypos, (masklay->flag & MASK_LAYERFLAG_LOCKED));
-
+
+ draw_keylist(v2d, &keys, NULL, ypos, yscale_fac, locked);
+
BLI_dlrbTree_free(&keys);
}
diff --git a/source/blender/editors/animation/keyframes_edit.c b/source/blender/editors/animation/keyframes_edit.c
index 7b35a154fc8..4571df0f077 100644
--- a/source/blender/editors/animation/keyframes_edit.c
+++ b/source/blender/editors/animation/keyframes_edit.c
@@ -1208,6 +1208,13 @@ static short set_keytype_jitter(KeyframeEditData *UNUSED(ked), BezTriple *bezt)
return 0;
}
+static short set_keytype_moving_hold(KeyframeEditData *UNUSED(ked), BezTriple *bezt)
+{
+ if (bezt->f2 & SELECT)
+ BEZKEYTYPE(bezt) = BEZT_KEYTYPE_MOVEHOLD;
+ return 0;
+}
+
/* Set the interpolation type of the selected BezTriples in each F-Curve to the specified one */
KeyframeEditFunc ANIM_editkeyframes_keytype(short code)
{
@@ -1221,6 +1228,9 @@ KeyframeEditFunc ANIM_editkeyframes_keytype(short code)
case BEZT_KEYTYPE_JITTER: /* jitter keyframe */
return set_keytype_jitter;
+ case BEZT_KEYTYPE_MOVEHOLD: /* moving hold */
+ return set_keytype_moving_hold;
+
case BEZT_KEYTYPE_KEYFRAME: /* proper keyframe */
default:
return set_keytype_keyframe;
diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c
index 0c0f54f0179..98be77b491f 100644
--- a/source/blender/editors/animation/keyframing.c
+++ b/source/blender/editors/animation/keyframing.c
@@ -930,11 +930,18 @@ bool insert_keyframe_direct(ReportList *reports, PointerRNA ptr, PropertyRNA *pr
/* update F-Curve flags to ensure proper behaviour for property type */
update_autoflags_fcurve_direct(fcu, prop);
-
+
/* adjust frame on which to add keyframe */
if ((flag & INSERTKEY_DRIVER) && (fcu->driver)) {
- /* for making it easier to add corrective drivers... */
- cfra = evaluate_driver(fcu->driver, cfra);
+ PathResolvedRNA anim_rna;
+
+ if (RNA_path_resolved_create(&ptr, prop, fcu->array_index, &anim_rna)) {
+ /* for making it easier to add corrective drivers... */
+ cfra = evaluate_driver(&anim_rna, fcu->driver, cfra);
+ }
+ else {
+ cfra = 0.0f;
+ }
}
/* obtain value to give keyframe */
diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c
index 6306926e0b2..6979a324b69 100644
--- a/source/blender/editors/armature/armature_utils.c
+++ b/source/blender/editors/armature/armature_utils.c
@@ -424,7 +424,9 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone
for (curBone = bones->first; curBone; curBone = curBone->next) {
eBone = MEM_callocN(sizeof(EditBone), "make_editbone");
- /* Copy relevant data from bone to eBone */
+ /* Copy relevant data from bone to eBone
+ * Keep selection logic in sync with ED_armature_sync_selection.
+ */
eBone->parent = parent;
BLI_strncpy(eBone->name, curBone->name, sizeof(eBone->name));
eBone->flag = curBone->flag;
@@ -435,11 +437,11 @@ EditBone *make_boneList(ListBase *edbo, ListBase *bones, EditBone *parent, Bone
eBone->flag |= BONE_TIPSEL;
if (eBone->parent && (eBone->flag & BONE_CONNECTED)) {
eBone->parent->flag |= BONE_TIPSEL;
- eBone->flag &= ~BONE_ROOTSEL; /* this is ignored when there is a connected parent, so unset it */
- }
- else {
- eBone->flag |= BONE_ROOTSEL;
}
+
+ /* For connected bones, take care when changing the selection when we have a connected parent,
+ * this flag is a copy of '(eBone->parent->flag & BONE_TIPSEL)'. */
+ eBone->flag |= BONE_ROOTSEL;
}
else {
/* if the bone is not selected, but connected to its parent
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index a929507929f..5015829f868 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -86,7 +86,7 @@ void ED_armature_enter_posemode(bContext *C, Base *base)
ReportList *reports = CTX_wm_reports(C);
Object *ob = base->object;
- if (ob->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(ob)) {
BKE_report(reports, RPT_WARNING, "Cannot pose libdata");
return;
}
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index dca9aa3e446..d9a3efa765c 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -183,7 +183,7 @@ static int has_poselib_pose_data_poll(bContext *C)
static int has_poselib_pose_data_for_editing_poll(bContext *C)
{
Object *ob = get_poselib_object(C);
- return (ob && ob->poselib && !ob->poselib->id.lib);
+ return (ob && ob->poselib && !ID_IS_LINKED_DATABLOCK(ob->poselib));
}
/* ----------------------------------- */
@@ -385,7 +385,7 @@ static int poselib_add_poll(bContext *C)
if (ED_operator_posemode(C)) {
Object *ob = get_poselib_object(C);
if (ob) {
- if ((ob->poselib == NULL) || (ob->poselib->id.lib == 0)) {
+ if ((ob->poselib == NULL) || !ID_IS_LINKED_DATABLOCK(ob->poselib)) {
return true;
}
}
diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c
index 420f72fedb3..e40dde24ce2 100644
--- a/source/blender/editors/curve/editcurve.c
+++ b/source/blender/editors/curve/editcurve.c
@@ -1301,7 +1301,7 @@ static int separate_exec(bContext *C, wmOperator *op)
DAG_relations_tag_update(bmain);
newob = newbase->object;
- newcu = newob->data = BKE_curve_copy(oldcu);
+ newcu = newob->data = BKE_curve_copy(bmain, oldcu);
newcu->editnurb = NULL;
id_us_min(&oldcu->id); /* because new curve is a copy: reduce user count */
@@ -5843,7 +5843,7 @@ static int curve_dissolve_exec(bContext *C, wmOperator *UNUSED(op))
normalize_v3(tan_r);
curve_fit_cubic_to_points_single_fl(
- points, points_len, dims, FLT_EPSILON,
+ points, points_len, NULL, dims, FLT_EPSILON,
tan_l, tan_r,
bezt_prev->vec[2], bezt_next->vec[0],
&error_sq_dummy);
@@ -6034,9 +6034,8 @@ int join_curve_exec(bContext *C, wmOperator *op)
BLI_movelisttolist(&cu->nurb, &tempbase);
DAG_relations_tag_update(bmain); // because we removed object(s), call before editmode!
-
- ED_object_editmode_enter(C, EM_WAITCURSOR);
- ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR | EM_DO_UNDO);
+
+ DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
diff --git a/source/blender/editors/curve/editcurve_paint.c b/source/blender/editors/curve/editcurve_paint.c
index 38018541929..2d8fc76ee7e 100644
--- a/source/blender/editors/curve/editcurve_paint.c
+++ b/source/blender/editors/curve/editcurve_paint.c
@@ -55,6 +55,8 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
+
#define USE_SPLINE_FIT
#ifdef USE_SPLINE_FIT
@@ -62,8 +64,11 @@
#endif
/* Distance between input samples */
-#define STROKE_SAMPLE_DIST_MIN_PX 3
-#define STROKE_SAMPLE_DIST_MAX_PX 6
+#define STROKE_SAMPLE_DIST_MIN_PX 1
+#define STROKE_SAMPLE_DIST_MAX_PX 3
+
+/* Distance between start/end points to consider cyclic */
+#define STROKE_CYCLIC_DIST_PX 8
/* -------------------------------------------------------------------- */
@@ -730,6 +735,11 @@ static void curve_draw_exec_precalc(wmOperator *op)
const CurvePaintSettings *cps = &cdd->vc.scene->toolsettings->curve_paint_settings;
PropertyRNA *prop;
+ prop = RNA_struct_find_property(op->ptr, "fit_method");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ RNA_property_enum_set(op->ptr, prop, cps->fit_method);
+ }
+
prop = RNA_struct_find_property(op->ptr, "corner_angle");
if (!RNA_property_is_set(op->ptr, prop)) {
const float corner_angle = (cps->flag & CURVE_PAINT_FLAG_CORNERS_DETECT) ? cps->corner_angle : (float)M_PI;
@@ -759,6 +769,32 @@ static void curve_draw_exec_precalc(wmOperator *op)
RNA_property_float_set(op->ptr, prop, error_threshold);
}
+ prop = RNA_struct_find_property(op->ptr, "use_cyclic");
+ if (!RNA_property_is_set(op->ptr, prop)) {
+ bool use_cyclic = false;
+
+ if (BLI_mempool_count(cdd->stroke_elem_pool) > 2) {
+ BLI_mempool_iter iter;
+ const struct StrokeElem *selem, *selem_first, *selem_last;
+
+ BLI_mempool_iternew(cdd->stroke_elem_pool, &iter);
+ selem_first = BLI_mempool_iterstep(&iter);
+ for (selem = BLI_mempool_iterstep(&iter); selem; selem = BLI_mempool_iterstep(&iter)) {
+ selem_last = selem;
+ }
+
+ if (len_squared_v2v2(
+ selem_first->mval,
+ selem_last->mval) <= SQUARE(STROKE_CYCLIC_DIST_PX * U.pixelsize))
+ {
+ use_cyclic = true;
+ }
+ }
+
+ RNA_property_boolean_set(op->ptr, prop, use_cyclic);
+ }
+
+
if ((cps->radius_taper_start != 0.0f) ||
(cps->radius_taper_end != 0.0f))
{
@@ -868,8 +904,10 @@ static int curve_draw_exec(bContext *C, wmOperator *op)
unsigned int cubic_spline_len = 0;
/* error in object local space */
+ const int fit_method = RNA_enum_get(op->ptr, "fit_method");
const float error_threshold = RNA_float_get(op->ptr, "error_threshold");
const float corner_angle = RNA_float_get(op->ptr, "corner_angle");
+ const bool use_cyclic = RNA_boolean_get(op->ptr, "use_cyclic");
{
BLI_mempool_iter iter;
@@ -894,14 +932,14 @@ static int curve_draw_exec(bContext *C, wmOperator *op)
unsigned int *corners = NULL;
unsigned int corners_len = 0;
- if (corner_angle < (float)M_PI) {
+ if ((fit_method == CURVE_PAINT_FIT_METHOD_SPLIT) && (corner_angle < (float)M_PI)) {
/* this could be configurable... */
const float corner_radius_min = error_threshold / 8;
const float corner_radius_max = error_threshold * 2;
const unsigned int samples_max = 16;
curve_fit_corners_detect_fl(
- (const float *)coords, stroke_len, dims,
+ coords, stroke_len, dims,
corner_radius_min, corner_radius_max,
samples_max, corner_angle,
&corners, &corners_len);
@@ -909,13 +947,29 @@ static int curve_draw_exec(bContext *C, wmOperator *op)
unsigned int *corners_index = NULL;
unsigned int corners_index_len = 0;
+ unsigned int calc_flag = CURVE_FIT_CALC_HIGH_QUALIY;
- const int result = curve_fit_cubic_to_points_fl(
- coords, stroke_len, dims, error_threshold, CURVE_FIT_CALC_HIGH_QUALIY,
- corners, corners_len,
- &cubic_spline, &cubic_spline_len,
- NULL,
- &corners_index, &corners_index_len);
+ if ((stroke_len > 2) && use_cyclic) {
+ calc_flag |= CURVE_FIT_CALC_CYCLIC;
+ }
+
+ int result;
+ if (fit_method == CURVE_PAINT_FIT_METHOD_REFIT) {
+ result = curve_fit_cubic_to_points_refit_fl(
+ coords, stroke_len, dims, error_threshold, calc_flag,
+ NULL, 0, corner_angle,
+ &cubic_spline, &cubic_spline_len,
+ NULL,
+ &corners_index, &corners_index_len);
+ }
+ else {
+ result = curve_fit_cubic_to_points_fl(
+ coords, stroke_len, dims, error_threshold, calc_flag,
+ corners, corners_len,
+ &cubic_spline, &cubic_spline_len,
+ NULL,
+ &corners_index, &corners_index_len);
+ }
MEM_freeN(coords);
if (corners) {
@@ -950,11 +1004,24 @@ static int curve_draw_exec(bContext *C, wmOperator *op)
if (corners_index) {
/* ignore the first and last */
- for (unsigned int i = 1; i < corners_index_len - 1; i++) {
+ unsigned int i_start = 0, i_end = corners_index_len;
+
+ if ((corners_index_len >= 2) &&
+ (calc_flag & CURVE_FIT_CALC_CYCLIC) == 0)
+ {
+ i_start += 1;
+ i_end -= 1;
+ }
+
+ for (unsigned int i = i_start; i < i_end; i++) {
bezt = &nu->bezt[corners_index[i]];
bezt->h1 = bezt->h2 = HD_FREE;
}
}
+
+ if (calc_flag & CURVE_FIT_CALC_CYCLIC) {
+ nu->flagu |= CU_NURB_CYCLIC;
+ }
}
if (corners_index) {
@@ -1220,13 +1287,19 @@ void CURVE_OT_draw(wmOperatorType *ot)
0.0001f, 10.0f);
RNA_def_property_ui_range(prop, 0.0, 10, 1, 4);
+ RNA_def_enum(ot->srna, "fit_method", rna_enum_curve_fit_method_items, CURVE_PAINT_FIT_METHOD_REFIT,
+ "Fit Method", "");
+
prop = RNA_def_float_distance(
ot->srna, "corner_angle", DEG2RADF(70.0f), 0.0f, M_PI, "Corner Angle", "", 0.0f, M_PI);
RNA_def_property_subtype(prop, PROP_ANGLE);
- prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+ prop = RNA_def_boolean(ot->srna, "use_cyclic", true, "Cyclic", "");
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+
prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
index a29266294b4..cad70443657 100644
--- a/source/blender/editors/curve/editcurve_select.c
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -294,11 +294,11 @@ static void select_adjacent_cp(
if (next < 0) bezt = &nu->bezt[a - 1];
while (a--) {
if (a - abs(next) < 0) break;
- if ((lastsel == 0) && (bezt->hide == 0) && ((bezt->f2 & SELECT) || (selstatus == DESELECT))) {
+ if ((lastsel == false) && (bezt->hide == 0) && ((bezt->f2 & SELECT) || (selstatus == DESELECT))) {
bezt += next;
if (!(bezt->f2 & SELECT) || (selstatus == DESELECT)) {
- short sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
- if ((sel == 1) && (cont == 0)) lastsel = true;
+ bool sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
+ if (sel && !cont) lastsel = true;
}
}
else {
@@ -315,11 +315,11 @@ static void select_adjacent_cp(
if (next < 0) bp = &nu->bp[a - 1];
while (a--) {
if (a - abs(next) < 0) break;
- if ((lastsel == 0) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == DESELECT))) {
+ if ((lastsel == false) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == DESELECT))) {
bp += next;
if (!(bp->f1 & SELECT) || (selstatus == DESELECT)) {
- short sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
- if ((sel == 1) && (cont == 0)) lastsel = true;
+ bool sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
+ if (sel && !cont) lastsel = true;
}
}
else {
@@ -820,7 +820,7 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
BezTriple *bezt;
int a;
int sel = 0;
- short lastsel = false;
+ bool lastsel = false;
if (obedit->type == OB_SURF) {
for (nu = editnurb->first; nu; nu = nu->next) {
@@ -935,9 +935,8 @@ static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
a = nu->pntsu * nu->pntsv;
bp = nu->bp;
while (a--) {
- if ((lastsel == 0) && (bp->hide == 0) && (bp->f1 & SELECT)) {
- if (lastsel != 0) sel = 1;
- else sel = 0;
+ if ((lastsel == false) && (bp->hide == 0) && (bp->f1 & SELECT)) {
+ sel = 0;
/* first and last are exceptions */
if (a == nu->pntsu * nu->pntsv - 1) {
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
index 053a7ee5023..b40b51e337f 100644
--- a/source/blender/editors/curve/editfont.c
+++ b/source/blender/editors/curve/editfont.c
@@ -1695,7 +1695,7 @@ static int font_open_exec(bContext *C, wmOperator *op)
if (pprop->prop) {
/* when creating new ID blocks, use is already 1, but RNA
- * pointer se also increases user, so this compensates it */
+ * pointer use also increases user, so this compensates it */
id_us_min(&font->id);
RNA_id_pointer_create(&font->id, &idptr);
diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c
index a0453f9694d..a61f863b61e 100644
--- a/source/blender/editors/curve/editfont_undo.c
+++ b/source/blender/editors/curve/editfont_undo.c
@@ -77,7 +77,7 @@ static struct {
/* We could have the undo API pass in the previous state, for now store a local list */
ListBase local_links;
-} uf_arraystore = {NULL};
+} uf_arraystore = {{NULL}};
/**
* \param create: When false, only free the arrays.
diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c
index 79a2c494239..bd09616243b 100644
--- a/source/blender/editors/gpencil/drawgpencil.c
+++ b/source/blender/editors/gpencil/drawgpencil.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -52,6 +52,7 @@
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_userdef_types.h"
+#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -94,9 +95,32 @@ typedef enum eDrawStrokeFlags {
#define GP_DRAWTHICKNESS_SPECIAL 3
/* ----- Tool Buffer Drawing ------ */
+/* helper function to set color of buffer point */
+static void gp_set_tpoint_color(tGPspoint *pt, float ink[4])
+{
+ float alpha = ink[3] * pt->strength;
+ CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
+ glColor4f(ink[0], ink[1], ink[2], alpha);
+}
+
+/* helper function to set color of point */
+static void gp_set_point_color(bGPDspoint *pt, float ink[4])
+{
+ float alpha = ink[3] * pt->strength;
+ CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f);
+ glColor4f(ink[0], ink[1], ink[2], alpha);
+}
+
+/* helper function to set color and point */
+static void gp_set_color_and_tpoint(tGPspoint *pt, float ink[4])
+{
+ gp_set_tpoint_color(pt, ink);
+ glVertex2iv(&pt->x);
+}
/* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */
-static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness, short dflag, short sflag)
+static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickness,
+ short dflag, short sflag, float ink[4])
{
tGPspoint *pt;
int i;
@@ -113,7 +137,8 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn
/* if drawing a single point, draw it larger */
glPointSize((float)(thickness + 2) * points->pressure);
glBegin(GL_POINTS);
- glVertex2iv(&points->x);
+
+ gp_set_color_and_tpoint(points, ink);
glEnd();
}
else if (sflag & GP_STROKE_ERASER) {
@@ -138,15 +163,18 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn
glBegin(GL_LINE_STRIP);
/* need to roll-back one point to ensure that there are no gaps in the stroke */
- if (i != 0) glVertex2iv(&(pt - 1)->x);
+ if (i != 0) {
+ gp_set_color_and_tpoint((pt - 1), ink);
+ }
/* now the point we want... */
- glVertex2iv(&pt->x);
+ gp_set_color_and_tpoint(pt, ink);
oldpressure = pt->pressure;
}
- else
- glVertex2iv(&pt->x);
+ else {
+ gp_set_color_and_tpoint(pt, ink);
+ }
}
glEnd();
@@ -155,37 +183,35 @@ static void gp_draw_stroke_buffer(tGPspoint *points, int totpoints, short thickn
}
/* --------- 2D Stroke Drawing Helpers --------- */
-
-/* helper function to calculate x-y drawing coordinates for 2D points */
-static void gp_calc_2d_stroke_xy(bGPDspoint *pt, short sflag, int offsx, int offsy, int winx, int winy, float r_co[2])
+/* change in parameter list */
+static void gp_calc_2d_stroke_fxy(float pt[3], short sflag, int offsx, int offsy, int winx, int winy, float r_co[2])
{
if (sflag & GP_STROKE_2DSPACE) {
- r_co[0] = pt->x;
- r_co[1] = pt->y;
+ r_co[0] = pt[0];
+ r_co[1] = pt[1];
}
else if (sflag & GP_STROKE_2DIMAGE) {
- const float x = (float)((pt->x * winx) + offsx);
- const float y = (float)((pt->y * winy) + offsy);
-
+ const float x = (float)((pt[0] * winx) + offsx);
+ const float y = (float)((pt[1] * winy) + offsy);
+
r_co[0] = x;
r_co[1] = y;
}
else {
- const float x = (float)(pt->x / 100 * winx) + offsx;
- const float y = (float)(pt->y / 100 * winy) + offsy;
-
+ const float x = (float)(pt[0] / 100 * winx) + offsx;
+ const float y = (float)(pt[1] / 100 * winy) + offsy;
+
r_co[0] = x;
r_co[1] = y;
}
}
-
/* ----------- Volumetric Strokes --------------- */
/* draw a 2D buffer stroke in "volumetric" style
* NOTE: the stroke buffer doesn't have any coordinate offsets/transforms
*/
static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, short thickness,
- short dflag, short UNUSED(sflag))
+ short dflag, short UNUSED(sflag), float ink[4])
{
GLUquadricObj *qobj = gluNewQuadric();
float modelview[4][4];
@@ -216,6 +242,7 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s
glLoadMatrixf((float *)modelview);
/* draw the disk using the current state... */
+ gp_set_tpoint_color(pt, ink);
gluDisk(qobj, 0.0, pt->pressure * thickness, 32, 1);
@@ -229,7 +256,8 @@ static void gp_draw_stroke_volumetric_buffer(tGPspoint *points, int totpoints, s
/* draw a 2D strokes in "volumetric" style */
static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, short thickness,
short dflag, short sflag,
- int offsx, int offsy, int winx, int winy)
+ int offsx, int offsy, int winx, int winy,
+ float diff_mat[4][4], float ink[4])
{
GLUquadricObj *qobj = gluNewQuadric();
float modelview[4][4];
@@ -238,7 +266,7 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor
bGPDspoint *pt;
int i;
-
+ float fpt[3];
/* HACK: We need a scale factor for the drawing in the image editor,
* which seems to use 1 unit as it's maximum size, whereas everything
@@ -256,10 +284,14 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor
glPushMatrix();
for (i = 0, pt = points; i < totpoints; i++, pt++) {
+ /* color of point */
+ gp_set_point_color(pt, ink);
+
/* set the transformed position */
float co[2];
- gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
translate_m4(modelview, co[0], co[1], 0.0f);
glLoadMatrixf((float *)modelview);
@@ -276,8 +308,9 @@ static void gp_draw_stroke_volumetric_2d(bGPDspoint *points, int totpoints, shor
}
/* draw a 3D stroke in "volumetric" style */
-static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, short thickness,
- short UNUSED(dflag), short UNUSED(sflag))
+static void gp_draw_stroke_volumetric_3d(
+ bGPDspoint *points, int totpoints, short thickness,
+ short UNUSED(dflag), short UNUSED(sflag), float diff_mat[4][4], float ink[4])
{
GLUquadricObj *qobj = gluNewQuadric();
@@ -286,7 +319,7 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor
bGPDspoint *pt;
int i;
-
+ float fpt[3];
/* Get the basic modelview matrix we use for performing calculations */
glGetFloatv(GL_MODELVIEW_MATRIX, (float *)base_modelview);
@@ -305,8 +338,13 @@ static void gp_draw_stroke_volumetric_3d(bGPDspoint *points, int totpoints, shor
glPushMatrix();
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
+ /* color of point */
+ gp_set_point_color(pt, ink);
+
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
/* apply translation to base_modelview, so that the translated point is put in the right place */
- translate_m4(base_modelview, pt->x, pt->y, pt->z);
+ translate_m4(base_modelview, fpt[0], fpt[1], fpt[2]);
/* copy the translation component to the billboard matrix we're going to use,
* then reset the base matrix to the original values so that we can do the same
@@ -378,9 +416,9 @@ static void gp_stroke_2d_flat(bGPDspoint *points, int totpoints, float(*points2d
static void gp_triangulate_stroke_fill(bGPDstroke *gps)
{
BLI_assert(gps->totpoints >= 3);
- gps->tot_triangles = gps->totpoints - 2;
-
+
/* allocate memory for temporary areas */
+ gps->tot_triangles = gps->totpoints - 2;
unsigned int (*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * gps->tot_triangles, "GP Stroke temp triangulation");
float (*points2d)[2] = MEM_mallocN(sizeof(*points2d) * gps->totpoints, "GP Stroke temp 2d points");
@@ -390,6 +428,8 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps)
gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction);
BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)gps->totpoints, direction, (unsigned int(*)[3])tmp_triangles);
+ /* Number of triangles */
+ gps->tot_triangles = gps->totpoints - 2;
/* save triangulation data in stroke cache */
if (gps->tot_triangles > 0) {
if (gps->triangles == NULL) {
@@ -399,9 +439,7 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps)
gps->triangles = MEM_recallocN(gps->triangles, sizeof(*gps->triangles) * gps->tot_triangles);
}
- int i;
-
- for (i = 0; i < gps->tot_triangles; i++) {
+ for (int i = 0; i < gps->tot_triangles; i++) {
bGPDtriangle *stroke_triangle = &gps->triangles[i];
stroke_triangle->v1 = tmp_triangles[i][0];
stroke_triangle->v2 = tmp_triangles[i][1];
@@ -428,21 +466,27 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps)
/* draw fills for shapes */
-static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short dflag, int offsx, int offsy, int winx, int winy)
+static void gp_draw_stroke_fill(
+ bGPdata *gpd, bGPDstroke *gps,
+ int offsx, int offsy, int winx, int winy, float diff_mat[4][4])
{
+ bGPDpalettecolor *palcolor;
+ int i;
+ float fpt[3];
+
BLI_assert(gps->totpoints >= 3);
-
+
+ palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+
/* Triangulation fill if high quality flag is enabled */
- if (dflag & GP_DRAWDATA_HQ_FILL) {
+ if (palcolor->flag & PC_COLOR_HQ_FILL) {
bGPDtriangle *stroke_triangle;
bGPDspoint *pt;
- int i;
-
+
/* Calculate triangles cache for filling area (must be done only after changes) */
if ((gps->flag & GP_STROKE_RECALC_CACHES) || (gps->tot_triangles == 0) || (gps->triangles == NULL)) {
gp_triangulate_stroke_fill(gps);
}
-
/* Draw all triangles for filling the polygon (cache must be calculated before) */
BLI_assert(gps->tot_triangles >= 1);
glBegin(GL_TRIANGLES);
@@ -450,32 +494,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short
if (gps->flag & GP_STROKE_3DSPACE) {
/* vertex 1 */
pt = &gps->points[stroke_triangle->v1];
- glVertex3fv(&pt->x);
-
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
/* vertex 2 */
pt = &gps->points[stroke_triangle->v2];
- glVertex3fv(&pt->x);
-
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
/* vertex 3 */
pt = &gps->points[stroke_triangle->v3];
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
-
/* vertex 1 */
pt = &gps->points[stroke_triangle->v1];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
-
/* vertex 2 */
pt = &gps->points[stroke_triangle->v2];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
-
/* vertex 3 */
pt = &gps->points[stroke_triangle->v3];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
@@ -483,30 +528,31 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short
}
else {
/* As an initial implementation, we use the OpenGL filled polygon drawing
- * here since it's the easiest option to implement for this case. It does
- * come with limitations (notably for concave shapes), though it works well
- * enough for many simple situations.
- *
- * We keep this legacy implementation around despite now having the high quality
- * fills, as this is necessary for keeping everything working nicely for files
- * created using old versions of Blender which may have depended on the artifacts
- * the old fills created.
- */
+ * here since it's the easiest option to implement for this case. It does
+ * come with limitations (notably for concave shapes), though it shouldn't
+ * be much of an issue in most cases.
+ *
+ * We keep this legacy implementation around despite now having the high quality
+ * fills, as this is necessary for keeping everything working nicely for files
+ * created using old versions of Blender which may have depended on the artifacts
+ * the old fills created.
+ */
bGPDspoint *pt;
- int i;
-
+
glBegin(GL_POLYGON);
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
if (gps->flag & GP_STROKE_3DSPACE) {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
-
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
+
glEnd();
}
}
@@ -514,23 +560,33 @@ static void gp_draw_stroke_fill(bGPDstroke *gps, short UNUSED(thickness), short
/* ----- Existing Strokes Drawing (3D and Point) ------ */
/* draw a given stroke - just a single dot (only one point) */
-static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dflag, short sflag,
- int offsx, int offsy, int winx, int winy)
+static void gp_draw_stroke_point(
+ bGPDspoint *points, short thickness, short dflag, short sflag,
+ int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4])
{
+ float fpt[3];
+ bGPDspoint *pt = &points[0];
+
+ /* color of point */
+ gp_set_point_color(pt, ink);
+
/* set point thickness (since there's only one of these) */
glPointSize((float)(thickness + 2) * points->pressure);
+ /* get final position using parent matrix */
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
/* draw point */
if (sflag & GP_STROKE_3DSPACE) {
glBegin(GL_POINTS);
- glVertex3fv(&points->x);
+ glVertex3fv(fpt);
glEnd();
}
else {
float co[2];
/* get coordinates of point */
- gp_calc_2d_stroke_xy(points, sflag, offsx, offsy, winx, winy, co);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
/* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, simple dot looks ok
* - also mandatory in if Image Editor 'image-based' dot
@@ -559,16 +615,21 @@ static void gp_draw_stroke_point(bGPDspoint *points, short thickness, short dfla
}
/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */
-static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug, short UNUSED(sflag))
+static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness, bool debug,
+ short UNUSED(sflag), float diff_mat[4][4], float ink[4], bool cyclic)
{
- bGPDspoint *pt;
+ bGPDspoint *pt, *pt2;
float curpressure = points[0].pressure;
int i;
-
+ float fpt[3];
+ float cyclic_fpt[3];
+
/* draw stroke curve */
glLineWidth(max_ff(curpressure * thickness, 1.0f));
glBegin(GL_LINE_STRIP);
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
+ gp_set_point_color(pt, ink);
+
/* if there was a significant pressure change, stop the curve, change the thickness of the stroke,
* and continue drawing again (since line-width cannot change in middle of GL_LINE_STRIP)
* Note: we want more visible levels of pressures when thickness is bigger.
@@ -580,15 +641,29 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
glBegin(GL_LINE_STRIP);
/* need to roll-back one point to ensure that there are no gaps in the stroke */
- if (i != 0) glVertex3fv(&(pt - 1)->x);
+ if (i != 0) {
+ pt2 = pt - 1;
+ mul_v3_m4v3(fpt, diff_mat, &pt2->x);
+ glVertex3fv(fpt);
+ }
/* now the point we want... */
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
+ }
+ /* saves first point to use in cyclic */
+ if (i == 0) {
+ copy_v3_v3(cyclic_fpt, fpt);
}
}
+ /* if cyclic draw line to first point */
+ if (cyclic) {
+ glVertex3fv(cyclic_fpt);
+ }
glEnd();
/* draw debug points of curve on top? */
@@ -597,9 +672,12 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
glPointSize((float)(thickness + 2));
glBegin(GL_POINTS);
- for (i = 0, pt = points; i < totpoints && pt; i++, pt++)
- glVertex3fv(&pt->x);
+ for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
+ }
glEnd();
+
}
}
@@ -607,7 +685,7 @@ static void gp_draw_stroke_3d(bGPDspoint *points, int totpoints, short thickness
/* draw a given stroke in 2d */
static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag,
- bool debug, int offsx, int offsy, int winx, int winy)
+ bool debug, int offsx, int offsy, int winx, int winy, float diff_mat[4][4], float ink[4])
{
/* otherwise thickness is twice that of the 3D view */
float thickness = (float)thickness_s * 0.5f;
@@ -625,6 +703,7 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
bGPDspoint *pt1, *pt2;
float pm[2];
int i;
+ float fpt[3];
glShadeModel(GL_FLAT);
glBegin(GL_QUADS);
@@ -635,10 +714,13 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
float m1[2], m2[2]; /* gradient and normal */
float mt[2], sc[2]; /* gradient for thickness, point for end-cap */
float pthick; /* thickness at segment point */
-
+
/* get x and y coordinates from points */
- gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0);
- gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1);
+ mul_v3_m4v3(fpt, diff_mat, &pt1->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s0);
+
+ mul_v3_m4v3(fpt, diff_mat, &pt2->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, s1);
/* calculate gradient and normal - 'angle'=(ny/nx) */
m1[1] = s1[1] - s0[1];
@@ -650,6 +732,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
/* always use pressure from first point here */
pthick = (pt1->pressure * thickness * scalefac);
+ /* color of point */
+ gp_set_point_color(pt1, ink);
+
/* if the first segment, start of segment is segment's normal */
if (i == 0) {
/* draw start cap first
@@ -725,6 +810,9 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
/* for once, we use second point's pressure (otherwise it won't be drawn) */
pthick = (pt2->pressure * thickness * scalefac);
+ /* color of point */
+ gp_set_point_color(pt2, ink);
+
/* calculate points for end of segment */
mt[0] = m2[0] * pthick;
mt[1] = m2[1] * pthick;
@@ -770,14 +858,15 @@ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness
if (debug) {
bGPDspoint *pt;
int i;
-
+ float fpt[3];
+
glPointSize((float)(thickness_s + 2));
glBegin(GL_POINTS);
for (i = 0, pt = points; i < totpoints && pt; i++, pt++) {
float co[2];
-
- gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
glEnd();
@@ -818,26 +907,45 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag)
}
/* draw a set of strokes */
-static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag,
- bool debug, short lthick, const float color[4], const float fill_color[4])
+static void gp_draw_strokes(
+ bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, int dflag,
+ bool debug, short lthick, const float opacity, const float tintcolor[4],
+ const bool onion, const bool custonion, float diff_mat[4][4])
{
bGPDstroke *gps;
-
+ float tcolor[4];
+ float tfill[4];
+ short sthickness;
+ float ink[4];
+
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* check if stroke can be drawn */
- if (gp_can_draw_stroke(gps, dflag) == false)
+ if (gp_can_draw_stroke(gps, dflag) == false) {
continue;
-
+ }
+ /* check if the color is visible */
+ bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+ if ((palcolor == NULL) ||
+ (palcolor->flag & PC_COLOR_HIDE) ||
+ /* if onion and ghost flag do not draw*/
+ (onion && (palcolor->flag & PC_COLOR_ONIONSKIN)))
+ {
+ continue;
+ }
+
+ /* calculate thickness */
+ sthickness = gps->thickness + lthick;
+
/* check which stroke-drawer to use */
if (dflag & GP_DRAWDATA_ONLY3D) {
const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY);
int mask_orig = 0;
-
+
if (no_xray) {
glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig);
glDepthMask(0);
glEnable(GL_DEPTH_TEST);
-
+
/* first arg is normally rv3d->dist, but this isn't
* available here and seems to work quite well without */
bglPolygonOffset(1.0f, 1.0f);
@@ -846,34 +954,65 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
glPolygonOffset(-1.0f, -1.0f);
#endif
}
-
+
/* 3D Fill */
- if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
- glColor4fv(fill_color);
- gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy);
+ //if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
+ if (gps->totpoints >= 3) {
+ /* set color using palette, tint color and opacity */
+ interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]);
+ tfill[3] = palcolor->fill[3] * opacity;
+ if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) {
+ if (!onion) {
+ glColor4fv(tfill);
+ }
+ else {
+ if (custonion) {
+ glColor4fv(tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tfill, UNPACK3(palcolor->fill), tintcolor[3]);
+ glColor4fv(tfill);
+ }
+ }
+ gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat);
+ }
}
-
+
/* 3D Stroke */
- glColor4fv(color);
-
- if (dflag & GP_DRAWDATA_VOLUMETRIC) {
+ /* set color using palette, tint color and opacity */
+ if (!onion) {
+ interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]);
+ tcolor[3] = palcolor->color[3] * opacity;
+ copy_v4_v4(ink, tcolor);
+ }
+ else {
+ if (custonion) {
+ copy_v4_v4(ink, tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity);
+ copy_v4_v4(ink, tcolor);
+ }
+ }
+ if (palcolor->flag & PC_COLOR_VOLUMETRIC) {
/* volumetric stroke drawing */
- gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, lthick, dflag, gps->flag);
+ gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, diff_mat, ink);
}
else {
/* 3D Lines - OpenGL primitives-based */
if (gps->totpoints == 1) {
- gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
+ gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy,
+ diff_mat, ink);
}
else {
- gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag);
+ gp_draw_stroke_3d(gps->points, gps->totpoints, sthickness, debug, gps->flag,
+ diff_mat, ink, gps->flag & GP_STROKE_CYCLIC);
}
}
-
if (no_xray) {
glDepthMask(mask_orig);
glDisable(GL_DEPTH_TEST);
-
+
bglPolygonOffset(0.0, 0.0);
#if 0
glDisable(GL_POLYGON_OFFSET_LINE);
@@ -883,25 +1022,58 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
}
else {
/* 2D - Fill */
- if ((dflag & GP_DRAWDATA_FILL) && (gps->totpoints >= 3)) {
- glColor4fv(fill_color);
- gp_draw_stroke_fill(gps, lthick, dflag, offsx, offsy, winx, winy);
+ if (gps->totpoints >= 3) {
+ /* set color using palette, tint color and opacity */
+ interp_v3_v3v3(tfill, palcolor->fill, tintcolor, tintcolor[3]);
+ tfill[3] = palcolor->fill[3] * opacity;
+ if (tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) {
+ if (!onion) {
+ glColor4fv(tfill);
+ }
+ else {
+ if (custonion) {
+ glColor4fv(tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tfill, palcolor->fill[0], palcolor->fill[1], palcolor->fill[2],
+ tintcolor[3]);
+ glColor4fv(tfill);
+ }
+ }
+ gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat);
+ }
}
-
+
/* 2D Strokes... */
- glColor4fv(color);
-
- if (dflag & GP_DRAWDATA_VOLUMETRIC) {
+ /* set color using palette, tint color and opacity */
+ if (!onion) {
+ interp_v3_v3v3(tcolor, palcolor->color, tintcolor, tintcolor[3]);
+ tcolor[3] = palcolor->color[3] * opacity;
+ copy_v4_v4(ink, tcolor);
+ }
+ else {
+ if (custonion) {
+ copy_v4_v4(ink, tintcolor);
+ }
+ else {
+ ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity);
+ copy_v4_v4(ink, tcolor);
+ }
+ }
+ if (palcolor->flag & PC_COLOR_VOLUMETRIC) {
/* blob/disk-based "volumetric" drawing */
- gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
+ gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag,
+ offsx, offsy, winx, winy, diff_mat, ink);
}
else {
/* normal 2D strokes */
if (gps->totpoints == 1) {
- gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy);
+ gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy,
+ diff_mat, ink);
}
else {
- gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, offsx, offsy, winx, winy);
+ gp_draw_stroke_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, debug,
+ offsx, offsy, winx, winy, diff_mat, ink);
}
}
}
@@ -909,13 +1081,19 @@ static void gp_draw_strokes(bGPDframe *gpf, int offsx, int offsy, int winx, int
}
/* Draw selected verts for strokes being edited */
-static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag, const float tcolor[3])
+static void gp_draw_strokes_edit(
+ bGPdata *gpd, bGPDframe *gpf, int offsx, int offsy, int winx, int winy, short dflag,
+ short lflag, float diff_mat[4][4], float alpha)
{
bGPDstroke *gps;
+ /* if alpha 0 do not draw */
+ if (alpha == 0.0f)
+ return;
+
const bool no_xray = (dflag & GP_DRAWDATA_NO_XRAY) != 0;
int mask_orig = 0;
-
+
/* set up depth masks... */
if (dflag & GP_DRAWDATA_ONLY3D) {
if (no_xray) {
@@ -939,7 +1117,8 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
bGPDspoint *pt;
float vsize, bsize;
int i;
-
+ float fpt[3];
+
/* check if stroke can be drawn */
if (gp_can_draw_stroke(gps, dflag) == false)
continue;
@@ -951,6 +1130,19 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
if ((gps->flag & GP_STROKE_SELECT) == 0)
continue;
+ /* verify palette color lock */
+ {
+ bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+ if (palcolor != NULL) {
+ if (palcolor->flag & PC_COLOR_HIDE) {
+ continue;
+ }
+ if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) {
+ continue;
+ }
+ }
+ }
+
/* Get size of verts:
* - The selected state needs to be larger than the unselected state so that
* they stand out more.
@@ -966,25 +1158,23 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
}
/* First Pass: Draw all the verts (i.e. these become the unselected state) */
- if (tcolor != NULL) {
- /* for now, we assume that the base color of the points is not too close to the real color */
- glColor3fv(tcolor);
- }
- else {
- /* this doesn't work well with the default theme and black strokes... */
- UI_ThemeColor(TH_GP_VERTEX);
- }
+ /* for now, we assume that the base color of the points is not too close to the real color */
+ /* set color using palette */
+ bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps);
+ glColor3fv(palcolor->color);
+
glPointSize(bsize);
glBegin(GL_POINTS);
for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
if (gps->flag & GP_STROKE_3DSPACE) {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
-
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
@@ -992,24 +1182,54 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
/* Second Pass: Draw only verts which are selected */
- UI_ThemeColor(TH_GP_VERTEX_SELECT);
+ float curColor[4];
+ UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, curColor);
+ glColor4f(curColor[0], curColor[1], curColor[2], alpha);
+
glPointSize(vsize);
glBegin(GL_POINTS);
for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
if (pt->flag & GP_SPOINT_SELECT) {
if (gps->flag & GP_STROKE_3DSPACE) {
- glVertex3fv(&pt->x);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ glVertex3fv(fpt);
}
else {
float co[2];
- gp_calc_2d_stroke_xy(pt, gps->flag, offsx, offsy, winx, winy, co);
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co);
glVertex2fv(co);
}
}
}
glEnd();
+
+ /* Draw start and end point if enabled stroke direction hint */
+ if ((gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1)) {
+ bGPDspoint *p;
+
+ glPointSize(vsize + 4);
+ glBegin(GL_POINTS);
+
+ /* start point in green bigger */
+ glColor3f(0.0f, 1.0f, 0.0f);
+ p = &gps->points[0];
+ mul_v3_m4v3(fpt, diff_mat, &p->x);
+ glVertex3fv(fpt);
+ glEnd();
+
+ /* end point in red smaller */
+ glPointSize(vsize + 1);
+ glBegin(GL_POINTS);
+
+ glColor3f(1.0f, 0.0f, 0.0f);
+ p = &gps->points[gps->totpoints - 1];
+ mul_v3_m4v3(fpt, diff_mat, &p->x);
+ glVertex3fv(fpt);
+ glEnd();
+ }
}
@@ -1031,18 +1251,20 @@ static void gp_draw_strokes_edit(bGPDframe *gpf, int offsx, int offsy, int winx,
/* ----- General Drawing ------ */
/* draw onion-skinning for a layer */
-static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy,
- int UNUSED(cfra), int dflag, bool debug, short lthick)
+static void gp_draw_onionskins(
+ bGPdata *gpd, bGPDlayer *gpl, bGPDframe *gpf, int offsx, int offsy, int winx, int winy,
+ int UNUSED(cfra), int dflag, bool debug, float diff_mat[4][4])
{
- const float alpha = gpl->color[3];
+ const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)};
+ const float alpha = 1.0f;
float color[4];
-
+
/* 1) Draw Previous Frames First */
if (gpl->flag & GP_LAYER_GHOST_PREVCOL) {
copy_v3_v3(color, gpl->gcolor_prev);
}
else {
- copy_v3_v3(color, gpl->color);
+ copy_v3_v3(color, default_color);
}
if (gpl->gstep > 0) {
@@ -1056,7 +1278,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* alpha decreases with distance from curframe index */
fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1));
color[3] = alpha * fac * 0.66f;
- gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat);
}
else
break;
@@ -1066,7 +1289,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* draw the strokes for the ghost frames (at half of the alpha set by user) */
if (gpf->prev) {
color[3] = (alpha / 7);
- gp_draw_strokes(gpf->prev, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gpf->prev, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat);
}
}
else {
@@ -1079,7 +1303,7 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
copy_v3_v3(color, gpl->gcolor_next);
}
else {
- copy_v3_v3(color, gpl->color);
+ copy_v3_v3(color, default_color);
}
if (gpl->gstep_next > 0) {
@@ -1093,7 +1317,8 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* alpha decreases with distance from curframe index */
fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1));
color[3] = alpha * fac * 0.66f;
- gp_draw_strokes(gf, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat);
}
else
break;
@@ -1103,27 +1328,31 @@ static void gp_draw_onionskins(bGPDlayer *gpl, bGPDframe *gpf, int offsx, int of
/* draw the strokes for the ghost frames (at half of the alpha set by user) */
if (gpf->next) {
color[3] = (alpha / 4);
- gp_draw_strokes(gpf->next, offsx, offsy, winx, winy, dflag, debug, lthick, color, color);
+ gp_draw_strokes(gpd, gpf->next, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color,
+ true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat);
}
}
else {
/* don't draw - disabled */
}
- /* 3) restore alpha */
- glColor4fv(gpl->color);
}
/* loop over gpencil data layers, drawing them */
-static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag)
+static void gp_draw_data_layers(
+ bGPDbrush *brush, float alpha, bGPdata *gpd,
+ int offsx, int offsy, int winx, int winy, int cfra, int dflag)
{
bGPDlayer *gpl;
-
+ float diff_mat[4][4];
+
for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
bGPDframe *gpf;
-
+ /* calculate parent position */
+ ED_gpencil_parent_location(gpl, diff_mat);
+
bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG) ? true : false;
- short lthick = gpl->thickness;
+ short lthick = brush->thickness + gpl->thickness;
/* don't draw layer if hidden */
if (gpl->flag & GP_LAYER_HIDE)
@@ -1155,9 +1384,6 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
/* HQ fills... */
GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL);
- /* fill strokes... */
- // XXX: this is not a very good limit
- GP_DRAWFLAG_APPLY((gpl->fill[3] > GPENCIL_ALPHA_OPACITY_THRESH), GP_DRAWDATA_FILL);
#undef GP_DRAWFLAG_APPLY
/* draw 'onionskins' (frame left + right) */
@@ -1165,11 +1391,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
/* Drawing method - only immediately surrounding (gstep = 0),
* or within a frame range on either side (gstep > 0)
*/
- gp_draw_onionskins(gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, lthick);
+ gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, diff_mat);
}
/* draw the strokes already in active frame */
- gp_draw_strokes(gpf, offsx, offsy, winx, winy, dflag, debug, lthick, gpl->color, gpl->fill);
+ gp_draw_strokes(gpd, gpf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness,
+ gpl->opacity, gpl->tintcolor, false, false, diff_mat);
/* Draw verts of selected strokes
* - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering
@@ -1183,8 +1410,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
(gpl->flag & GP_LAYER_LOCKED) == 0 &&
(gpd->flag & GP_DATA_STROKE_EDITMODE))
{
- gp_draw_strokes_edit(gpf, offsx, offsy, winx, winy, dflag,
- (gpl->color[3] < 0.95f) ? gpl->color : NULL);
+ gp_draw_strokes_edit(gpd, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, diff_mat, alpha);
}
/* Check if may need to draw the active stroke cache, only if this layer is the active layer
@@ -1194,7 +1420,7 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
(gpf->flag & GP_FRAME_PAINT))
{
/* Set color for drawing buffer stroke - since this may not be set yet */
- glColor4fv(gpl->color);
+ // glColor4fv(gpl->color);
/* Buffer stroke needs to be drawn with a different linestyle
* to help differentiate them from normal strokes.
@@ -1202,11 +1428,12 @@ static void gp_draw_data_layers(bGPdata *gpd, int offsx, int offsy, int winx, in
* It should also be noted that sbuffer contains temporary point types
* i.e. tGPspoints NOT bGPDspoints
*/
- if (gpl->flag & GP_LAYER_VOLUMETRIC) {
- gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
+ if (gpd->sflag & PC_COLOR_VOLUMETRIC) {
+ gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick,
+ dflag, gpd->sbuffer_sflag, gpd->scolor);
}
else {
- gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag);
+ gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, gpd->scolor);
}
}
}
@@ -1258,7 +1485,9 @@ static void gp_draw_status_text(bGPdata *gpd, ARegion *ar)
}
/* draw grease-pencil datablock */
-static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag)
+static void gp_draw_data(
+ bGPDbrush *brush, float alpha, bGPdata *gpd,
+ int offsx, int offsy, int winx, int winy, int cfra, int dflag)
{
/* reset line drawing style (in case previous user didn't reset) */
setlinestyle(0);
@@ -1276,7 +1505,7 @@ static void gp_draw_data(bGPdata *gpd, int offsx, int offsy, int winx, int winy,
glEnable(GL_BLEND);
/* draw! */
- gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag);
+ gp_draw_data_layers(brush, alpha, gpd, offsx, offsy, winx, winy, cfra, dflag);
/* turn off alpha blending, then smooth lines */
glDisable(GL_BLEND); // alpha blending
@@ -1303,14 +1532,25 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i
}
if (gpd_source) {
- gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag);
+ ToolSettings *ts = scene->toolsettings;
+ bGPDbrush *brush = gpencil_brush_getactive(ts);
+ if (brush != NULL) {
+ gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source,
+ offsx, offsy, winx, winy, cfra, dflag);
+ }
+
}
}
/* scene/clip data has already been drawn, only object/track data is drawn here
* if gpd_source == gpd, we don't have any object/track data and we can skip */
if (gpd_source == NULL || (gpd_source && gpd_source != gpd)) {
- gp_draw_data(gpd, offsx, offsy, winx, winy, cfra, dflag);
+ ToolSettings *ts = scene->toolsettings;
+ bGPDbrush *brush = gpencil_brush_getactive(ts);
+ if (brush != NULL) {
+ gp_draw_data(brush, ts->gp_sculpt.alpha, gpd,
+ offsx, offsy, winx, winy, cfra, dflag);
+ }
}
}
@@ -1479,6 +1719,7 @@ void ED_gpencil_draw_view3d(wmWindowManager *wm, Scene *scene, View3D *v3d, AReg
/* draw it! */
gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype);
+
}
void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype)
diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c
index 0271afd6827..1bb3b7e1ae7 100644
--- a/source/blender/editors/gpencil/gpencil_brush.c
+++ b/source/blender/editors/gpencil/gpencil_brush.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2015, Blender Foundation
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*
@@ -51,6 +51,7 @@
#include "DNA_space_types.h"
#include "DNA_view3d_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_global.h"
@@ -223,9 +224,26 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i
GP_EditBrush_Data *brush = gso->brush;
float inf = gp_brush_influence_calc(gso, radius, co);
bool affect_pressure = (brush->flag & GP_EDITBRUSH_FLAG_SMOOTH_PRESSURE) != 0;
-
+ /* need one flag enabled by default */
+ if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION |
+ GP_BRUSHEDIT_FLAG_APPLY_STRENGTH |
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0)
+ {
+ gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION;
+ }
+
/* perform smoothing */
- return gp_smooth_stroke(gps, i, inf, affect_pressure);
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) {
+ gp_smooth_stroke(gps, i, inf, affect_pressure);
+ }
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) {
+ gp_smooth_stroke_strength(gps, i, inf);
+ }
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) {
+ gp_smooth_stroke_thickness(gps, i, inf);
+ }
+
+ return true;
}
/* ----------------------------------------------- */
@@ -268,6 +286,41 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
/* ----------------------------------------------- */
+/* Color Strength Brush */
+
+/* Make color more or less transparent by the specified amounts */
+static bool gp_brush_strength_apply(
+ tGP_BrushEditData *gso, bGPDstroke *gps, int i,
+ const int radius, const int co[2])
+{
+ bGPDspoint *pt = gps->points + i;
+ float inf;
+
+ /* Compute strength of effect
+ * - We divide the strength by 10, so that users can set "sane" values.
+ * Otherwise, good default values are in the range of 0.093
+ */
+ inf = gp_brush_influence_calc(gso, radius, co) / 10.0f;
+
+ /* apply */
+ // XXX: this is much too strong, and it should probably do some smoothing with the surrounding stuff
+ if (gp_brush_invert_check(gso)) {
+ /* make line thinner - reduce stroke pressure */
+ pt->strength -= inf;
+ }
+ else {
+ /* make line thicker - increase stroke pressure */
+ pt->strength += inf;
+ }
+
+ /* Strength should stay within [0.0, 1.0] */
+ CLAMP(pt->strength, 0.0f, 1.0f);
+
+ return true;
+}
+
+
+/* ----------------------------------------------- */
/* Grab Brush */
/* Custom data per stroke for the Grab Brush
@@ -373,11 +426,12 @@ static void gp_brush_grab_calc_dvec(tGP_BrushEditData *gso)
}
/* Apply grab transform to all relevant points of the affected strokes */
-static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps)
+static void gp_brush_grab_apply_cached(
+ tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, float diff_mat[4][4])
{
tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps);
int i;
-
+
/* Apply dvec to all of the stored points */
for (i = 0; i < data->size; i++) {
bGPDspoint *pt = &gps->points[data->points[i]];
@@ -385,9 +439,23 @@ static void gp_brush_grab_apply_cached(tGP_BrushEditData *gso, bGPDstroke *gps)
/* adjust the amount of displacement to apply */
mul_v3_v3fl(delta, gso->dvec, data->weights[i]);
+ if (!parented) {
+ /* apply */
+ add_v3_v3(&pt->x, delta);
+ }
+ else {
+ float fpt[3];
+ /* apply transformation */
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ /* apply */
+ add_v3_v3(fpt, delta);
+ copy_v3_v3(&pt->x, fpt);
+ /* undo transformation to the init parent position */
+ float inverse_diff_mat[4][4];
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+ mul_m4_v3(inverse_diff_mat, &pt->x);
+ }
- /* apply */
- add_v3_v3(&pt->x, delta);
}
}
@@ -592,62 +660,92 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in
*/
const float inf = gp_brush_influence_calc(gso, radius, co) / 2.0f;
const float fac = BLI_frand() * inf;
-
- /* Jitter is applied perpendicular to the mouse movement vector
- * - We compute all effects in screenspace (since it's easier)
- * and then project these to get the points/distances in
- * viewspace as needed
- */
- float mvec[2], svec[2];
-
- /* mouse movement in ints -> floats */
- mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
- mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
-
- /* rotate mvec by 90 degrees... */
- svec[0] = -mvec[1];
- svec[1] = mvec[0];
-
- //printf("svec = %f %f, ", svec[0], svec[1]);
-
- /* scale the displacement by the random displacement, and apply */
- if (BLI_frand() > 0.5f) {
- mul_v2_fl(svec, -fac);
- }
- else {
- mul_v2_fl(svec, fac);
+ /* need one flag enabled by default */
+ if ((gso->settings->flag & (GP_BRUSHEDIT_FLAG_APPLY_POSITION |
+ GP_BRUSHEDIT_FLAG_APPLY_STRENGTH |
+ GP_BRUSHEDIT_FLAG_APPLY_THICKNESS)) == 0)
+ {
+ gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION;
}
-
- //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]);
-
- /* convert to dataspace */
- if (gps->flag & GP_STROKE_3DSPACE) {
- /* 3D: Project to 3D space */
- if (gso->sa->spacetype == SPACE_VIEW3D) {
- bool flip;
- RegionView3D *rv3d = gso->ar->regiondata;
- float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
- if (flip == false) {
- float dvec[3];
- ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac);
- add_v3_v3(&pt->x, dvec);
+
+ /* apply random to position */
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) {
+ /* Jitter is applied perpendicular to the mouse movement vector
+ * - We compute all effects in screenspace (since it's easier)
+ * and then project these to get the points/distances in
+ * viewspace as needed
+ */
+ float mvec[2], svec[2];
+
+ /* mouse movement in ints -> floats */
+ mvec[0] = (float)(gso->mval[0] - gso->mval_prev[0]);
+ mvec[1] = (float)(gso->mval[1] - gso->mval_prev[1]);
+
+ /* rotate mvec by 90 degrees... */
+ svec[0] = -mvec[1];
+ svec[1] = mvec[0];
+
+ /* scale the displacement by the random displacement, and apply */
+ if (BLI_frand() > 0.5f) {
+ mul_v2_fl(svec, -fac);
+ }
+ else {
+ mul_v2_fl(svec, fac);
+ }
+
+ //printf("%f %f (%f), nco = {%f %f}, co = %d %d\n", svec[0], svec[1], fac, nco[0], nco[1], co[0], co[1]);
+
+ /* convert to dataspace */
+ if (gps->flag & GP_STROKE_3DSPACE) {
+ /* 3D: Project to 3D space */
+ if (gso->sa->spacetype == SPACE_VIEW3D) {
+ bool flip;
+ RegionView3D *rv3d = gso->ar->regiondata;
+ float zfac = ED_view3d_calc_zfac(rv3d, &pt->x, &flip);
+ if (flip == false) {
+ float dvec[3];
+ ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac);
+ add_v3_v3(&pt->x, dvec);
+ }
+ }
+ else {
+ /* ERROR */
+ BLI_assert("3D stroke being sculpted in non-3D view");
}
}
else {
- /* ERROR */
- BLI_assert("3D stroke being sculpted in non-3D view");
+ /* 2D: As-is */
+ // XXX: v2d scaling/offset?
+ float nco[2];
+ nco[0] = (float)co[0] + svec[0];
+ nco[1] = (float)co[1] + svec[1];
+
+ copy_v2_v2(&pt->x, nco);
}
}
- else {
- /* 2D: As-is */
- // XXX: v2d scaling/offset?
- float nco[2];
- nco[0] = (float)co[0] + svec[0];
- nco[1] = (float)co[1] + svec[1];
-
- copy_v2_v2(&pt->x, nco);
+ /* apply random to strength */
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) {
+ if (BLI_frand() > 0.5f) {
+ pt->strength += fac;
+ }
+ else {
+ pt->strength -= fac;
+ }
+ CLAMP_MIN(pt->strength, 0.0f);
+ CLAMP_MAX(pt->strength, 1.0f);
}
-
+ /* apply random to thickness (use pressure) */
+ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) {
+ if (BLI_frand() > 0.5f) {
+ pt->pressure += fac;
+ }
+ else {
+ pt->pressure -= fac;
+ }
+ /* only limit lower value */
+ CLAMP_MIN(pt->pressure, 0.0f);
+ }
+
/* done */
return true;
}
@@ -1099,7 +1197,9 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso)
/* Apply ----------------------------------------------- */
/* Apply brush operation to points in this stroke */
-static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP_BrushApplyCb apply)
+static bool gpsculpt_brush_do_stroke(
+ tGP_BrushEditData *gso, bGPDstroke *gps, bool parented,
+ float diff_mat[4][4], GP_BrushApplyCb apply)
{
GP_SpaceConversion *gsc = &gso->gsc;
rcti *rect = &gso->brush_rect;
@@ -1111,9 +1211,16 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP
int i;
bool include_last = false;
bool changed = false;
-
+
if (gps->totpoints == 1) {
- gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]);
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, gps->points, &pc1[0], &pc1[1]);
+ }
+ else {
+ bGPDspoint pt_temp;
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ }
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
@@ -1140,10 +1247,20 @@ static bool gpsculpt_brush_do_stroke(tGP_BrushEditData *gso, bGPDstroke *gps, GP
continue;
}
}
-
- gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]);
- gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]);
-
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, pt1, &pc1[0], &pc1[1]);
+ gp_point_to_xy(gsc, gps, pt2, &pc2[0], &pc2[1]);
+ }
+ else {
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc1[0], &pc1[1]);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &pc2[0], &pc2[1]);
+ }
+
+
/* Check that point segment of the boundbox of the selection stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1])))
@@ -1228,76 +1345,105 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso)
/* Find visible strokes, and perform operations on those if hit */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ float diff_mat[4][4];
+ bool parented = false;
+
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
{
- switch (gso->brush_type) {
- case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_smooth_apply);
- break;
- }
-
- case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_thickness_apply);
- break;
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ parented = true;
+ }
+ else {
+ parented = false;
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
}
-
- case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */
- {
- if (gso->first) {
- /* First time this brush stroke is being applied:
- * 1) Prepare data buffers (init/clear) for this stroke
- * 2) Use the points now under the cursor
- */
- gp_brush_grab_stroke_init(gso, gps);
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_grab_store_points);
+
+ switch (gso->brush_type) {
+ case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_smooth_apply);
+ break;
}
- else {
- /* Apply effect to the stored points */
- gp_brush_grab_apply_cached(gso, gps);
- changed |= true;
+
+ case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_apply);
+ break;
}
- break;
- }
-
- case GP_EDITBRUSH_TYPE_PUSH: /* Push points */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_push_apply);
- break;
- }
-
- case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_pinch_apply);
- break;
- }
-
- case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_twist_apply);
- break;
+
+ case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_strength_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_GRAB: /* Grab points */
+ {
+ if (gso->first) {
+ /* First time this brush stroke is being applied:
+ * 1) Prepare data buffers (init/clear) for this stroke
+ * 2) Use the points now under the cursor
+ */
+ gp_brush_grab_stroke_init(gso, gps);
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_grab_store_points);
+ }
+ else {
+ /* Apply effect to the stored points */
+ gp_brush_grab_apply_cached(gso, gps, parented, diff_mat);
+ changed |= true;
+ }
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_PUSH: /* Push points */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_pinch_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_twist_apply);
+ break;
+ }
+
+ case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */
+ {
+ changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_randomize_apply);
+ break;
+ }
+
+ default:
+ printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
+ break;
}
-
- case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */
- {
- changed |= gpsculpt_brush_do_stroke(gso, gps, gp_brush_randomize_apply);
- break;
+ /* Triangulation must be calculated if changed */
+ if (changed) {
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+ gps->tot_triangles = 0;
}
-
- default:
- printf("ERROR: Unknown type of GPencil Sculpt brush - %u\n", gso->brush_type);
- break;
- }
-
- /* Triangulation must be calculated if changed */
- if (changed) {
- gps->flag |= GP_STROKE_RECALC_CACHES;
- gps->tot_triangles = 0;
}
}
CTX_DATA_END;
-
+
return changed;
}
@@ -1441,6 +1587,11 @@ static int gpsculpt_brush_invoke(bContext *C, wmOperator *op, const wmEvent *eve
needs_timer = true;
break;
+ case GP_EDITBRUSH_TYPE_STRENGTH:
+ brush_rate = 0.01f; // XXX: hardcoded
+ needs_timer = true;
+ break;
+
case GP_EDITBRUSH_TYPE_PINCH:
brush_rate = 0.001f; // XXX: hardcoded
needs_timer = true;
diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c
index c47985ebc1b..95ea13c399a 100644
--- a/source/blender/editors/gpencil/gpencil_convert.c
+++ b/source/blender/editors/gpencil/gpencil_convert.c
@@ -142,12 +142,31 @@ static EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), PointerRN
/* convert the coordinates from the given stroke point into 3d-coordinates
* - assumes that the active space is the 3D-View
*/
-static void gp_strokepoint_convertcoords(bContext *C, bGPDstroke *gps, bGPDspoint *pt, float p3d[3], rctf *subrect)
+static void gp_strokepoint_convertcoords(
+ bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt,
+ float p3d[3], const rctf *subrect)
{
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
ARegion *ar = CTX_wm_region(C);
-
+ bGPDspoint mypt, *pt;
+
+ float diff_mat[4][4];
+ pt = &mypt;
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent == NULL) {
+ copy_v3_v3(&pt->x, &source_pt->x);
+ }
+ else {
+ /* apply parent transform */
+ float fpt[3];
+ ED_gpencil_parent_location(gpl, diff_mat);
+ mul_v3_m4v3(fpt, diff_mat, &source_pt->x);
+ copy_v3_v3(&pt->x, fpt);
+ }
+
+
if (gps->flag & GP_STROKE_3DSPACE) {
/* directly use 3d-coordinates */
copy_v3_v3(p3d, &pt->x);
@@ -628,7 +647,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
bp = &nu->bp[old_nbp - 1];
/* First point */
- gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
if (prev_bp) {
interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC);
if (do_gtd) {
@@ -649,7 +668,7 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
/* Second point */
/* Note dt2 is always negative, which marks the gap. */
if (gps->totpoints > 1) {
- gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
interp_v3_v3v3(p2, p, next_p, -GAP_DFAC);
if (do_gtd) {
dt2 = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
@@ -670,9 +689,9 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
float p[3], next_p[3];
float dt = 0.0f;
- gp_strokepoint_convertcoords(C, gps, gps->points, p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points, p, subrect);
if (gps->totpoints > 1) {
- gp_strokepoint_convertcoords(C, gps, gps->points + 1, next_p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect);
interp_v3_v3v3(p, p, next_p, -GAP_DFAC);
if (do_gtd) {
dt = interpf(gps->points[1].time, gps->points[0].time, -GAP_DFAC);
@@ -701,10 +720,10 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv
i++, pt++, bp++)
{
float p[3];
- float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
+ float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC;
/* get coordinates to add at */
- gp_strokepoint_convertcoords(C, gps, pt, p, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect);
gp_stroke_to_path_add_point(gtd, bp, p, (prev_bp) ? prev_bp->vec : p, do_gtd, gps->inittime, pt->time,
width, rad_fac, minmax_weights);
@@ -816,12 +835,12 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu
/* get initial coordinates */
pt = gps->points;
if (tot) {
- gp_strokepoint_convertcoords(C, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect);
if (tot > 1) {
- gp_strokepoint_convertcoords(C, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect);
}
if (stitch && tot > 2) {
- gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
}
}
@@ -940,7 +959,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu
/* add points */
for (i = stitch ? 1 : 0, bezt = &nu->bezt[old_nbezt]; i < tot; i++, pt++, bezt++) {
- float width = pt->pressure * gpl->thickness * WIDTH_CORR_FAC;
+ float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC;
if (i || old_nbezt) {
interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC);
@@ -964,7 +983,7 @@ static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Cu
copy_v3_v3(p3d_cur, p3d_next);
if (i + 2 < tot) {
- gp_strokepoint_convertcoords(C, gps, pt + 2, p3d_next, subrect);
+ gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect);
}
prev_bezt = bezt;
diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c
index 746497f0ff5..e915446e461 100644
--- a/source/blender/editors/gpencil/gpencil_data.c
+++ b/source/blender/editors/gpencil/gpencil_data.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*
@@ -40,6 +40,8 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+#include "BLI_math.h"
#include "BLT_translation.h"
@@ -57,6 +59,7 @@
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
+#include "BKE_colortools.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -72,6 +75,8 @@
#include "gpencil_intern.h"
+/* maximum sizes of gp-session buffer */
+#define GP_STROKE_BUFFER_MAX 5000
/* ************************************************ */
/* Datablock Operators */
@@ -596,6 +601,61 @@ void GPENCIL_OT_layer_isolate(wmOperatorType *ot)
"In addition to toggling the editability, also affect the visibility");
}
+/* ********************** Merge Layer with the next layer **************************** */
+
+static int gp_merge_layer_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl_current = gpencil_layer_getactive(gpd);
+ bGPDlayer *gpl_next = gpl_current->next;
+
+ if (ELEM(NULL, gpd, gpl_current, gpl_next)) {
+ BKE_report(op->reports, RPT_ERROR, "No layers to merge");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Collect frames of gpl_current in hash table to avoid O(n^2) lookups */
+ GHash *gh_frames_cur = BLI_ghash_int_new_ex(__func__, 64);
+ for (bGPDframe *gpf = gpl_current->frames.first; gpf; gpf = gpf->next) {
+ BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf);
+ }
+
+ /* read all frames from next layer */
+ for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) {
+ /* try to find frame in active layer */
+ bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum));
+ if (!frame) {
+ /* nothing found, create new */
+ frame = gpencil_frame_addnew(gpl_current, gpf->framenum);
+ }
+ /* add to tail all strokes */
+ BLI_movelisttolist(&frame->strokes, &gpf->strokes);
+ }
+ /* Now delete next layer */
+ gpencil_layer_delete(gpd, gpl_next);
+ BLI_ghash_free(gh_frames_cur, NULL, NULL);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_layer_merge(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Merge Down";
+ ot->idname = "GPENCIL_OT_layer_merge";
+ ot->description = "Merge the current layer with the layer below";
+
+ /* callbacks */
+ ot->exec = gp_merge_layer_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/* ********************** Change Layer ***************************** */
static int gp_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
@@ -663,3 +723,1740 @@ void GPENCIL_OT_layer_change(wmOperatorType *ot)
}
/* ************************************************ */
+
+/* ******************* Arrange Stroke Up/Down in drawing order ************************** */
+
+enum {
+ GP_STROKE_MOVE_UP = -1,
+ GP_STROKE_MOVE_DOWN = 1,
+ GP_STROKE_MOVE_TOP = 2,
+ GP_STROKE_MOVE_BOTTOM = 3
+};
+
+static int gp_stroke_arrange_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+ bGPDstroke *gps;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl, gpl->actframe)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ /* temp listbase to store selected strokes */
+ ListBase selected = {NULL};
+ const int direction = RNA_enum_get(op->ptr, "type");
+
+ /* verify if any selected stroke is in the extreme of the stack and select to move */
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* only if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+ /* some stroke is already at front*/
+ if ((direction == GP_STROKE_MOVE_TOP) || (direction == GP_STROKE_MOVE_UP)) {
+ if (gps == gpf->strokes.last) {
+ BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on top");
+ return OPERATOR_CANCELLED;
+ }
+ }
+ /* some stroke is already at botom */
+ if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) {
+ if (gps == gpf->strokes.first) {
+ BKE_report(op->reports, RPT_ERROR, "Some selected stroke is already on bottom");
+ return OPERATOR_CANCELLED;
+ }
+ }
+ /* add to list */
+ BLI_addtail(&selected, BLI_genericNodeN(gps));
+ }
+ }
+
+ /* Now do the movement of the stroke */
+ switch (direction) {
+ /* Bring to Front */
+ case GP_STROKE_MOVE_TOP:
+ for (LinkData *link = selected.first; link; link = link->next) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkafter(&gpf->strokes, gpf->strokes.last, gps);
+ }
+ break;
+ /* Bring Forward */
+ case GP_STROKE_MOVE_UP:
+ for (LinkData *link = selected.last; link; link = link->prev) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkafter(&gpf->strokes, gps->next, gps);
+ }
+ break;
+ /* Send Backward */
+ case GP_STROKE_MOVE_DOWN:
+ for (LinkData *link = selected.first; link; link = link->next) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkbefore(&gpf->strokes, gps->prev, gps);
+ }
+ break;
+ /* Send to Back */
+ case GP_STROKE_MOVE_BOTTOM:
+ for (LinkData *link = selected.last; link; link = link->prev) {
+ gps = link->data;
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps);
+ }
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_arrange(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {GP_STROKE_MOVE_UP, "UP", 0, "Bring Forward", ""},
+ {GP_STROKE_MOVE_DOWN, "DOWN", 0, "Send Backward", ""},
+ {GP_STROKE_MOVE_TOP, "TOP", 0, "Bring to Front", ""},
+ {GP_STROKE_MOVE_BOTTOM, "BOTTOM", 0, "Send to Back", ""},
+ {0, NULL, 0, NULL, NULL }
+ };
+
+ /* identifiers */
+ ot->name = "Arrange Stroke";
+ ot->idname = "GPENCIL_OT_stroke_arrange";
+ ot->description = "Arrange selected strokes up/down in the drawing order of the active layer";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_arrange_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_STROKE_MOVE_UP, "Direction", "");
+}
+/* ******************* Move Stroke to new color ************************** */
+
+static int gp_stroke_change_color_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette;
+ bGPDpalettecolor *color;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ palette = gpencil_palette_getactive(gpd);
+ color = gpencil_palettecolor_getactive(palette);
+ if (ELEM(NULL, palette, color)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* loop all strokes */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ /* only if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false)
+ continue;
+
+ /* asign new color (only if different) */
+ if (STREQ(gps->colorname, color->info) == false) {
+ strcpy(gps->colorname, color->info);
+ gps->flag |= GP_STROKE_RECALC_COLOR;
+ }
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_change_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Change Stroke Color";
+ ot->idname = "GPENCIL_OT_stroke_change_color";
+ ot->description = "Move selected strokes to active color";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_change_color_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ******************* Lock color of non selected Strokes colors ************************** */
+
+static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ palette = gpencil_palette_getactive(gpd);
+ if (ELEM(NULL, palette))
+ return OPERATOR_CANCELLED;
+
+ /* first lock all colors */
+ for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag |= PC_COLOR_LOCKED;
+ }
+
+ /* loop all selected strokes and unlock any color */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ /* only if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* unlock color */
+ if (gps->palcolor != NULL) {
+ gps->palcolor->flag &= ~PC_COLOR_LOCKED;
+ }
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Lock Unused Colors";
+ ot->idname = "GPENCIL_OT_stroke_lock_color";
+ ot->description = "Lock any color not used in any selected stroke";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_lock_color_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ******************* Apply layer thickness change to Strokes ************************** */
+
+static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = gpencil_layer_getactive(gpd);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, gpl, gpl->frames.first))
+ return OPERATOR_CANCELLED;
+
+ /* loop all strokes */
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* Apply thickness */
+ gps->thickness = gps->thickness + gpl->thickness;
+ }
+ }
+ /* clear value */
+ gpl->thickness = 0.0f;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Apply Stroke Thickness";
+ ot->idname = "GPENCIL_OT_stroke_apply_thickness";
+ ot->description = "Apply the thickness change of the layer to its strokes";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_apply_thickness_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ******************* Close Strokes ************************** */
+
+enum {
+ GP_STROKE_CYCLIC_CLOSE = 1,
+ GP_STROKE_CYCLIC_OPEN = 2,
+ GP_STROKE_CYCLIC_TOGGLE = 3
+};
+
+static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const int type = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* loop all selected strokes */
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+ {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ bGPDpalettecolor *palcolor = gps->palcolor;
+
+ /* skip strokes that are not selected or invalid for current view */
+ if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* skip hidden or locked colors */
+ if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
+ continue;
+
+ switch (type) {
+ case GP_STROKE_CYCLIC_CLOSE:
+ /* Close all (enable) */
+ gps->flag |= GP_STROKE_CYCLIC;
+ break;
+ case GP_STROKE_CYCLIC_OPEN:
+ /* Open all (disable) */
+ gps->flag &= ~GP_STROKE_CYCLIC;
+ break;
+ case GP_STROKE_CYCLIC_TOGGLE:
+ /* Just toggle flag... */
+ gps->flag ^= GP_STROKE_CYCLIC;
+ break;
+ default:
+ BLI_assert(0);
+ break;
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+/**
+ * Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
+ * option to force opened/closed strokes instead of just toggle behavior.
+ */
+void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
+{
+ static EnumPropertyItem cyclic_type[] = {
+ {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
+ {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
+ {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Set Cyclical State";
+ ot->idname = "GPENCIL_OT_stroke_cyclical_set";
+ ot->description = "Close or open the selected stroke adding an edge from last to first point";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_cyclical_set_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
+}
+
+/* ******************* Stroke join ************************** */
+
+/* Helper: flip stroke */
+static void gpencil_flip_stroke(bGPDstroke *gps)
+{
+ bGPDspoint pt, *point, *point2;
+ int end = gps->totpoints - 1;
+
+ for (int i = 0; i < gps->totpoints / 2; i++) {
+ /* save first point */
+ point = &gps->points[i];
+ pt.x = point->x;
+ pt.y = point->y;
+ pt.z = point->z;
+ pt.flag = point->flag;
+ pt.pressure = point->pressure;
+ pt.strength = point->strength;
+ pt.time = point->time;
+
+ /* replace first point with last point */
+ point2 = &gps->points[end];
+ point->x = point2->x;
+ point->y = point2->y;
+ point->z = point2->z;
+ point->flag = point2->flag;
+ point->pressure = point2->pressure;
+ point->strength = point2->strength;
+ point->time = point2->time;
+
+ /* replace last point with first saved before */
+ point = &gps->points[end];
+ point->x = pt.x;
+ point->y = pt.y;
+ point->z = pt.z;
+ point->flag = pt.flag;
+ point->pressure = pt.pressure;
+ point->strength = pt.strength;
+ point->time = pt.time;
+
+ end--;
+ }
+}
+
+/* Helper: copy point between strokes */
+static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
+ float pressure, float strength, float deltatime)
+{
+ bGPDspoint *newpoint;
+
+ gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
+ gps->totpoints++;
+
+ newpoint = &gps->points[gps->totpoints - 1];
+ newpoint->x = point->x * delta[0];
+ newpoint->y = point->y * delta[1];
+ newpoint->z = point->z * delta[2];
+ newpoint->flag = point->flag;
+ newpoint->pressure = pressure;
+ newpoint->strength = strength;
+ newpoint->time = point->time + deltatime;
+}
+
+/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
+static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b)
+{
+ bGPDspoint point, *pt;
+ int i;
+ float delta[3] = {1.0f, 1.0f, 1.0f};
+ float deltatime = 0.0f;
+
+ /* sanity checks */
+ if (ELEM(NULL, gps_a, gps_b))
+ return;
+
+ if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
+ return;
+
+ /* define start and end points of each stroke */
+ float sa[3], sb[3], ea[3], eb[3];
+ pt = &gps_a->points[0];
+ copy_v3_v3(sa, &pt->x);
+
+ pt = &gps_a->points[gps_a->totpoints - 1];
+ copy_v3_v3(ea, &pt->x);
+
+ pt = &gps_b->points[0];
+ copy_v3_v3(sb, &pt->x);
+
+ pt = &gps_b->points[gps_b->totpoints - 1];
+ copy_v3_v3(eb, &pt->x);
+ /* review if need flip stroke B */
+ float ea_sb = len_squared_v3v3(ea, sb);
+ float ea_eb = len_squared_v3v3(ea, eb);
+ /* flip if distance to end point is shorter */
+ if (ea_eb < ea_sb) {
+ gpencil_flip_stroke(gps_b);
+ }
+
+ /* 1st: add one tail point to start invisible area */
+ point = gps_a->points[gps_a->totpoints - 1];
+ deltatime = point.time;
+ gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
+
+ /* 2nd: add one head point to finish invisible area */
+ point = gps_b->points[0];
+ gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
+
+ /* 3rd: add all points */
+ for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
+ /* check if still room in buffer */
+ if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
+ gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
+ }
+ }
+}
+
+enum {
+ GP_STROKE_JOIN = -1,
+ GP_STROKE_JOINCOPY = 1
+};
+
+static int gp_stroke_join_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *activegpl = gpencil_layer_getactive(gpd);
+ bGPDstroke *gps, *gpsn;
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+
+ bGPDframe *gpf_a = NULL;
+ bGPDstroke *stroke_a = NULL;
+ bGPDstroke *stroke_b = NULL;
+ bGPDstroke *new_stroke = NULL;
+
+ int type = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ if (activegpl->flag & GP_LAYER_LOCKED)
+ return OPERATOR_CANCELLED;
+
+ BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
+
+
+ /* read all selected strokes */
+ bool first = false;
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+ {
+ bGPDframe *gpf = gpl->actframe;
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+ /* to join strokes, cyclic must be disabled */
+ gps->flag &= ~GP_STROKE_CYCLIC;
+ /* saves first frame and stroke */
+ if (!first) {
+ first = true;
+ gpf_a = gpf;
+ stroke_a = gps;
+ }
+ else {
+ stroke_b = gps;
+ /* create a new stroke if was not created before (only created if something to join) */
+ if (new_stroke == NULL) {
+ new_stroke = MEM_dupallocN(stroke_a);
+ new_stroke->points = MEM_dupallocN(stroke_a->points);
+ new_stroke->triangles = NULL;
+ new_stroke->tot_triangles = 0;
+ new_stroke->flag |= GP_STROKE_RECALC_CACHES;
+ /* if new, set current color */
+ if (type == GP_STROKE_JOINCOPY) {
+ new_stroke->palcolor = palcolor;
+ strcpy(new_stroke->colorname, palcolor->info);
+ new_stroke->flag |= GP_STROKE_RECALC_COLOR;
+ }
+ }
+ /* join new_stroke and stroke B. New stroke will contain all the previous data */
+ gpencil_stroke_join_strokes(new_stroke, stroke_b);
+
+ /* if join only, delete old strokes */
+ if (type == GP_STROKE_JOIN) {
+ if (stroke_a) {
+ BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
+ BLI_remlink(&gpf->strokes, stroke_a);
+ free_gpencil_stroke(stroke_a);
+ stroke_a = NULL;
+ }
+ if (stroke_b) {
+ BLI_remlink(&gpf->strokes, stroke_b);
+ free_gpencil_stroke(stroke_b);
+ stroke_b = NULL;
+ }
+ }
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+ /* add new stroke if was not added before */
+ if (type == GP_STROKE_JOINCOPY) {
+ if (new_stroke) {
+ /* Add a new frame if needed */
+ if (activegpl->actframe == NULL)
+ activegpl->actframe = gpencil_frame_addnew(activegpl, gpf_a->framenum);
+
+ BLI_addtail(&activegpl->actframe->strokes, new_stroke);
+ }
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_join(wmOperatorType *ot)
+{
+ static EnumPropertyItem join_type[] = {
+ {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
+ {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Join Strokes";
+ ot->idname = "GPENCIL_OT_stroke_join";
+ ot->description = "Join selected strokes (optionally as new stroke)";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_join_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
+}
+
+/* ******************* Stroke flip ************************** */
+
+static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* read all selected strokes */
+ CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
+ {
+ bGPDframe *gpf = gpl->actframe;
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+ /* flip stroke */
+ gpencil_flip_stroke(gps);
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Flip Stroke";
+ ot->idname = "GPENCIL_OT_stroke_flip";
+ ot->description = "Change drawing direction of selected strokes";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_flip_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ************************************************ */
+/* Drawing Brushes Operators */
+
+/* ******************* Add New Brush ************************ */
+
+/* add new brush - wrapper around API */
+static int gp_brush_add_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ /* if there's no existing container */
+ if (ts == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
+ return OPERATOR_CANCELLED;
+ }
+ /* add new brush now */
+ gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Brush";
+ ot->idname = "GPENCIL_OT_brush_add";
+ ot->description = "Add new Grease Pencil drawing brush for the active Grease Pencil datablock";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_brush_add_exec;
+ ot->poll = gp_add_poll;
+}
+
+/* ******************* Remove Active Brush ************************* */
+
+static int gp_brush_remove_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = gpencil_brush_getactive(ts);
+
+ /* sanity checks */
+ if (ELEM(NULL, ts, brush))
+ return OPERATOR_CANCELLED;
+
+ if (BLI_listbase_count(&ts->gp_brushes) < 2) {
+ BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush. Unable to delete brush");
+ return OPERATOR_CANCELLED;
+ }
+
+
+ /* make the brush before this the new active brush
+ * - use the one after if this is the first
+ * - if this is the only brush, this naturally becomes NULL
+ */
+ if (brush->prev)
+ gpencil_brush_setactive(ts, brush->prev);
+ else
+ gpencil_brush_setactive(ts, brush->next);
+
+ /* delete the brush now... */
+ gpencil_brush_delete(ts, brush);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove brush";
+ ot->idname = "GPENCIL_OT_brush_remove";
+ ot->description = "Remove active Grease Pencil drawing brush";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_brush_remove_exec;
+ ot->poll = gp_active_brush_poll;
+}
+
+/* ********************** Change Brush ***************************** */
+
+static int gp_brush_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
+{
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ /* call the menu, which will call this operator again, hence the canceled */
+ pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
+ layout = UI_popup_menu_layout(pup);
+ uiItemsEnumO(layout, "GPENCIL_OT_brush_change", "brush");
+ UI_popup_menu_end(C, pup);
+
+ return OPERATOR_INTERFACE;
+}
+
+static int gp_brush_change_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = NULL;
+ int brush_num = RNA_enum_get(op->ptr, "brush");
+
+ /* Get brush or create new one */
+ if (brush_num == -1) {
+ /* Create brush */
+ brush = gpencil_brush_addnew(ts, DATA_("GP_Brush"), true);
+ }
+ else {
+ /* Try to get brush */
+ brush = BLI_findlink(&ts->gp_brushes, brush_num);
+
+ if (brush == NULL) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent brush (index = %d)", brush_num);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* Set active brush */
+ gpencil_brush_setactive(ts, brush);
+
+ /* updates */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_change(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Change Brush";
+ ot->idname = "GPENCIL_OT_brush_change";
+ ot->description = "Change active Grease Pencil drawing brush";
+
+ /* callbacks */
+ ot->invoke = gp_brush_change_invoke;
+ ot->exec = gp_brush_change_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* gp brush to use (dynamic enum) */
+ ot->prop = RNA_def_enum(ot->srna, "brush", DummyRNA_DEFAULT_items, 0, "Grease Pencil Brush", "");
+ RNA_def_enum_funcs(ot->prop, ED_gpencil_brushes_enum_itemf);
+}
+
+/* ******************* Move Brush Up/Down ************************** */
+
+enum {
+ GP_BRUSH_MOVE_UP = -1,
+ GP_BRUSH_MOVE_DOWN = 1
+};
+
+static int gp_brush_move_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = gpencil_brush_getactive(ts);
+
+ int direction = RNA_enum_get(op->ptr, "type");
+
+ /* sanity checks */
+ if (ELEM(NULL, ts, brush)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ /* up or down? */
+ if (direction == GP_BRUSH_MOVE_UP) {
+ /* up */
+ BLI_remlink(&ts->gp_brushes, brush);
+ BLI_insertlinkbefore(&ts->gp_brushes, brush->prev, brush);
+ }
+ else if (direction == GP_BRUSH_MOVE_DOWN) {
+ /* down */
+ BLI_remlink(&ts->gp_brushes, brush);
+ BLI_insertlinkafter(&ts->gp_brushes, brush->next, brush);
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {GP_BRUSH_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_BRUSH_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL }
+ };
+
+ /* identifiers */
+ ot->name = "Move Brush";
+ ot->idname = "GPENCIL_OT_brush_move";
+ ot->description = "Move the active Grease Pencil drawing brush up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gp_brush_move_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", slot_move, GP_BRUSH_MOVE_UP, "Type", "");
+}
+
+/* ******************* Brush create presets ************************** */
+
+static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ gpencil_brush_init_presets(ts);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_presets_create(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Create Preset Brushes";
+ ot->idname = "GPENCIL_OT_brush_presets_create";
+ ot->description = "Create a set of predefined Grease Pencil drawing brushes";
+
+ /* api callbacks */
+ ot->exec = gp_brush_presets_create_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+}
+
+/* ***************** Copy Brush ************************ */
+
+static int gp_brush_copy_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ /* if there's no existing container */
+ if (ts == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere for brush data to go");
+ return OPERATOR_CANCELLED;
+ }
+
+ bGPDbrush *brush = gpencil_brush_getactive(ts);
+ bGPDbrush *newbrush;
+
+ /* sanity checks */
+ if (ELEM(NULL, brush))
+ return OPERATOR_CANCELLED;
+
+ /* create a brush and duplicate data */
+ newbrush = gpencil_brush_addnew(ts, brush->info, true);
+ newbrush->thickness = brush->thickness;
+ newbrush->draw_smoothfac = brush->draw_smoothfac;
+ newbrush->draw_smoothlvl = brush->draw_smoothlvl;
+ newbrush->sublevel = brush->sublevel;
+ newbrush->flag = brush->flag;
+ newbrush->draw_sensitivity = brush->draw_sensitivity;
+ newbrush->draw_strength = brush->draw_strength;
+ newbrush->draw_jitter = brush->draw_jitter;
+ newbrush->draw_angle = brush->draw_angle;
+ newbrush->draw_angle_factor = brush->draw_angle_factor;
+ newbrush->draw_random_press = brush->draw_random_press;
+ newbrush->draw_random_sub = brush->draw_random_sub;
+
+ /* free automatic curves created by default (replaced by copy) */
+ curvemapping_free(newbrush->cur_sensitivity);
+ curvemapping_free(newbrush->cur_strength);
+ curvemapping_free(newbrush->cur_jitter);
+
+ /* make a copy of curves */
+ newbrush->cur_sensitivity = curvemapping_copy(brush->cur_sensitivity);
+ newbrush->cur_strength = curvemapping_copy(brush->cur_strength);
+ newbrush->cur_jitter = curvemapping_copy(brush->cur_jitter);
+
+ gpencil_brush_setactive(ts, newbrush);
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Brush";
+ ot->idname = "GPENCIL_OT_brush_copy";
+ ot->description = "Copy current Grease Pencil drawing brush";
+
+ /* callbacks */
+ ot->exec = gp_brush_copy_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************** Select Brush ************************ */
+
+static int gp_brush_select_exec(bContext *C, wmOperator *op)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ /* if there's no existing container */
+ if (ts == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere to go");
+ return OPERATOR_CANCELLED;
+ }
+
+ const int index = RNA_int_get(op->ptr, "index");
+ bGPDbrush *brush = BLI_findlink(&ts->gp_brushes, index);
+ /* sanity checks */
+ if (ELEM(NULL, brush)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ gpencil_brush_setactive(ts, brush);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_brush_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Brush";
+ ot->idname = "GPENCIL_OT_brush_select";
+ ot->description = "Select a Grease Pencil drawing brush";
+
+ /* callbacks */
+ ot->exec = gp_brush_select_exec;
+ ot->poll = gp_active_brush_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX);
+}
+
+/* ************************************************ */
+/* Palette Operators */
+
+/* ******************* Add New Palette ************************ */
+
+/* add new palette - wrapper around API */
+static int gp_palette_add_exec(bContext *C, wmOperator *op)
+{
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
+
+ /* if there's no existing Grease-Pencil data there, add some */
+ if (gpd_ptr == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
+ return OPERATOR_CANCELLED;
+ }
+ if (*gpd_ptr == NULL)
+ *gpd_ptr = gpencil_data_addnew(DATA_("GPencil"));
+
+ /* add new palette now */
+ gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Palette";
+ ot->idname = "GPENCIL_OT_palette_add";
+ ot->description = "Add new Grease Pencil palette for the active Grease Pencil datablock";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palette_add_exec;
+ ot->poll = gp_add_poll;
+}
+
+/* ******************* Remove Active Palette ************************* */
+
+static int gp_palette_remove_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ if (BLI_listbase_count(&gpd->palettes) < 2) {
+ BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette. Unable to delete palette");
+ return OPERATOR_CANCELLED;
+ }
+
+
+ /* make the palette before this the new active palette
+ * - use the one after if this is the first
+ * - if this is the only palette, this naturally becomes NULL
+ */
+ if (palette->prev)
+ gpencil_palette_setactive(gpd, palette->prev);
+ else
+ gpencil_palette_setactive(gpd, palette->next);
+
+ /* delete the palette now... */
+ gpencil_palette_delete(gpd, palette);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove palette";
+ ot->idname = "GPENCIL_OT_palette_remove";
+ ot->description = "Remove active Grease Pencil palette";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palette_remove_exec;
+ ot->poll = gp_active_palette_poll;
+}
+
+/* ********************** Change Palette ***************************** */
+
+static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
+{
+ uiPopupMenu *pup;
+ uiLayout *layout;
+
+ /* call the menu, which will call this operator again, hence the canceled */
+ pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
+ layout = UI_popup_menu_layout(pup);
+ uiItemsEnumO(layout, "GPENCIL_OT_palette_change", "palette");
+ UI_popup_menu_end(C, pup);
+
+ return OPERATOR_INTERFACE;
+}
+
+static int gp_palette_change_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ bGPDpalette *palette = NULL;
+ int palette_num = RNA_enum_get(op->ptr, "palette");
+
+ /* Get palette or create new one */
+ if (palette_num == -1) {
+ /* Create palette */
+ palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
+ }
+ else {
+ /* Try to get palette */
+ palette = BLI_findlink(&gpd->palettes, palette_num);
+
+ if (palette == NULL) {
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot change to non-existent palette (index = %d)", palette_num);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* Set active palette */
+ gpencil_palette_setactive(gpd, palette);
+
+ /* updates */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_change(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Change Palette";
+ ot->idname = "GPENCIL_OT_palette_change";
+ ot->description = "Change active Grease Pencil palette";
+
+ /* callbacks */
+ ot->invoke = gp_palette_change_invoke;
+ ot->exec = gp_palette_change_exec;
+ ot->poll = gp_active_palette_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* gp palette to use (dynamic enum) */
+ ot->prop = RNA_def_enum(ot->srna, "palette", DummyRNA_DEFAULT_items, 0, "Grease Pencil Palette", "");
+ RNA_def_enum_funcs(ot->prop, ED_gpencil_palettes_enum_itemf);
+}
+
+/* ******************* Lock and hide any color non used in current layer ************************** */
+
+static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ palette = gpencil_palette_getactive(gpd);
+ if (ELEM(NULL, palette))
+ return OPERATOR_CANCELLED;
+
+ /* first lock and hide all colors */
+ for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag |= PC_COLOR_LOCKED;
+ palcolor->flag |= PC_COLOR_HIDE;
+ }
+
+ /* loop all selected strokes and unlock any color used in active layer */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL) && (gpl->flag & GP_LAYER_ACTIVE)) {
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+
+ /* unlock/unhide color if not unlocked before */
+ if (gps->palcolor != NULL) {
+ gps->palcolor->flag &= ~PC_COLOR_LOCKED;
+ gps->palcolor->flag &= ~PC_COLOR_HIDE;
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Disable Unused Layer Colors";
+ ot->idname = "GPENCIL_OT_palette_lock_layer";
+ ot->description = "Lock and hide any color not used in any layer";
+
+ /* api callbacks */
+ ot->exec = gp_palette_lock_layer_exec;
+ ot->poll = gp_active_layer_poll;
+}
+
+/* ************************************************ */
+/* Palette Colors Operators */
+
+/* ******************* Add New Palette ************************ */
+
+/* add new palette - wrapper around API */
+static int gp_palettecolor_add_exec(bContext *C, wmOperator *op)
+{
+ bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL);
+
+ /* if there's no existing Grease-Pencil data there, add some */
+ if (gpd_ptr == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go");
+ return OPERATOR_CANCELLED;
+ }
+ if (*gpd_ptr == NULL)
+ *gpd_ptr = gpencil_data_addnew(DATA_("GPencil"));
+
+ /* verify palette */
+ bGPDpalette *palette = gpencil_palette_getactive(*gpd_ptr);
+ if (palette == NULL)
+ palette = gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true);
+
+ /* add new palette color now */
+ gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Palette Color";
+ ot->idname = "GPENCIL_OT_palettecolor_add";
+ ot->description = "Add new Grease Pencil palette color for the active Grease Pencil datablock";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_add_exec;
+ ot->poll = gp_add_poll;
+}
+
+/* ******************* Remove Active Palette color ************************* */
+
+static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *color = gpencil_palettecolor_getactive(palette);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, color))
+ return OPERATOR_CANCELLED;
+
+ /* make the palette color before this the new active color
+ * - use the one after if this is the first
+ * - if this is the only color, this naturally becomes NULL
+ */
+ if (color->prev)
+ gpencil_palettecolor_setactive(palette, color->prev);
+ else
+ gpencil_palettecolor_setactive(palette, color->next);
+
+ /* delete the strokes */
+ gpencil_palettecolor_delete_strokes(gpd, color->info);
+
+ /* delete the palette color now... */
+ gpencil_palettecolor_delete(palette, color);
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove palette color";
+ ot->idname = "GPENCIL_OT_palettecolor_remove";
+ ot->description = "Remove active Grease Pencil palette color";
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_remove_exec;
+ ot->poll = gp_active_palettecolor_poll;
+}
+
+/* ********************** Isolate palette color **************************** */
+
+static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *active_color = gpencil_palettecolor_getactive(palette);
+ bGPDpalettecolor *palcolor;
+
+ int flags = PC_COLOR_LOCKED;
+ bool isolate = false;
+
+ if (RNA_boolean_get(op->ptr, "affect_visibility"))
+ flags |= PC_COLOR_HIDE;
+
+ if (ELEM(NULL, gpd, active_color)) {
+ BKE_report(op->reports, RPT_ERROR, "No active color to isolate");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Test whether to isolate or clear all flags */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ /* Skip if this is the active one */
+ if (palcolor == active_color)
+ continue;
+
+ /* If the flags aren't set, that means that the color is
+ * not alone, so we have some colors to isolate still
+ */
+ if ((palcolor->flag & flags) == 0) {
+ isolate = true;
+ break;
+ }
+ }
+
+ /* Set/Clear flags as appropriate */
+ if (isolate) {
+ /* Set flags on all "other" colors */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ if (palcolor == active_color)
+ continue;
+ else
+ palcolor->flag |= flags;
+ }
+ }
+ else {
+ /* Clear flags - Restore everything else */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag &= ~flags;
+ }
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Isolate Palette Color";
+ ot->idname = "GPENCIL_OT_palettecolor_isolate";
+ ot->description = "Toggle whether the active color is the only one that is editable and/or visible";
+
+ /* callbacks */
+ ot->exec = gp_isolate_palettecolor_exec;
+ ot->poll = gp_active_palettecolor_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "affect_visibility", false, "Affect Visibility", "In addition to toggling "
+ "the editability, also affect the visibility");
+}
+
+/* *********************** Hide Palette colors ******************************** */
+
+static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+
+ bool unselected = RNA_boolean_get(op->ptr, "unselected");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ if (unselected) {
+ bGPDpalettecolor *color;
+
+ /* hide unselected */
+ for (color = palette->colors.first; color; color = color->next) {
+ if (color != palcolor) {
+ color->flag |= PC_COLOR_HIDE;
+ }
+ }
+ }
+ else {
+ /* hide selected/active */
+ palcolor->flag |= PC_COLOR_HIDE;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hide Color(s)";
+ ot->idname = "GPENCIL_OT_palettecolor_hide";
+ ot->description = "Hide selected/unselected Grease Pencil colors";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_hide_exec;
+ ot->poll = gp_active_palettecolor_poll; /* NOTE: we need an active color to play with */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected colors");
+}
+
+/* ********************** Show All Colors ***************************** */
+
+/* poll callback for showing colors */
+static int gp_palettecolor_reveal_poll(bContext *C)
+{
+ return ED_gpencil_data_get_active(C) != NULL;
+}
+
+static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ /* make all colors visible */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag &= ~PC_COLOR_HIDE;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Show All Colors";
+ ot->idname = "GPENCIL_OT_palettecolor_reveal";
+ ot->description = "Unhide all hidden Grease Pencil palette colors";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_reveal_exec;
+ ot->poll = gp_palettecolor_reveal_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************** Lock/Unlock All Palette colors ************************ */
+
+static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ /* make all layers non-editable */
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag |= PC_COLOR_LOCKED;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Lock All Colors";
+ ot->idname = "GPENCIL_OT_palettecolor_lock_all";
+ ot->description = "Lock all Grease Pencil colors to prevent them from being accidentally modified";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_lock_all_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* -------------------------- */
+
+static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette))
+ return OPERATOR_CANCELLED;
+
+ /* make all layers editable again*/
+ for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) {
+ palcolor->flag &= ~PC_COLOR_LOCKED;
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Unlock All Colors";
+ ot->idname = "GPENCIL_OT_palettecolor_unlock_all";
+ ot->description = "Unlock all Grease Pencil colors so that they can be edited";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_unlock_all_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ******************* Move Color Up/Down ************************** */
+
+enum {
+ GP_COLOR_MOVE_UP = -1,
+ GP_COLOR_MOVE_DOWN = 1
+};
+
+static int gp_palettecolor_move_exec(bContext *C, wmOperator *op)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+
+ int direction = RNA_enum_get(op->ptr, "direction");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ /* up or down? */
+ if (direction == GP_COLOR_MOVE_UP) {
+ /* up */
+ BLI_remlink(&palette->colors, palcolor);
+ BLI_insertlinkbefore(&palette->colors, palcolor->prev, palcolor);
+ }
+ else if (direction == GP_COLOR_MOVE_DOWN) {
+ /* down */
+ BLI_remlink(&palette->colors, palcolor);
+ BLI_insertlinkafter(&palette->colors, palcolor->next, palcolor);
+ }
+ else {
+ BLI_assert(0);
+ }
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_move(wmOperatorType *ot)
+{
+ static EnumPropertyItem slot_move[] = {
+ {GP_COLOR_MOVE_UP, "UP", 0, "Up", ""},
+ {GP_COLOR_MOVE_DOWN, "DOWN", 0, "Down", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ /* identifiers */
+ ot->name = "Move Palette color";
+ ot->idname = "GPENCIL_OT_palettecolor_move";
+ ot->description = "Move the active Grease Pencil palette color up/down in the list";
+
+ /* api callbacks */
+ ot->exec = gp_palettecolor_move_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "direction", slot_move, GP_COLOR_MOVE_UP, "Direction", "");
+}
+
+/* ***************** Select all strokes using Palette color ************************ */
+
+static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ /* read all strokes and select*/
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ /* verify something to do */
+ for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false)
+ continue;
+
+ /* select */
+ if (strcmp(palcolor->info, gps->colorname) == 0) {
+ bGPDspoint *pt;
+ int i;
+
+ gps->flag |= GP_STROKE_SELECT;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ }
+ }
+ }
+ }
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Color";
+ ot->idname = "GPENCIL_OT_palettecolor_select";
+ ot->description = "Select all Grease Pencil strokes using current color";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_select_exec;
+ ot->poll = gp_palettecolor_reveal_poll; /* XXX: could use dedicated poll later */
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/* ***************** Copy Palette color ************************ */
+
+static int gp_palettecolor_copy_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+ bGPDpalettecolor *newcolor;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd, palette, palcolor))
+ return OPERATOR_CANCELLED;
+
+ /* create a new color and duplicate data */
+ newcolor = gpencil_palettecolor_addnew(palette, palcolor->info, true);
+ copy_v4_v4(newcolor->color, palcolor->color);
+ copy_v4_v4(newcolor->fill, palcolor->fill);
+ newcolor->flag = palcolor->flag;
+
+ /* notifiers */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_palettecolor_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Copy Color";
+ ot->idname = "GPENCIL_OT_palettecolor_copy";
+ ot->description = "Copy current Grease Pencil palette color";
+
+ /* callbacks */
+ ot->exec = gp_palettecolor_copy_exec;
+ ot->poll = gp_active_palettecolor_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index ac49a51c716..621ebea6603 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -125,10 +125,48 @@ static int gp_stroke_edit_poll(bContext *C)
return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
}
+/* ************ Stroke Hide selection Toggle ************** */
+
+static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ if (ts == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* Just toggle alpha... */
+ if (ts->gp_sculpt.alpha > 0.0f) {
+ ts->gp_sculpt.alpha = 0.0f;
+ }
+ else {
+ ts->gp_sculpt.alpha = 1.0f;
+ }
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
+
+ return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Hide Selection";
+ ot->idname = "GPENCIL_OT_selection_opacity_toggle";
+ ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
+
+ /* callbacks */
+ ot->exec = gpencil_hideselect_toggle_exec;
+ ot->poll = gp_stroke_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+}
+
/* ************** Duplicate Selected Strokes **************** */
/* Make copies of selected point segments in a selected stroke */
-static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes)
+static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
{
bGPDspoint *pt;
int i;
@@ -169,6 +207,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes)
/* make a stupid copy first of the entire stroke (to get the flags too) */
gpsd = MEM_dupallocN(gps);
+ strcpy(gpsd->tmp_layerinfo, layername); /* saves original layer name */
/* initialize triangle memory - will be calculated on next redraw */
gpsd->triangles = NULL;
@@ -216,8 +255,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
/* make copies of selected strokes, and deselect these once we're done */
for (gps = gpf->strokes.first; gps; gps = gps->next) {
/* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false)
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
+ }
if (gps->flag & GP_STROKE_SELECT) {
if (gps->totpoints == 1) {
@@ -226,8 +266,9 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
/* make direct copies of the stroke and its points */
gpsd = MEM_dupallocN(gps);
+ strcpy(gpsd->tmp_layerinfo, gpl->info);
gpsd->points = MEM_dupallocN(gps->points);
-
+
/* triangle information - will be calculated on next redraw */
gpsd->flag |= GP_STROKE_RECALC_CACHES;
gpsd->triangles = NULL;
@@ -238,7 +279,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op)
}
else {
/* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &new_strokes);
+ gp_duplicate_points(gps, &new_strokes, gpl->info);
}
/* deselect original stroke, or else the originals get moved too
@@ -345,6 +386,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
/* make direct copies of the stroke and its points */
gpsd = MEM_dupallocN(gps);
+ strcpy(gpsd->tmp_layerinfo, gpl->info); /* saves original layer name */
gpsd->points = MEM_dupallocN(gps->points);
/* triangles cache - will be recalculated on next redraw */
@@ -358,7 +400,7 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
}
else {
/* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &gp_strokes_copypastebuf);
+ gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
}
}
}
@@ -395,13 +437,20 @@ static int gp_strokes_paste_poll(bContext *C)
return (CTX_data_active_gpencil_layer(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
}
+enum {
+ GP_COPY_ONLY = -1,
+ GP_COPY_MERGE = 1
+};
+
static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
bGPDframe *gpf;
-
+
+ int type = RNA_enum_get(op->ptr, "type");
+
/* check for various error conditions */
if (gpd == NULL) {
BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
@@ -415,7 +464,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
/* no active layer - let's just create one */
gpl = gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
}
- else if (gpencil_layer_is_editable(gpl) == false) {
+ else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
return OPERATOR_CANCELLED;
}
@@ -463,26 +512,34 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
* we are obliged to add a new frame if one
* doesn't exist already
*/
- gpf = gpencil_layer_getframe(gpl, CFRA, true);
- if (gpf) {
bGPDstroke *gps;
-
/* Copy each stroke into the layer */
for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use(C, gps)) {
- bGPDstroke *new_stroke = MEM_dupallocN(gps);
-
- new_stroke->points = MEM_dupallocN(gps->points);
-
- new_stroke->flag |= GP_STROKE_RECALC_CACHES;
- new_stroke->triangles = NULL;
-
- new_stroke->next = new_stroke->prev = NULL;
- BLI_addtail(&gpf->strokes, new_stroke);
+ /* need to verify if layer exist nad frame */
+ if (type != GP_COPY_MERGE) {
+ gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info));
+ if (gpl == NULL) {
+ /* no layer - use active (only if layer deleted before paste) */
+ gpl = CTX_data_active_gpencil_layer(C);
+ }
+ }
+ gpf = gpencil_layer_getframe(gpl, CFRA, true);
+ if (gpf) {
+ bGPDstroke *new_stroke = MEM_dupallocN(gps);
+ new_stroke->tmp_layerinfo[0] = '\0';
+
+ new_stroke->points = MEM_dupallocN(gps->points);
+
+ new_stroke->flag |= GP_STROKE_RECALC_CACHES;
+ new_stroke->triangles = NULL;
+
+ new_stroke->next = new_stroke->prev = NULL;
+ BLI_addtail(&gpf->strokes, new_stroke);
+ }
}
}
- }
/* updates */
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
@@ -492,10 +549,16 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_paste(wmOperatorType *ot)
{
+ static EnumPropertyItem copy_type[] = {
+ {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
+ {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
/* identifiers */
ot->name = "Paste Strokes";
ot->idname = "GPENCIL_OT_paste";
- ot->description = "Paste previously copied strokes into active layer";
+ ot->description = "Paste previously copied strokes or copy and merge in active layer";
/* callbacks */
ot->exec = gp_strokes_paste_exec;
@@ -503,6 +566,8 @@ void GPENCIL_OT_paste(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
}
/* ******************* Move To Layer ****************************** */
@@ -1069,7 +1134,7 @@ void GPENCIL_OT_delete(wmOperatorType *ot)
};
/* identifiers */
- ot->name = "Delete...";
+ ot->name = "Delete";
ot->idname = "GPENCIL_OT_delete";
ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
@@ -1124,25 +1189,65 @@ static int gp_snap_poll(bContext *C)
static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
{
RegionView3D *rv3d = CTX_wm_region_data(C);
- float gridf = rv3d->gridview;
+ const float gridf = rv3d->gridview;
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- bGPDspoint *pt;
- int i;
-
- // TOOD: if entire stroke is selected, offset entire stroke by same amount?
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* only if point is selected.. */
- if (pt->flag & GP_SPOINT_SELECT) {
- pt->x = gridf * floorf(0.5f + pt->x / gridf);
- pt->y = gridf * floorf(0.5f + pt->y / gridf);
- pt->z = gridf * floorf(0.5f + pt->z / gridf);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ float diff_mat[4][4];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
+ bGPDspoint *pt;
+ int i;
+
+ // TOOD: if entire stroke is selected, offset entire stroke by same amount?
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+
+ /* only if point is selected.. */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (gpl->parent == NULL) {
+ pt->x = gridf * floorf(0.5f + pt->x / gridf);
+ pt->y = gridf * floorf(0.5f + pt->y / gridf);
+ pt->z = gridf * floorf(0.5f + pt->z / gridf);
+ }
+ else {
+ /* apply parent transformations */
+ float fpt[3];
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
+ fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
+ fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
+ fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
+
+ /* return data */
+ copy_v3_v3(&pt->x, fpt);
+ gp_apply_parent_point(gpl, pt);
+ }
+
+ }
+ }
+
}
}
}
- CTX_DATA_END;
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -1169,41 +1274,68 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
View3D *v3d = CTX_wm_view3d(C);
-
+
const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d);
-
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- bGPDspoint *pt;
- int i;
-
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0)
- continue;
-
- if (use_offset) {
- float offset[3];
-
- /* compute offset from first point of stroke to cursor */
- /* TODO: Allow using midpoint instead? */
- sub_v3_v3v3(offset, cursor_global, &gps->points->x);
-
- /* apply offset to all points in the stroke */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- add_v3_v3(&pt->x, offset);
+
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ float diff_mat[4][4];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
}
- }
- else {
- /* affect each selected point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- copy_v3_v3(&pt->x, cursor_global);
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
+ bGPDspoint *pt;
+ int i;
+
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0)
+ continue;
+
+ if (use_offset) {
+ float offset[3];
+
+ /* compute offset from first point of stroke to cursor */
+ /* TODO: Allow using midpoint instead? */
+ sub_v3_v3v3(offset, cursor_global, &gps->points->x);
+
+ /* apply offset to all points in the stroke */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ add_v3_v3(&pt->x, offset);
+ }
+ }
+ else {
+ /* affect each selected point */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ copy_v3_v3(&pt->x, cursor_global);
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
+ }
+ }
}
+
+
}
}
}
- CTX_DATA_END;
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
return OPERATOR_FINISHED;
@@ -1243,24 +1375,57 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
INIT_MINMAX(min, max);
/* calculate midpoints from selected points */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- bGPDspoint *pt;
- int i;
-
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0)
- continue;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- add_v3_v3(centroid, &pt->x);
- minmax_v3v3_v3(min, max, &pt->x);
- count++;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ float diff_mat[4][4];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
+ bGPDspoint *pt;
+ int i;
+
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0)
+ continue;
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (gpl->parent == NULL) {
+ add_v3_v3(centroid, &pt->x);
+ minmax_v3v3_v3(min, max, &pt->x);
+ }
+ else {
+ /* apply parent transformations */
+ float fpt[3];
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
+ add_v3_v3(centroid, fpt);
+ minmax_v3v3_v3(min, max, fpt);
+ }
+ count++;
+ }
+ }
+
}
}
}
- CTX_DATA_END;
if (v3d->around == V3D_AROUND_CENTER_MEAN && count) {
mul_v3_fl(centroid, 1.0f / (float)count);
diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 53fb33eeb9b..0ff0878d4ce 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -101,6 +101,23 @@ void gp_point_to_xy(GP_SpaceConversion *settings, struct bGPDstroke *gps, struct
int *r_x, int *r_y);
/**
+ * Convert point to parent space
+ *
+ * \param pt Original point
+ * \param diff_mat Matrix with the difference between original parent matrix
+ * \param[out] r_pt Pointer to new point after apply matrix
+ */
+void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt);
+/**
+ * Change points position relative to parent object
+ */
+void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps);
+/**
+ * Change point position relative to parent object
+ */
+void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt);
+
+/**
* Convert a screenspace point to a 3D Grease Pencil coordinate.
*
* For use with editing tools where it is easier to perform the operations in 2D,
@@ -116,6 +133,10 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float
int gp_add_poll(struct bContext *C);
int gp_active_layer_poll(struct bContext *C);
+int gp_active_brush_poll(struct bContext *C);
+int gp_active_palette_poll(struct bContext *C);
+int gp_active_palettecolor_poll(struct bContext *C);
+int gp_brush_crt_presets_poll(bContext *C);
/* Copy/Paste Buffer --------------------------------- */
/* gpencil_edit.c */
@@ -137,17 +158,47 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke
bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure);
/**
+* Apply smooth for strength to stroke point
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf);
+
+/**
+* Apply smooth for thickness to stroke point (use pressure)
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf);
+
+/**
* Subdivide a stroke once, by adding points at the midpoint between each pair of points
* \param gps Stroke data
* \param new_totpoints Total number of points (after subdividing)
*/
void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints);
+/**
+* Add randomness to stroke
+* \param gps Stroke data
+* \param brsuh Brush data
+*/
+void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush);
+
/* Layers Enums -------------------------------------- */
struct EnumPropertyItem *ED_gpencil_layers_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
struct EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop, bool *r_free);
+/* Enums of GP Brushes */
+EnumPropertyItem *ED_gpencil_brushes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free);
+
+/* Enums of GP palettes */
+EnumPropertyItem *ED_gpencil_palettes_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free);
/* ***************************************************** */
/* Operator Defines */
@@ -166,6 +217,7 @@ typedef enum eGPencil_PaintModes {
/* stroke editing ----- */
void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot);
+void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot);
void GPENCIL_OT_select(struct wmOperatorType *ot);
void GPENCIL_OT_select_all(struct wmOperatorType *ot);
@@ -216,12 +268,45 @@ void GPENCIL_OT_lock_all(struct wmOperatorType *ot);
void GPENCIL_OT_unlock_all(struct wmOperatorType *ot);
void GPENCIL_OT_layer_isolate(struct wmOperatorType *ot);
+void GPENCIL_OT_layer_merge(struct wmOperatorType *ot);
void GPENCIL_OT_active_frame_delete(struct wmOperatorType *ot);
void GPENCIL_OT_active_frames_delete_all(struct wmOperatorType *ot);
void GPENCIL_OT_convert(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_arrange(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_change_color(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_lock_color(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_apply_thickness(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_cyclical_set(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_join(struct wmOperatorType *ot);
+void GPENCIL_OT_stroke_flip(struct wmOperatorType *ot);
+
+void GPENCIL_OT_brush_add(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_change(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_move(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_presets_create(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_copy(struct wmOperatorType *ot);
+void GPENCIL_OT_brush_select(struct wmOperatorType *ot);
+
+void GPENCIL_OT_palette_add(struct wmOperatorType *ot);
+void GPENCIL_OT_palette_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_palette_change(struct wmOperatorType *ot);
+void GPENCIL_OT_palette_lock_layer(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_add(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_remove(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_isolate(struct wmOperatorType *ot);
+
+void GPENCIL_OT_palettecolor_hide(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_reveal(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_lock_all(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_unlock_all(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_move(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_select(struct wmOperatorType *ot);
+void GPENCIL_OT_palettecolor_copy(struct wmOperatorType *ot);
+
/* undo stack ---------- */
void gpencil_undo_init(struct bGPdata *gpd);
@@ -273,4 +358,39 @@ typedef enum ACTCONT_TYPES {
ACTCONT_GPENCIL
} ACTCONT_TYPES;
+/**
+* Iterate over all editable strokes in the current context,
+* stopping on each usable layer + stroke pair (i.e. gpl and gps)
+* to perform some operations on the stroke.
+*
+* \param gpl The identifier to use for the layer of the stroke being processed.
+* Choose a suitable value to avoid name clashes.
+* \param gps The identifier to use for current stroke being processed.
+* Choose a suitable value to avoid name clashes.
+*/
+#define GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) \
+{ \
+ CTX_DATA_BEGIN(C, bGPDlayer*, gpl, editable_gpencil_layers) \
+ { \
+ if (gpl->actframe == NULL) \
+ continue; \
+ /* calculate difference matrix if parent object */ \
+ float diff_mat[4][4]; \
+ ED_gpencil_parent_location(gpl, diff_mat); \
+ /* loop over strokes */ \
+ for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { \
+ /* skip strokes that are invalid for current view */ \
+ if (ED_gpencil_stroke_can_use(C, gps) == false) \
+ continue; \
+ /* check if the color is editable */ \
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) \
+ continue; \
+ /* ... Do Stuff With Strokes ... */
+
+#define GP_EDITABLE_STROKES_END \
+ } \
+ } \
+ CTX_DATA_END; \
+} (void)0
+
#endif /* __GPENCIL_INTERN_H__ */
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index 65ee1122b56..6bbb8f7c965 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2009, Blender Foundation, Joshua Leung
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -140,8 +140,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
* that the only data being edited is that of the Grease Pencil strokes
*/
- /* FKEY = Eraser Radius */
- kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
+ /* CTRL + FKEY = Eraser Radius */
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
RNA_string_set(kmi->ptr, "data_path_primary", "user_preferences.edit.grease_pencil_eraser_radius");
@@ -169,8 +169,8 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_SHIFT, 0);
RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.strength");
- /* Ctrl-FKEY = Sculpt Brush Size */
- kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, KM_CTRL, 0);
+ /* FKEY = Sculpt Brush Size */
+ kmi = WM_keymap_add_item(keymap, "WM_OT_radial_control", FKEY, KM_PRESS, 0, 0);
RNA_string_set(kmi->ptr, "data_path_primary", "tool_settings.gpencil_sculpt.brush.size");
@@ -266,14 +266,37 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0);
RNA_boolean_set(kmi->ptr, "unselected", true);
+
+ WM_keymap_add_item(keymap, "GPENCIL_OT_selection_opacity_toggle", HKEY, KM_PRESS, KM_CTRL, 0);
/* Isolate Layer */
WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0);
/* Move to Layer */
WM_keymap_add_item(keymap, "GPENCIL_OT_move_to_layer", MKEY, KM_PRESS, 0, 0);
-
-
+
+ /* Select drawing brush using index */
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ONEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 0);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", TWOKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 1);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", THREEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 2);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FOURKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 3);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", FIVEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 4);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SIXKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 5);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", SEVENKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 6);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", EIGHTKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 7);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", NINEKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 8);
+ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_select", ZEROKEY, KM_PRESS, 0, 0);
+ RNA_int_set(kmi->ptr, "index", 9);
+
/* Transform Tools */
kmi = WM_keymap_add_item(keymap, "TRANSFORM_OT_translate", GKEY, KM_PRESS, 0, 0);
@@ -318,7 +341,8 @@ void ED_operatortypes_gpencil(void)
/* Editing (Strokes) ------------ */
WM_operatortype_append(GPENCIL_OT_editmode_toggle);
-
+ WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle);
+
WM_operatortype_append(GPENCIL_OT_select);
WM_operatortype_append(GPENCIL_OT_select_all);
WM_operatortype_append(GPENCIL_OT_select_circle);
@@ -362,12 +386,44 @@ void ED_operatortypes_gpencil(void)
WM_operatortype_append(GPENCIL_OT_lock_all);
WM_operatortype_append(GPENCIL_OT_unlock_all);
WM_operatortype_append(GPENCIL_OT_layer_isolate);
-
+ WM_operatortype_append(GPENCIL_OT_layer_merge);
+
WM_operatortype_append(GPENCIL_OT_active_frame_delete);
WM_operatortype_append(GPENCIL_OT_active_frames_delete_all);
WM_operatortype_append(GPENCIL_OT_convert);
+ WM_operatortype_append(GPENCIL_OT_stroke_arrange);
+ WM_operatortype_append(GPENCIL_OT_stroke_change_color);
+ WM_operatortype_append(GPENCIL_OT_stroke_lock_color);
+ WM_operatortype_append(GPENCIL_OT_stroke_apply_thickness);
+ WM_operatortype_append(GPENCIL_OT_stroke_cyclical_set);
+ WM_operatortype_append(GPENCIL_OT_stroke_join);
+ WM_operatortype_append(GPENCIL_OT_stroke_flip);
+
+ WM_operatortype_append(GPENCIL_OT_palette_add);
+ WM_operatortype_append(GPENCIL_OT_palette_remove);
+ WM_operatortype_append(GPENCIL_OT_palette_change);
+ WM_operatortype_append(GPENCIL_OT_palette_lock_layer);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_add);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_remove);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_isolate);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_hide);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_reveal);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_lock_all);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_unlock_all);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_move);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_select);
+ WM_operatortype_append(GPENCIL_OT_palettecolor_copy);
+
+ WM_operatortype_append(GPENCIL_OT_brush_add);
+ WM_operatortype_append(GPENCIL_OT_brush_remove);
+ WM_operatortype_append(GPENCIL_OT_brush_change);
+ WM_operatortype_append(GPENCIL_OT_brush_move);
+ WM_operatortype_append(GPENCIL_OT_brush_presets_create);
+ WM_operatortype_append(GPENCIL_OT_brush_copy);
+ WM_operatortype_append(GPENCIL_OT_brush_select);
+
/* Editing (Time) --------------- */
}
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index a570d586f50..403d632b5da 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -18,7 +18,7 @@
* The Original Code is Copyright (C) 2008, Blender Foundation, Joshua Leung
* This is a new part of Blender
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -39,21 +39,26 @@
#include "BLI_blenlib.h"
#include "BLI_math.h"
#include "BLI_utildefines.h"
+#include "BLI_rand.h"
#include "BLT_translation.h"
#include "PIL_time.h"
+#include "BKE_main.h"
+#include "BKE_paint.h"
#include "BKE_gpencil.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_tracking.h"
+#include "BKE_colortools.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_gpencil_types.h"
+#include "DNA_brush_types.h"
#include "DNA_windowmanager_types.h"
#include "UI_view2d.h"
@@ -151,6 +156,10 @@ typedef struct tGPsdata {
float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */
void *erasercursor; /* radial cursor data for drawing eraser */
+
+ bGPDpalettecolor *palettecolor; /* current palette color */
+ bGPDbrush *brush; /* current drawing brush */
+ short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */
} tGPsdata;
/* ------ */
@@ -333,10 +342,90 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3]
}
}
+/* apply jitter to stroke */
+static void gp_brush_jitter(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2])
+{
+ float pressure = pt->pressure;
+ float tmp_pressure = pt->pressure;
+ if (brush->draw_jitter > 0.0f) {
+ float curvef = curvemapping_evaluateF(brush->cur_jitter, 0, pressure);
+ tmp_pressure = curvef * brush->draw_sensitivity;
+ }
+ const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */
+ const float fac = BLI_frand() * exfactor * tmp_pressure;
+ /* Jitter is applied perpendicular to the mouse movement vector (2D space) */
+ float mvec[2], svec[2];
+ /* mouse movement in ints -> floats */
+ if (gpd->sbuffer_size > 1) {
+ mvec[0] = (float)(mval[0] - (pt - 1)->x);
+ mvec[1] = (float)(mval[1] - (pt - 1)->y);
+ normalize_v2(mvec);
+ }
+ else {
+ mvec[0] = 0.0f;
+ mvec[1] = 0.0f;
+ }
+ /* rotate mvec by 90 degrees... */
+ svec[0] = -mvec[1];
+ svec[1] = mvec[0];
+ /* scale the displacement by the random, and apply */
+ if (BLI_frand() > 0.5f) {
+ mul_v2_fl(svec, -fac);
+ }
+ else {
+ mul_v2_fl(svec, fac);
+ }
+
+ r_mval[0] = mval[0] + svec[0];
+ r_mval[1] = mval[1] + svec[1];
+
+}
+
+/* apply pressure change depending of the angle of the stroke to simulate a pen with shape */
+static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2])
+{
+ float mvec[2];
+ float sen = brush->draw_angle_factor; /* sensitivity */;
+ float fac;
+ float mpressure;
+
+ float angle = brush->draw_angle; /* default angle of brush in radians */;
+ float v0[2] = { cos(angle), sin(angle) }; /* angle vector of the brush with full thickness */
+
+ /* Apply to first point (only if there are 2 points because before no data to do it ) */
+ if (gpd->sbuffer_size == 1) {
+ mvec[0] = (float)(mval[0] - (pt - 1)->x);
+ mvec[1] = (float)(mval[1] - (pt - 1)->y);
+ normalize_v2(mvec);
+
+ /* uses > 1.0f to get a smooth transition in first point */
+ fac = 1.4f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
+ (pt - 1)->pressure = (pt - 1)->pressure - (sen * fac);
+
+ CLAMP((pt - 1)->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f);
+ }
+
+ /* apply from second point */
+ if (gpd->sbuffer_size >= 1) {
+ mvec[0] = (float)(mval[0] - (pt - 1)->x);
+ mvec[1] = (float)(mval[1] - (pt - 1)->y);
+ normalize_v2(mvec);
+
+ fac = 1.0f - fabs(dot_v2v2(v0, mvec)); /* 0.0 to 1.0 */
+ /* interpolate with previous point for smoother transitions */
+ mpressure = interpf(pt->pressure - (sen * fac), (pt - 1)->pressure, 0.3f);
+ pt->pressure = mpressure;
+
+ CLAMP(pt->pressure, GPENCIL_ALPHA_OPACITY_THRESH, 1.0f);
+ }
+
+}
+
/* add current stroke-point to buffer (returns whether point was successfully added) */
static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure, double curtime)
{
bGPdata *gpd = p->gpd;
+ bGPDbrush *brush = p->brush;
tGPspoint *pt;
/* check painting mode */
@@ -349,6 +438,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
/* store settings */
copy_v2_v2_int(&pt->x, mval);
pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */
+ pt->strength = 1.0f;
pt->time = (float)(curtime - p->inittime);
/* increment buffer size */
@@ -363,6 +453,7 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
/* store settings */
copy_v2_v2_int(&pt->x, mval);
pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */
+ pt->strength = 1.0f;
pt->time = (float)(curtime - p->inittime);
/* now the buffer has 2 points (and shouldn't be allowed to get any larger) */
@@ -381,8 +472,65 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size);
/* store settings */
- copy_v2_v2_int(&pt->x, mval);
- pt->pressure = pressure;
+ /* pressure */
+ if (brush->flag & GP_BRUSH_USE_PRESSURE) {
+ float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure);
+ pt->pressure = curvef * brush->draw_sensitivity;
+ }
+ else {
+ pt->pressure = 1.0f;
+ }
+ /* Apply jitter to position */
+ if (brush->draw_jitter > 0.0f) {
+ int r_mval[2];
+ gp_brush_jitter(gpd, brush, pt, mval, r_mval);
+ copy_v2_v2_int(&pt->x, r_mval);
+ }
+ else {
+ copy_v2_v2_int(&pt->x, mval);
+ }
+ /* apply randomness to pressure */
+ if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_PRESSURE)) {
+ float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure);
+ float tmp_pressure = curvef * brush->draw_sensitivity;
+ if (BLI_frand() > 0.5f) {
+ pt->pressure -= tmp_pressure * brush->draw_random_press * BLI_frand();
+ }
+ else {
+ pt->pressure += tmp_pressure * brush->draw_random_press * BLI_frand();
+ }
+ CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f);
+ }
+
+ /* apply angle of stroke to brush size */
+ if (brush->draw_angle_factor > 0.0f) {
+ gp_brush_angle(gpd, brush, pt, mval);
+ }
+
+ /* color strength */
+ if (brush->flag & GP_BRUSH_USE_STENGTH_PRESSURE) {
+ float curvef = curvemapping_evaluateF(brush->cur_strength, 0, pressure);
+ float tmp_pressure = curvef * brush->draw_sensitivity;
+
+ pt->strength = tmp_pressure * brush->draw_strength;
+ }
+ else {
+ pt->strength = brush->draw_strength;
+ }
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+
+ /* apply randomness to color strength */
+ if ((brush->draw_random_press > 0.0f) && (brush->flag & GP_BRUSH_USE_RANDOM_STRENGTH)) {
+ if (BLI_frand() > 0.5f) {
+ pt->strength -= pt->strength * brush->draw_random_press * BLI_frand();
+ }
+ else {
+ pt->strength += pt->strength * brush->draw_random_press * BLI_frand();
+ }
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ }
+
+ /* point time */
pt->time = (float)(curtime - p->inittime);
/* increment counters */
@@ -395,12 +543,15 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
return GP_STROKEADD_NORMAL;
}
else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) {
+
+ bGPDlayer *gpl = gpencil_layer_getactive(gpd);
/* get pointer to destination point */
pt = (tGPspoint *)(gpd->sbuffer);
/* store settings */
copy_v2_v2_int(&pt->x, mval);
pt->pressure = 1.0f; /* T44932 - Pressure vals are unreliable, so ignore for now */
+ pt->strength = 1.0f;
pt->time = (float)(curtime - p->inittime);
/* if there's stroke for this poly line session add (or replace last) point
@@ -433,10 +584,16 @@ static short gp_stroke_addpoint(tGPsdata *p, const int mval[2], float pressure,
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pts);
+ }
/* copy pressure and time */
pts->pressure = pt->pressure;
+ pts->strength = pt->strength;
pts->time = pt->time;
+ /* force fill recalc */
+ gps->flag |= GP_STROKE_RECALC_CACHES;
}
/* increment counters */
@@ -534,6 +691,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
bGPDstroke *gps;
bGPDspoint *pt;
tGPspoint *ptc;
+ bGPDbrush *brush = p->brush;
int i, totelem;
/* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */
@@ -569,7 +727,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* copy appropriate settings for stroke */
gps->totpoints = totelem;
- gps->thickness = p->gpl->thickness;
+ gps->thickness = brush->thickness;
gps->flag = gpd->sbuffer_sflag;
gps->inittime = p->inittime;
@@ -577,7 +735,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
gps->flag |= GP_STROKE_RECALC_CACHES;
/* allocate enough memory for a continuous array for storage points */
- int sublevel = gpl->sublevel;
+ int sublevel = brush->sublevel;
int new_totpoints = gps->totpoints;
for (i = 0; i < sublevel; i++) {
@@ -600,9 +758,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
pt++;
@@ -614,9 +777,15 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
+
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
}
}
@@ -626,9 +795,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* convert screen-coordinates to appropriate coordinates (and store them) */
gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL);
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent_point(gpl, pt);
+ }
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
}
else {
@@ -703,6 +877,8 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
/* copy pressure and time */
pt->pressure = ptc->pressure;
+ pt->strength = ptc->strength;
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
pt->time = ptc->time;
}
@@ -716,25 +892,38 @@ static void gp_stroke_newfrombuffer(tGPsdata *p)
gp_subdivide_stroke(gps, totpoints);
}
}
-
+ /* apply randomness to stroke */
+ if (brush->draw_random_sub > 0.0f) {
+ gp_randomize_stroke(gps, brush);
+ }
+
/* smooth stroke after subdiv - only if there's something to do
* for each iteration, the factor is reduced to get a better smoothing without changing too much
* the original stroke
*/
- if (gpl->draw_smoothfac > 0.0f) {
+ if (brush->draw_smoothfac > 0.0f) {
float reduce = 0.0f;
- for (int r = 0; r < gpl->draw_smoothlvl; ++r) {
+ for (int r = 0; r < brush->draw_smoothlvl; ++r) {
for (i = 0; i < gps->totpoints; i++) {
/* NOTE: No pressure smoothing, or else we get annoying thickness changes while drawing... */
- gp_smooth_stroke(gps, i, gpl->draw_smoothfac - reduce, false);
+ gp_smooth_stroke(gps, i, brush->draw_smoothfac - reduce, false);
}
reduce += 0.25f; // reduce the factor
}
}
-
+ /* if parented change position relative to parent object */
+ if (gpl->parent != NULL) {
+ gp_apply_parent(gpl, gps);
+ }
+
if (depth_arr)
MEM_freeN(depth_arr);
}
+ /* Save palette color */
+ bGPDpalette *palette = gpencil_palette_getactive(p->gpd);
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+ gps->palcolor = palcolor;
+ strcpy(gps->colorname, palcolor->info);
/* add stroke to frame */
BLI_addtail(&p->gpf->strokes, gps);
@@ -761,12 +950,21 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons
(p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH))
{
RegionView3D *rv3d = p->ar->regiondata;
+ bGPDlayer *gpl = p->gpl;
+
const int mval[2] = {x, y};
float mval_3d[3];
-
+ float fpt[3];
+
+ float diff_mat[4][4];
+ /* calculate difference matrix if parent object */
+ ED_gpencil_parent_location(gpl, diff_mat);
+
if (ED_view3d_autodist_simple(p->ar, mval, mval_3d, 0, NULL)) {
const float depth_mval = view3d_point_depth(rv3d, mval_3d);
- const float depth_pt = view3d_point_depth(rv3d, &pt->x);
+
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ const float depth_pt = view3d_point_depth(rv3d, fpt);
if (depth_pt > depth_mval) {
return true;
@@ -804,7 +1002,13 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
int pc1[2] = {0};
int pc2[2] = {0};
int i;
-
+ float diff_mat[4][4];
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
if (gps->totpoints == 0) {
/* just free stroke */
if (gps->points)
@@ -816,8 +1020,14 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
else if (gps->totpoints == 1) {
/* only process if it hasn't been masked out... */
if (!(p->flags & GP_PAINTFLAG_SELECTMASK) || (gps->points->flag & GP_SPOINT_SELECT)) {
- gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&p->gsc, gps, gps->points, &pc1[0], &pc1[1]);
+ }
+ else {
+ bGPDspoint pt_temp;
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(&p->gsc, gps, &pt_temp, &pc1[0], &pc1[1]);
+ }
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) {
/* only check if point is inside */
@@ -826,7 +1036,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
// XXX: pressure sensitive eraser should apply here too?
MEM_freeN(gps->points);
if (gps->triangles)
- MEM_freeN(gps->triangles);
+ MEM_freeN(gps->triangles);
BLI_freelinkN(&gpf->strokes, gps);
}
}
@@ -836,7 +1046,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* Pressure threshold at which stroke should be culled: Calculated as pressure value
* below which we would have invisible strokes
*/
- const float cull_thresh = (gpl->thickness) ? 1.0f / ((float)gpl->thickness) : 1.0f;
+ const float cull_thresh = (gps->thickness) ? 1.0f / ((float)gps->thickness) : 1.0f;
/* Amount to decrease the pressure of each point with each stroke */
// TODO: Fetch from toolsettings, or compute based on thickness instead?
@@ -865,15 +1075,24 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
/* get points to work with */
pt1 = gps->points + i;
pt2 = gps->points + i + 1;
-
+
/* only process if it hasn't been masked out... */
if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT))
continue;
- /* get coordinates of point in screenspace */
- gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
- gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]);
+ gp_point_to_xy(&p->gsc, gps, pt2, &pc2[0], &pc2[1]);
+ }
+ else {
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
+ }
+
/* Check that point segment of the boundbox of the eraser stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
((!ELEM(V2D_IS_CLIPPED, pc2[0], pc2[1])) && BLI_rcti_isect_pt(rect, pc2[0], pc2[1])))
@@ -955,7 +1174,10 @@ static void gp_stroke_doeraser(tGPsdata *p)
/* loop over strokes, checking segments for intersections */
for (gps = gpf->strokes.first; gps; gps = gpn) {
gpn = gps->next;
-
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
/* Not all strokes in the datablock may be valid in the current editor/context
* (e.g. 2D space strokes in the 3D view, if the same datablock is shared)
*/
@@ -994,6 +1216,78 @@ static void gp_session_validatebuffer(tGPsdata *p)
p->inittime = 0.0;
}
+/* create a new palette color */
+static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette)
+{
+ bGPDpalettecolor *palcolor;
+
+ palcolor = gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
+
+ return palcolor;
+}
+
+/* initialize a drawing brush */
+static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p)
+{
+ bGPDbrush *brush;
+
+ /* if not exist, create a new one */
+ if (BLI_listbase_is_empty(&ts->gp_brushes)) {
+ /* create new brushes */
+ gpencil_brush_init_presets(ts);
+ brush = gpencil_brush_getactive(ts);
+ }
+ else {
+ /* Use the current */
+ brush = gpencil_brush_getactive(ts);
+ }
+ /* be sure curves are initializated */
+ curvemapping_initialize(brush->cur_sensitivity);
+ curvemapping_initialize(brush->cur_strength);
+ curvemapping_initialize(brush->cur_jitter);
+
+ /* asign to temp tGPsdata */
+ p->brush = brush;
+}
+
+
+/* initialize a paint palette brush and a default color if not exist */
+static void gp_init_palette(tGPsdata *p)
+{
+ bGPdata *gpd;
+ bGPDpalette *palette;
+ bGPDpalettecolor *palcolor;
+
+ gpd = p->gpd;
+
+ /* if not exist, create a new palette */
+ if (BLI_listbase_is_empty(&gpd->palettes)) {
+ /* create new palette */
+ palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
+ /* now create a default color */
+ palcolor = gp_create_new_color(palette);
+ }
+ else {
+ /* Use the current palette and color */
+ palette = gpencil_palette_getactive(gpd);
+ /* the palette needs one color */
+ if (BLI_listbase_is_empty(&palette->colors)) {
+ palcolor = gp_create_new_color(palette);
+ }
+ else {
+ palcolor = gpencil_palettecolor_getactive(palette);
+ }
+ /* in some situations can be null, so use first */
+ if (palcolor == NULL) {
+ gpencil_palettecolor_setactive(palette, palette->colors.first);
+ palcolor = palette->colors.first;
+ }
+ }
+
+ /* asign to temp tGPsdata */
+ p->palettecolor = palcolor;
+}
+
/* (re)init new painting data */
static bool gp_session_initdata(bContext *C, tGPsdata *p)
{
@@ -1158,7 +1452,16 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p)
/* clear out buffer (stored in gp-data), in case something contaminated it */
gp_session_validatebuffer(p);
-
+ /* set brush and create a new one if null */
+ gp_init_drawing_brush(ts, p);
+ /* set palette info and create a new one if null */
+ gp_init_palette(p);
+ /* set palette colors */
+ bGPDpalettecolor *palcolor = p->palettecolor;
+ bGPdata *pdata = p->gpd;
+ copy_v4_v4(pdata->scolor, palcolor->color);
+ pdata->sflag = palcolor->flag;
+
return 1;
}
@@ -1177,7 +1480,7 @@ static tGPsdata *gp_session_initpaint(bContext *C)
* erase size won't get lost
*/
p->radius = U.gp_eraser;
-
+
/* return context data for running paint operator */
return p;
}
@@ -1495,7 +1798,6 @@ static bool gpencil_is_tablet_eraser_active(const wmEvent *event)
/* ------------------------------- */
-
static void gpencil_draw_exit(bContext *C, wmOperator *op)
{
tGPsdata *p = op->customdata;
@@ -1513,7 +1815,7 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op)
/* turn off radial brush cursor */
gpencil_draw_toggle_eraser_cursor(C, p, false);
}
-
+
/* always store the new eraser size to be used again next time
* NOTE: Do this even when not in eraser mode, as eraser may
* have been toggled at some point.
@@ -1600,7 +1902,7 @@ static void gpencil_draw_status_indicators(tGPsdata *p)
break;
case GP_PAINTMODE_DRAW:
ED_area_headerprint(p->sa, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | "
- "ESC/Enter to end (or click outside this area)"));
+ "E/ESC/Enter to end (or click outside this area)"));
break;
case GP_PAINTMODE_DRAW_POLY:
ED_area_headerprint(p->sa, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | "
@@ -1691,6 +1993,31 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event)
*/
p->mval[0] = event->mval[0] + 1;
p->mval[1] = event->mval[1] + 1;
+
+ /* verify key status for straight lines */
+ if ((event->ctrl > 0) || (event->alt > 0)) {
+ if (p->straight[0] == 0) {
+ int dx = abs(p->mval[0] - p->mvalo[0]);
+ int dy = abs(p->mval[1] - p->mvalo[1]);
+ if ((dx > 0) || (dy > 0)) {
+ /* check mouse direction to replace the other coordinate with previous values */
+ if (dx >= dy) {
+ /* horizontal */
+ p->straight[0] = 1;
+ p->straight[1] = p->mval[1]; /* save y */
+ }
+ else {
+ /* vertical */
+ p->straight[0] = 2;
+ p->straight[1] = p->mval[0]; /* save x */
+ }
+ }
+ }
+ }
+ else {
+ p->straight[0] = 0;
+ }
+
p->curtime = PIL_check_seconds_timer();
/* handle pressure sensitivity (which is supplied by tablets) */
@@ -1725,7 +2052,9 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event)
p->mvalo[1] = p->mval[1];
p->opressure = p->pressure;
p->inittime = p->ocurtime = p->curtime;
-
+ p->straight[0] = 0;
+ p->straight[1] = 0;
+
/* special exception here for too high pressure values on first touch in
* windows for some tablets, then we just skip first touch...
*/
@@ -1733,6 +2062,18 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event)
return;
}
+ /* check if alt key is pressed and limit to straight lines */
+ if (p->straight[0] != 0) {
+ if (p->straight[0] == 1) {
+ /* horizontal */
+ p->mval[1] = p->straight[1]; /* replace y */
+ }
+ else {
+ /* vertical */
+ p->mval[0] = p->straight[1]; /* replace x */
+ }
+ }
+
/* fill in stroke data (not actually used directly by gpencil_draw_apply) */
RNA_collection_add(op->ptr, "stroke", &itemptr);
@@ -1855,21 +2196,21 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event
if (p->paintmode == GP_PAINTMODE_ERASER) {
gpencil_draw_toggle_eraser_cursor(C, p, true);
}
-
/* set cursor
* NOTE: This may change later (i.e. intentionally via brush toggle,
* or unintentionally if the user scrolls outside the area)...
*/
gpencil_draw_cursor_set(p);
-
+
/* only start drawing immediately if we're allowed to do so... */
if (RNA_boolean_get(op->ptr, "wait_for_input") == false) {
/* hotkey invoked - start drawing */
/* printf("\tGP - set first spot\n"); */
p->status = GP_STATUS_PAINTING;
-
+
/* handle the initial drawing - i.e. for just doing a simple dot */
gpencil_draw_apply_event(op, event);
+ op->flag |= OP_IS_MODAL_CURSOR_REGION;
}
else {
/* toolbar invoked - don't start drawing yet... */
@@ -1976,6 +2317,10 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* is essential for ensuring that they can quickly return to that view
*/
}
+ else if ((ELEM(event->type, DKEY)) && (event->val == KM_RELEASE)) {
+ /* enable continuous if release D key in mid drawing */
+ p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON;
+ }
else {
estate = OPERATOR_RUNNING_MODAL;
}
@@ -1986,7 +2331,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* exit painting mode (and/or end current stroke)
* NOTE: cannot do RIGHTMOUSE (as is standard for canceling) as that would break polyline [#32647]
*/
- if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY)) {
+ if (ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) {
/* exit() ends the current stroke before cleaning up */
/* printf("\t\tGP - end of paint op + end of stroke\n"); */
p->status = GP_STATUS_DONE;
@@ -2045,6 +2390,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
}
else {
/* printf("\t\tGP - end of stroke + op\n"); */
+ /* disable paint session */
+ p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON;
+
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
@@ -2074,6 +2422,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
in_bounds = true;
}
else {
+ /* disable paint session */
+ p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON;
+
/* Out of bounds, or invalid in some other way */
p->status = GP_STATUS_ERROR;
estate = OPERATOR_CANCELLED;
@@ -2090,6 +2441,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
in_bounds = BLI_rcti_isect_pt_v(&region_rect, event->mval);
}
else {
+ /* disable paint session */
+ p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON;
+
/* No region */
p->status = GP_STATUS_ERROR;
estate = OPERATOR_CANCELLED;
@@ -2117,6 +2471,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
p = gpencil_stroke_begin(C, op);
if (p->status == GP_STATUS_ERROR) {
+ /* disable paint session */
+ p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON;
+
estate = OPERATOR_CANCELLED;
}
}
@@ -2125,6 +2482,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event)
* NOTE: Don't eter this case if an error occurred while finding the
* region (as above)
*/
+ /* disable paint session */
+ p->scene->toolsettings->gpencil_flags &= ~GP_TOOL_FLAG_PAINTSESSIONS_ON;
+
p->status = GP_STATUS_DONE;
estate = OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index b6482786b4f..612b35aa608 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -43,6 +43,7 @@
#include "DNA_gpencil_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
+#include "DNA_object_types.h"
#include "BKE_context.h"
#include "BKE_gpencil.h"
@@ -616,9 +617,10 @@ void GPENCIL_OT_select_less(wmOperatorType *ot)
/* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke()
* It would be great to de-duplicate the logic here sometime, but that can wait...
*/
-static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
- const int mx, const int my, const int radius,
- const bool select, rcti *rect)
+static bool gp_stroke_do_circle_sel(
+ bGPDstroke *gps, GP_SpaceConversion *gsc,
+ const int mx, const int my, const int radius,
+ const bool select, rcti *rect, const bool parented, float diff_mat[4][4])
{
bGPDspoint *pt1, *pt2;
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
@@ -626,7 +628,14 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
bool changed = false;
if (gps->totpoints == 1) {
- gp_point_to_xy(gsc, gps, gps->points, &x0, &y0);
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, gps->points, &x0, &y0);
+ }
+ else {
+ bGPDspoint pt_temp;
+ gp_point_to_parent_space(gps->points, diff_mat, &pt_temp);
+ gp_point_to_xy(gsc, gps, &pt_temp, &x0, &y0);
+ }
/* do boundbox check first */
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
@@ -654,9 +663,18 @@ static bool gp_stroke_do_circle_sel(bGPDstroke *gps, GP_SpaceConversion *gsc,
/* get points to work with */
pt1 = gps->points + i;
pt2 = gps->points + i + 1;
-
- gp_point_to_xy(gsc, gps, pt1, &x0, &y0);
- gp_point_to_xy(gsc, gps, pt2, &x1, &y1);
+ if (!parented) {
+ gp_point_to_xy(gsc, gps, pt1, &x0, &y0);
+ gp_point_to_xy(gsc, gps, pt2, &x1, &y1);
+ }
+ else {
+ bGPDspoint npt;
+ gp_point_to_parent_space(pt1, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &x0, &y0);
+
+ gp_point_to_parent_space(pt2, diff_mat, &npt);
+ gp_point_to_xy(gsc, gps, &npt, &x1, &y1);
+ }
/* check that point segment of the boundbox of the selection stroke */
if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) ||
@@ -733,12 +751,14 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
/* find visible strokes, and select if hit */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
- changed |= gp_stroke_do_circle_sel(gps, &gsc, mx, my, radius, select, &rect);
+ changed |= gp_stroke_do_circle_sel(
+ gps, &gsc, mx, my, radius, select, &rect,
+ (gpl->parent != NULL), diff_mat);
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* updates */
if (changed) {
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
@@ -818,17 +838,25 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op)
WM_operator_properties_border_to_rcti(op, &rect);
/* select/deselect points */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
+
bGPDspoint *pt;
int i;
-
+
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
int x0, y0;
-
+
/* convert point coords to screenspace */
- gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
+ }
+ else {
+ bGPDspoint pt2;
+ gp_point_to_parent_space(pt, diff_mat, &pt2);
+ gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0);
+ }
+
/* test if in selection rect */
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0)) {
if (select) {
@@ -837,16 +865,16 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op)
else {
pt->flag &= ~GP_SPOINT_SELECT;
}
-
+
changed = true;
}
}
-
+
/* Ensure that stroke selection is in sync with its points */
gpencil_stroke_sync_selection(gps);
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* updates */
if (changed) {
WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
@@ -920,20 +948,26 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op)
}
/* select/deselect points */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
bGPDspoint *pt;
int i;
-
+
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
int x0, y0;
-
+
/* convert point coords to screenspace */
- gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
-
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&gsc, gps, pt, &x0, &y0);
+ }
+ else {
+ bGPDspoint pt2;
+ gp_point_to_parent_space(pt, diff_mat, &pt2);
+ gp_point_to_xy(&gsc, gps, &pt2, &x0, &y0);
+ }
/* test if in lasso boundbox + within the lasso noose */
if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&rect, x0, y0) &&
- BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX))
+ BLI_lasso_is_point_inside(mcords, mcords_tot, x0, y0, INT_MAX))
{
if (select) {
pt->flag |= GP_SPOINT_SELECT;
@@ -941,16 +975,16 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op)
else {
pt->flag &= ~GP_SPOINT_SELECT;
}
-
+
changed = true;
}
}
-
+
/* Ensure that stroke selection is in sync with its points */
gpencil_stroke_sync_selection(gps);
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* cleanup */
MEM_freeN((void *)mcords);
@@ -1020,35 +1054,42 @@ static int gpencil_select_exec(bContext *C, wmOperator *op)
/* First Pass: Find stroke point which gets hit */
/* XXX: maybe we should go from the top of the stack down instead... */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
+ GP_EDITABLE_STROKES_BEGIN(C, gpl, gps)
{
bGPDspoint *pt;
int i;
-
+
/* firstly, check for hit-point */
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
int xy[2];
-
- gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]);
-
+
+ if (gpl->parent == NULL) {
+ gp_point_to_xy(&gsc, gps, pt, &xy[0], &xy[1]);
+ }
+ else {
+ bGPDspoint pt2;
+ gp_point_to_parent_space(pt, diff_mat, &pt2);
+ gp_point_to_xy(&gsc, gps, &pt2, &xy[0], &xy[1]);
+ }
+
/* do boundbox check first */
if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
const int pt_distance = len_manhattan_v2v2_int(mval, xy);
-
+
/* check if point is inside */
if (pt_distance <= radius_squared) {
/* only use this point if it is a better match than the current hit - T44685 */
if (pt_distance < hit_distance) {
hit_stroke = gps;
- hit_point = pt;
+ hit_point = pt;
hit_distance = pt_distance;
}
}
}
}
}
- CTX_DATA_END;
-
+ GP_EDITABLE_STROKES_END;
+
/* Abort if nothing hit... */
if (ELEM(NULL, hit_stroke, hit_point)) {
return OPERATOR_CANCELLED;
diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c
index c39a3aa5cfc..1d7582eb18b 100644
--- a/source/blender/editors/gpencil/gpencil_undo.c
+++ b/source/blender/editors/gpencil/gpencil_undo.c
@@ -43,7 +43,9 @@
#include "BKE_blender_undo.h"
#include "BKE_context.h"
+#include "BKE_global.h"
#include "BKE_gpencil.h"
+#include "BKE_main.h"
#include "ED_gpencil.h"
@@ -140,7 +142,7 @@ void gpencil_undo_push(bGPdata *gpd)
*/
undo_node->gpd->adt = NULL;
- BKE_gpencil_free(undo_node->gpd);
+ BKE_gpencil_free(undo_node->gpd, false);
MEM_freeN(undo_node->gpd);
BLI_freelinkN(&undo_nodes, undo_node);
@@ -151,7 +153,7 @@ void gpencil_undo_push(bGPdata *gpd)
/* create new undo node */
undo_node = MEM_callocN(sizeof(bGPundonode), "gpencil undo node");
- undo_node->gpd = gpencil_data_duplicate(gpd, true);
+ undo_node->gpd = gpencil_data_duplicate(G.main, gpd, true);
cur_node = undo_node;
@@ -168,7 +170,7 @@ void gpencil_undo_finish(void)
*/
undo_node->gpd->adt = NULL;
- BKE_gpencil_free(undo_node->gpd);
+ BKE_gpencil_free(undo_node->gpd, false);
MEM_freeN(undo_node->gpd);
undo_node = undo_node->next;
diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c
index d62625baaa4..f2c542da0aa 100644
--- a/source/blender/editors/gpencil/gpencil_utils.c
+++ b/source/blender/editors/gpencil/gpencil_utils.c
@@ -17,7 +17,7 @@
*
* The Original Code is Copyright (C) 2014, Blender Foundation
*
- * Contributor(s): Joshua Leung
+ * Contributor(s): Joshua Leung, Antonio Vazquez
*
* ***** END GPL LICENSE BLOCK *****
*/
@@ -32,9 +32,13 @@
#include <stddef.h>
#include <math.h>
+#include "MEM_guardedalloc.h"
+
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLT_translation.h"
+#include "BLI_rand.h"
#include "DNA_gpencil_types.h"
#include "DNA_object_types.h"
@@ -46,6 +50,7 @@
#include "BKE_context.h"
#include "BKE_gpencil.h"
#include "BKE_tracking.h"
+#include "BKE_action.h"
#include "WM_api.h"
@@ -269,6 +274,34 @@ int gp_active_layer_poll(bContext *C)
return (gpl != NULL);
}
+/* poll callback for checking if there is an active brush */
+int gp_active_brush_poll(bContext *C)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush = gpencil_brush_getactive(ts);
+
+ return (brush != NULL);
+}
+
+/* poll callback for checking if there is an active palette */
+int gp_active_palette_poll(bContext *C)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+
+ return (palette != NULL);
+}
+
+/* poll callback for checking if there is an active palette color */
+int gp_active_palettecolor_poll(bContext *C)
+{
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+
+ return (palcolor != NULL);
+}
+
/* ******************************************************** */
/* Dynamic Enums of GP Layers */
/* NOTE: These include an option to create a new layer and use that... */
@@ -412,6 +445,60 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps)
return ED_gpencil_stroke_can_use_direct(sa, gps);
}
+/* Check whether given stroke can be edited for the current color */
+bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps)
+{
+ /* check if the color is editable */
+ bGPDpalettecolor *palcolor = gps->palcolor;
+ if (palcolor != NULL) {
+ if (palcolor->flag & PC_COLOR_HIDE)
+ return false;
+ if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED))
+ return false;
+ }
+
+ return true;
+}
+
+/* Get palette color or create a new one */
+bGPDpalettecolor *ED_gpencil_stroke_getcolor(bGPdata *gpd, bGPDstroke *gps)
+{
+ bGPDpalette *palette;
+ bGPDpalettecolor *palcolor;
+
+ if ((gps->palcolor != NULL) && ((gps->flag & GP_STROKE_RECALC_COLOR) == 0))
+ return gps->palcolor;
+
+ /* get palette */
+ palette = gpencil_palette_getactive(gpd);
+ if (palette == NULL) {
+ palette = gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true);
+ }
+ /* get color */
+ palcolor = gpencil_palettecolor_getbyname(palette, gps->colorname);
+ if (palcolor == NULL) {
+ if (gps->palcolor == NULL) {
+ palcolor = gpencil_palettecolor_addnew(palette, DATA_("Color"), true);
+ /* set to a different color */
+ ARRAY_SET_ITEMS(palcolor->color, 1.0f, 0.0f, 1.0f, 0.9f);
+ }
+ else {
+ palcolor = gpencil_palettecolor_addnew(palette, gps->colorname, true);
+ /* set old color and attributes */
+ bGPDpalettecolor *gpscolor = gps->palcolor;
+ copy_v4_v4(palcolor->color, gpscolor->color);
+ copy_v4_v4(palcolor->fill, gpscolor->fill);
+ palcolor->flag = gpscolor->flag;
+ }
+ }
+
+ /* clear flag and set pointer */
+ gps->flag &= ~GP_STROKE_RECALC_COLOR;
+ gps->palcolor = palcolor;
+
+ return palcolor;
+}
+
/* ******************************************************** */
/* Space Conversion */
@@ -451,6 +538,50 @@ void gp_point_conversion_init(bContext *C, GP_SpaceConversion *r_gsc)
}
}
+/* convert point to parent space */
+void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt)
+{
+ float fpt[3];
+
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ copy_v3_v3(&r_pt->x, fpt);
+}
+
+/* Change position relative to parent object */
+void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps)
+{
+ bGPDspoint *pt;
+ int i;
+
+ /* undo matrix */
+ float diff_mat[4][4];
+ float inverse_diff_mat[4][4];
+ float fpt[3];
+
+ ED_gpencil_parent_location(gpl, diff_mat);
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+
+ for (i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x);
+ copy_v3_v3(&pt->x, fpt);
+ }
+}
+
+/* Change point position relative to parent object */
+void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt)
+{
+ /* undo matrix */
+ float diff_mat[4][4];
+ float inverse_diff_mat[4][4];
+ float fpt[3];
+
+ ED_gpencil_parent_location(gpl, diff_mat);
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+
+ mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x);
+ copy_v3_v3(&pt->x, fpt);
+}
/* Convert Grease Pencil points to screen-space values
* WARNING: This assumes that the caller has already checked whether the stroke in question can be drawn
@@ -591,25 +722,107 @@ bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure)
madd_v3_v3fl(sco, &pt1->x, average_fac);
madd_v3_v3fl(sco, &pt2->x, average_fac);
+#if 0
+ /* XXX: Disabled because get weird result */
/* do pressure too? */
if (affect_pressure) {
pressure += pt1->pressure * average_fac;
pressure += pt2->pressure * average_fac;
}
+#endif
}
}
/* Based on influence factor, blend between original and optimal smoothed coordinate */
interp_v3_v3v3(&pt->x, &pt->x, sco, inf);
+#if 0
+ /* XXX: Disabled because get weird result */
if (affect_pressure) {
pt->pressure = pressure;
}
+#endif
return true;
}
/**
+* Apply smooth for strength to stroke point
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf)
+{
+ bGPDspoint *ptb = &gps->points[i];
+
+ /* Do nothing if not enough points */
+ if (gps->totpoints <= 2) {
+ return false;
+ }
+
+ /* Compute theoretical optimal value using distances */
+ bGPDspoint *pta, *ptc;
+ int before = i - 1;
+ int after = i + 1;
+
+ CLAMP_MIN(before, 0);
+ CLAMP_MAX(after, gps->totpoints - 1);
+
+ pta = &gps->points[before];
+ ptc = &gps->points[after];
+
+ /* the optimal value is the corresponding to the interpolation of the strength
+ * at the distance of point b
+ */
+ const float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
+ const float optimal = (1.0f - fac) * pta->strength + fac * ptc->strength;
+
+ /* Based on influence factor, blend between original and optimal */
+ ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal;
+
+ return true;
+}
+
+/**
+* Apply smooth for thickness to stroke point (use pressure)
+* \param gps Stroke to smooth
+* \param i Point index
+* \param inf Amount of smoothing to apply
+*/
+bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf)
+{
+ bGPDspoint *ptb = &gps->points[i];
+
+ /* Do nothing if not enough points */
+ if (gps->totpoints <= 2) {
+ return false;
+ }
+
+ /* Compute theoretical optimal value using distances */
+ bGPDspoint *pta, *ptc;
+ int before = i - 1;
+ int after = i + 1;
+
+ CLAMP_MIN(before, 0);
+ CLAMP_MAX(after, gps->totpoints - 1);
+
+ pta = &gps->points[before];
+ ptc = &gps->points[after];
+
+ /* the optimal value is the corresponding to the interpolation of the pressure
+ * at the distance of point b
+ */
+ float fac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x);
+ float optimal = (1.0f - fac) * pta->pressure + fac * ptc->pressure;
+
+ /* Based on influence factor, blend between original and optimal */
+ ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal;
+
+ return true;
+}
+
+/**
* Subdivide a stroke once, by adding a point half way between each pair of existing points
* \param gps Stroke data
* \param new_totpoints Total number of points (after subdividing)
@@ -633,16 +846,99 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints)
interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f);
pt->pressure = interpf(prev->pressure, next->pressure, 0.5f);
- pt->time = interpf(prev->time, next->time, 0.5f);
+ pt->strength = interpf(prev->strength, next->strength, 0.5f);
+ CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ pt->time = interpf(prev->time, next->time, 0.5f);
}
/* Update to new total number of points */
gps->totpoints = new_totpoints;
}
-/* ******************************************************** */
+/**
+ * Add randomness to stroke
+ * \param gps Stroke data
+ * \param brsuh Brush data
+ */
+void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush)
+{
+ bGPDspoint *pt1, *pt2, *pt3;
+ float v1[3];
+ float v2[3];
+ if (gps->totpoints < 3) {
+ return;
+ }
+
+ /* get two vectors using 3 points */
+ pt1 = &gps->points[0];
+ pt2 = &gps->points[1];
+ pt3 = &gps->points[(int)(gps->totpoints * 0.75)];
+
+ sub_v3_v3v3(v1, &pt2->x, &pt1->x);
+ sub_v3_v3v3(v2, &pt3->x, &pt2->x);
+ normalize_v3(v1);
+ normalize_v3(v2);
+
+ /* get normal vector to plane created by two vectors */
+ float normal[3];
+ cross_v3_v3v3(normal, v1, v2);
+ normalize_v3(normal);
+ /* get orthogonal vector to plane to rotate random effect */
+ float ortho[3];
+ cross_v3_v3v3(ortho, v1, normal);
+ normalize_v3(ortho);
+ /* Read all points and apply shift vector (first and last point not modified) */
+ for (int i = 1; i < gps->totpoints - 1; ++i) {
+ bGPDspoint *pt = &gps->points[i];
+ /* get vector with shift (apply a division because random is too sensitive */
+ const float fac = BLI_frand() * (brush->draw_random_sub / 10.0f);
+ float svec[3];
+ copy_v3_v3(svec, ortho);
+ if (BLI_frand() > 0.5f) {
+ mul_v3_fl(svec, -fac);
+ }
+ else {
+ mul_v3_fl(svec, fac);
+ }
+
+ /* apply shift */
+ add_v3_v3(&pt->x, svec);
+ }
+
+}
+/* calculate difference matrix */
+void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4])
+{
+ Object *ob = gpl->parent;
+ if (ob == NULL) {
+ unit_m4(diff_mat);
+ return;
+ }
+ else {
+ if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) {
+ mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse);
+ return;
+ }
+ else if (gpl->partype == PARBONE) {
+ bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr);
+ if (pchan) {
+ float tmp_mat[4][4];
+ mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat);
+ mul_m4_m4m4(diff_mat, tmp_mat, gpl->inverse);
+ }
+ else {
+ mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); /* if bone not found use object (armature) */
+ }
+ return;
+ }
+ else {
+ unit_m4(diff_mat); /* not defined type */
+ }
+ }
+}
+/* ******************************************************** */
bool ED_gpencil_stroke_minmax(
const bGPDstroke *gps, const bool use_select,
float r_min[3], float r_max[3])
@@ -659,3 +955,74 @@ bool ED_gpencil_stroke_minmax(
}
return changed;
}
+/* Dynamic Enums of GP Brushes */
+
+EnumPropertyItem *ED_gpencil_brushes_enum_itemf(
+ bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ bGPDbrush *brush;
+ EnumPropertyItem *item = NULL, item_tmp = { 0 };
+ int totitem = 0;
+ int i = 0;
+
+ if (ELEM(NULL, C, ts)) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ /* Existing brushes */
+ for (brush = ts->gp_brushes.first; brush; brush = brush->next, i++) {
+ item_tmp.identifier = brush->info;
+ item_tmp.name = brush->info;
+ item_tmp.value = i;
+
+ if (brush->flag & GP_BRUSH_ACTIVE)
+ item_tmp.icon = ICON_BRUSH_DATA;
+ else
+ item_tmp.icon = ICON_NONE;
+
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+/* Dynamic Enums of GP Palettes */
+
+EnumPropertyItem *ED_gpencil_palettes_enum_itemf(
+ bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop),
+ bool *r_free)
+{
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ bGPDpalette *palette;
+ EnumPropertyItem *item = NULL, item_tmp = { 0 };
+ int totitem = 0;
+ int i = 0;
+
+ if (ELEM(NULL, C, gpd)) {
+ return DummyRNA_DEFAULT_items;
+ }
+
+ /* Existing palettes */
+ for (palette = gpd->palettes.first; palette; palette = palette->next, i++) {
+ item_tmp.identifier = palette->info;
+ item_tmp.name = palette->info;
+ item_tmp.value = i;
+
+ if (palette->flag & PL_PALETTE_ACTIVE)
+ item_tmp.icon = ICON_COLOR;
+ else
+ item_tmp.icon = ICON_NONE;
+
+ RNA_enum_item_add(&item, &totitem, &item_tmp);
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+ *r_free = true;
+
+ return item;
+}
+/* ******************************************************** */
diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h
index 27e1051a336..0940f594482 100644
--- a/source/blender/editors/include/ED_anim_api.h
+++ b/source/blender/editors/include/ED_anim_api.h
@@ -74,6 +74,7 @@ typedef struct bAnimContext {
short mode; /* editor->mode */
short spacetype; /* sa->spacetype */
short regiontype; /* active region -> type (channels or main) */
+
struct ScrArea *sa; /* editor host */
struct SpaceLink *sl; /* editor data */
struct ARegion *ar; /* region within editor */
@@ -85,6 +86,8 @@ typedef struct bAnimContext {
ListBase *markers; /* active set of markers */
struct ReportList *reports; /* pointer to current reports list */
+
+ float yscale_fac; /* scale factor for height of channels (i.e. based on the size of keyframes) */
} bAnimContext;
/* Main Data container types */
@@ -331,11 +334,11 @@ typedef enum eAnimFilter_Flags {
/* -------------- Channel Defines -------------- */
/* channel heights */
-#define ACHANNEL_FIRST (-0.8f * U.widget_unit)
-#define ACHANNEL_HEIGHT (0.8f * U.widget_unit)
-#define ACHANNEL_HEIGHT_HALF (0.4f * U.widget_unit)
-#define ACHANNEL_SKIP (0.1f * U.widget_unit)
-#define ACHANNEL_STEP (ACHANNEL_HEIGHT + ACHANNEL_SKIP)
+#define ACHANNEL_FIRST(ac) (-0.8f * (ac)->yscale_fac * U.widget_unit)
+#define ACHANNEL_HEIGHT(ac) (0.8f * (ac)->yscale_fac * U.widget_unit)
+#define ACHANNEL_HEIGHT_HALF(ac) (0.4f * (ac)->yscale_fac * U.widget_unit)
+#define ACHANNEL_SKIP (0.1f * U.widget_unit)
+#define ACHANNEL_STEP(ac) (ACHANNEL_HEIGHT(ac) + ACHANNEL_SKIP)
/* channel widths */
#define ACHANNEL_NAMEWIDTH (10 * U.widget_unit)
@@ -347,7 +350,6 @@ typedef enum eAnimFilter_Flags {
/* -------------- NLA Channel Defines -------------- */
/* NLA channel heights */
-// XXX: NLACHANNEL_FIRST isn't used?
#define NLACHANNEL_FIRST (-0.8f * U.widget_unit)
#define NLACHANNEL_HEIGHT(snla) ((snla && (snla->flag & SNLA_NOSTRIPCURVES)) ? (0.8f * U.widget_unit) : (1.2f * U.widget_unit))
#define NLACHANNEL_HEIGHT_HALF(snla) ((snla && (snla->flag & SNLA_NOSTRIPCURVES)) ? (0.4f * U.widget_unit) : (0.6f * U.widget_unit))
diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h
index de5ab80a88f..d526b0841cc 100644
--- a/source/blender/editors/include/ED_gpencil.h
+++ b/source/blender/editors/include/ED_gpencil.h
@@ -41,6 +41,8 @@ struct bGPdata;
struct bGPDlayer;
struct bGPDframe;
struct bGPDstroke;
+struct bGPDpalette;
+struct bGPDpalettecolor;
struct bAnimContext;
struct KeyframeEditData;
struct PointerRNA;
@@ -57,6 +59,7 @@ struct wmKeyConfig;
typedef struct tGPspoint {
int x, y; /* x and y coordinates of cursor (in relative to area) */
float pressure; /* pressure of tablet at this point */
+ float strength; /* pressure of tablet at this point for alpha factor */
float time; /* Time relative to stroke start (used when converting to path) */
} tGPspoint;
@@ -86,6 +89,9 @@ bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfr
bool ED_gpencil_stroke_can_use_direct(const struct ScrArea *sa, const struct bGPDstroke *gps);
bool ED_gpencil_stroke_can_use(const struct bContext *C, const struct bGPDstroke *gps);
+bool ED_gpencil_stroke_color_use(const struct bGPDlayer *gpl, const struct bGPDstroke *gps);
+
+struct bGPDpalettecolor *ED_gpencil_stroke_getcolor(struct bGPdata *gpd, struct bGPDstroke *gps);
bool ED_gpencil_stroke_minmax(
const struct bGPDstroke *gps, const bool use_select,
@@ -142,4 +148,10 @@ bool ED_gpencil_anim_copybuf_paste(struct bAnimContext *ac, const short copy_mod
int ED_gpencil_session_active(void);
int ED_undo_gpencil_step(struct bContext *C, int step, const char *name);
+/* ------------ Transformation Utilities ------------ */
+
+/* get difference matrix using parent */
+void ED_gpencil_parent_location(struct bGPDlayer *gpl, float diff_mat[4][4]);
+
+
#endif /* __ED_GPENCIL_H__ */
diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h
index 7d163da0db0..b1f3f012e09 100644
--- a/source/blender/editors/include/ED_keyframes_draw.h
+++ b/source/blender/editors/include/ED_keyframes_draw.h
@@ -80,7 +80,7 @@ typedef struct ActKeyBlock {
/* key-block info */
char sel;
- short handle_type;
+ short flag;
float val;
float start, end;
@@ -89,6 +89,12 @@ typedef struct ActKeyBlock {
short totcurve;
} ActKeyBlock;
+/* ActKeyBlock - Flag */
+typedef enum eActKeyBlock_Flag {
+ /* Key block represents a moving hold */
+ ACTKEYBLOCK_FLAG_MOVING_HOLD = (1 << 0),
+} eActKeyBlock_Flag;
+
/* *********************** Keyframe Drawing ****************************** */
/* options for keyframe shape drawing */
@@ -108,23 +114,23 @@ void draw_keyframe_shape(float x, float y, float xscale, float hsize, short sel,
/* Channel Drawing ------------------ */
/* F-Curve */
-void draw_fcurve_channel(struct View2D *v2d, struct AnimData *adt, struct FCurve *fcu, float ypos);
+void draw_fcurve_channel(struct View2D *v2d, struct AnimData *adt, struct FCurve *fcu, float ypos, float yscale_fac);
/* Action Group Summary */
-void draw_agroup_channel(struct View2D *v2d, struct AnimData *adt, struct bActionGroup *agrp, float ypos);
+void draw_agroup_channel(struct View2D *v2d, struct AnimData *adt, struct bActionGroup *agrp, float ypos, float yscale_fac);
/* Action Summary */
-void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos);
+void draw_action_channel(struct View2D *v2d, struct AnimData *adt, struct bAction *act, float ypos, float yscale_fac);
/* Object Summary */
-void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos);
+void draw_object_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Object *ob, float ypos, float yscale_fac);
/* Scene Summary */
-void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos);
+void draw_scene_channel(struct View2D *v2d, struct bDopeSheet *ads, struct Scene *sce, float ypos, float yscale_fac);
/* DopeSheet Summary */
-void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos);
+void draw_summary_channel(struct View2D *v2d, struct bAnimContext *ac, float ypos, float yscale_fac);
/* Grease Pencil datablock summary */
-void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPdata *gpd, float ypos);
+void draw_gpencil_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPdata *gpd, float ypos, float yscale_fac);
/* Grease Pencil Layer */
-void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos);
+void draw_gpl_channel(struct View2D *v2d, struct bDopeSheet *ads, struct bGPDlayer *gpl, float ypos, float yscale_fac);
/* Mask Layer */
-void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos);
+void draw_masklay_channel(struct View2D *v2d, struct bDopeSheet *ads, struct MaskLayer *masklay, float ypos, float yscale_fac);
/* Keydata Generation --------------- */
/* F-Curve */
diff --git a/source/blender/editors/include/ED_keyframing.h b/source/blender/editors/include/ED_keyframing.h
index 81e2558e765..602e203a381 100644
--- a/source/blender/editors/include/ED_keyframing.h
+++ b/source/blender/editors/include/ED_keyframing.h
@@ -325,18 +325,18 @@ bool ANIM_driver_vars_paste(struct ReportList *reports, struct FCurve *fcu, bool
/* Notes:
* - All the defines for this (User-Pref settings and Per-Scene settings)
* are defined in DNA_userdef_types.h
- * - Scene settings take presidence over those for userprefs, with old files
+ * - Scene settings take precedence over those for userprefs, with old files
* inheriting userpref settings for the scene settings
* - "On/Off + Mode" are stored per Scene, but "settings" are currently stored
* as userprefs
*/
/* Auto-Keying macros for use by various tools */
-/* check if auto-keyframing is enabled (per scene takes presidence) */
+/* check if auto-keyframing is enabled (per scene takes precedence) */
#define IS_AUTOKEY_ON(scene) ((scene) ? (scene->toolsettings->autokey_mode & AUTOKEY_ON) : (U.autokey_mode & AUTOKEY_ON))
-/* check the mode for auto-keyframing (per scene takes presidence) */
+/* check the mode for auto-keyframing (per scene takes precedence) */
#define IS_AUTOKEY_MODE(scene, mode) ((scene) ? (scene->toolsettings->autokey_mode == AUTOKEY_MODE_##mode) : (U.autokey_mode == AUTOKEY_MODE_##mode))
-/* check if a flag is set for auto-keyframing (per scene takes presidence) */
+/* check if a flag is set for auto-keyframing (per scene takes precedence) */
#define IS_AUTOKEY_FLAG(scene, flag) \
((scene) ? \
((scene->toolsettings->autokey_flag & AUTOKEY_FLAG_##flag) || (U.autokey_flag & AUTOKEY_FLAG_##flag)) \
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 8bd8a14030f..422836b5be2 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -208,6 +208,7 @@ eV3DProjStatus ED_view3d_project_float_global(const struct ARegion *ar, const fl
eV3DProjStatus ED_view3d_project_float_object(const struct ARegion *ar, const float co[3], float r_co[2], const eV3DProjTest flag);
float ED_view3d_calc_zfac(const struct RegionView3D *rv3d, const float co[3], bool *r_flip);
+bool ED_view3d_clip_segment(const struct RegionView3D *rv3d, float ray_start[3], float ray_end[3]);
bool ED_view3d_win_to_ray(
const struct ARegion *ar, const struct View3D *v3d, const float mval[2],
float ray_start[3], float ray_normal[3], const bool do_clip);
@@ -218,6 +219,7 @@ void ED_view3d_global_to_vector(const struct RegionView3D *rv3d, const float coo
void ED_view3d_win_to_3d(const struct ARegion *ar, const float depth_pt[3], const float mval[2], float out[3]);
void ED_view3d_win_to_3d_int(const struct ARegion *ar, const float depth_pt[3], const int mval[2], float out[3]);
void ED_view3d_win_to_delta(const struct ARegion *ar, const float mval[2], float out[3], const float zfac);
+void ED_view3d_win_to_origin(const struct ARegion *ar, const float mval[2], float out[3]);
void ED_view3d_win_to_vector(const struct ARegion *ar, const float mval[2], float out[3]);
bool ED_view3d_win_to_segment(const struct ARegion *ar, struct View3D *v3d, const float mval[2],
float r_ray_start[3], float r_ray_end[3], const bool do_clip);
diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h
index 2c80701bf69..e016e014a1a 100644
--- a/source/blender/editors/include/UI_icons.h
+++ b/source/blender/editors/include/UI_icons.h
@@ -320,9 +320,9 @@ DEF_ICON(OUTLINER_OB_SPEAKER)
DEF_ICON(BLANK123)
DEF_ICON(BLANK124)
DEF_ICON(BLANK125)
- DEF_ICON(BLANK126)
- DEF_ICON(BLANK127)
#endif
+DEF_ICON(RESTRICT_COLOR_OFF)
+DEF_ICON(RESTRICT_COLOR_ON)
DEF_ICON(RESTRICT_VIEW_OFF)
DEF_ICON(RESTRICT_VIEW_ON)
DEF_ICON(RESTRICT_SELECT_OFF)
@@ -1020,6 +1020,7 @@ DEF_VICO(KEYTYPE_KEYFRAME_VEC)
DEF_VICO(KEYTYPE_BREAKDOWN_VEC)
DEF_VICO(KEYTYPE_EXTREME_VEC)
DEF_VICO(KEYTYPE_JITTER_VEC)
+DEF_VICO(KEYTYPE_MOVING_HOLD_VEC)
DEF_VICO(COLORSET_01_VEC)
DEF_VICO(COLORSET_02_VEC)
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index a623f5cfb9c..f77e795adca 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -382,7 +382,11 @@ typedef bool (*uiMenuStepFunc)(struct bContext *C, int direction, void *arg1);
typedef struct uiPopupMenu uiPopupMenu;
-struct uiPopupMenu *UI_popup_menu_begin(struct bContext *C, const char *title, int icon) ATTR_NONNULL();
+uiPopupMenu *UI_popup_menu_begin(
+ struct bContext *C, const char *title, int icon) ATTR_NONNULL();
+uiPopupMenu *UI_popup_menu_begin_ex(
+ struct bContext *C, const char *title, const char *block_name,
+ int icon) ATTR_NONNULL();
void UI_popup_menu_end(struct bContext *C, struct uiPopupMenu *head);
struct uiLayout *UI_popup_menu_layout(uiPopupMenu *head);
diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h
index dc843952229..a81221ed54b 100644
--- a/source/blender/editors/include/UI_resources.h
+++ b/source/blender/editors/include/UI_resources.h
@@ -347,6 +347,8 @@ void UI_GetThemeColorShade3ubv(int colorid, int offset, unsigned char col[3])
// get four color values, scaled to 0.0-1.0 range
void UI_GetThemeColor4fv(int colorid, float col[4]);
+// get four color values, range 0.0-1.0, complete with shading offset for the RGB components
+void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4]);
// get the 3 or 4 byte values
void UI_GetThemeColor3ubv(int colorid, unsigned char col[3]);
diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c
index ba7240be5d8..cbe8654ceb6 100644
--- a/source/blender/editors/interface/interface.c
+++ b/source/blender/editors/interface/interface.c
@@ -1138,7 +1138,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, uiBut *but,
* as new items are added to the menu later on. It also optimises efficiency -
* a radial menu is best kept symmetrical, with as large an angle between
* items as possible, so that the gestural mouse movements can be fast and inexact.
-
+ *
* It starts off with two opposite sides for the first two items
* then joined by the one below for the third (this way, even with three items,
* the menu seems to still be 'in order' reading left to right). Then the fourth is
diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c
index 72a6a04feec..9ce863dc8f7 100644
--- a/source/blender/editors/interface/interface_draw.c
+++ b/source/blender/editors/interface/interface_draw.c
@@ -734,24 +734,50 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol),
CLAMP(max, rect.ymin, rect.ymax);
fdrawline(rect.xmax - 3, min, rect.xmax - 3, max);
}
+ /* RGB (3 channel) */
+ else if (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB) {
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+
+ glPushMatrix();
+
+ glTranslatef(rect.xmin, yofs, 0.f);
+ glScalef(w, h, 0.f);
+
+ glColor3fv( colors_alpha[0] );
+ glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_1);
+ glDrawArrays(GL_POINTS, 0, scopes->waveform_tot);
+
+ glColor3fv( colors_alpha[1] );
+ glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_2);
+ glDrawArrays(GL_POINTS, 0, scopes->waveform_tot);
- /* RGB / YCC (3 channels) */
+ glColor3fv( colors_alpha[2] );
+ glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_3);
+ glDrawArrays(GL_POINTS, 0, scopes->waveform_tot);
+
+ glDisableClientState(GL_VERTEX_ARRAY);
+ glPopMatrix();
+ }
+ /* PARADE / YCC (3 channels) */
else if (ELEM(scopes->wavefrm_mode,
- SCOPES_WAVEFRM_RGB,
+ SCOPES_WAVEFRM_RGB_PARADE,
SCOPES_WAVEFRM_YCC_601,
SCOPES_WAVEFRM_YCC_709,
- SCOPES_WAVEFRM_YCC_JPEG))
+ SCOPES_WAVEFRM_YCC_JPEG
+ ))
{
- int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB);
-
+ int rgb = (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB_PARADE);
+
glBlendFunc(GL_ONE, GL_ONE);
-
+
glPushMatrix();
glEnableClientState(GL_VERTEX_ARRAY);
-
+
glTranslatef(rect.xmin, yofs, 0.f);
glScalef(w3, h, 0.f);
-
+
glColor3fv((rgb) ? colors_alpha[0] : colorsycc_alpha[0]);
glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_1);
glDrawArrays(GL_POINTS, 0, scopes->waveform_tot);
@@ -760,19 +786,19 @@ void ui_draw_but_WAVEFORM(ARegion *ar, uiBut *but, uiWidgetColors *UNUSED(wcol),
glColor3fv((rgb) ? colors_alpha[1] : colorsycc_alpha[1]);
glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_2);
glDrawArrays(GL_POINTS, 0, scopes->waveform_tot);
-
+
glTranslatef(1.f, 0.f, 0.f);
glColor3fv((rgb) ? colors_alpha[2] : colorsycc_alpha[2]);
glVertexPointer(2, GL_FLOAT, 0, scopes->waveform_3);
glDrawArrays(GL_POINTS, 0, scopes->waveform_tot);
-
+
glDisableClientState(GL_VERTEX_ARRAY);
glPopMatrix();
-
-
- /* min max */
+ }
+ /* min max */
+ if (scopes->wavefrm_mode != SCOPES_WAVEFRM_LUMA ) {
for (int c = 0; c < 3; c++) {
- if (scopes->wavefrm_mode == SCOPES_WAVEFRM_RGB)
+ if (ELEM(scopes->wavefrm_mode, SCOPES_WAVEFRM_RGB_PARADE, SCOPES_WAVEFRM_RGB))
glColor3f(colors[c][0] * 0.75f, colors[c][1] * 0.75f, colors[c][2] * 0.75f);
else
glColor3f(colorsycc[c][0] * 0.75f, colorsycc[c][1] * 0.75f, colorsycc[c][2] * 0.75f);
diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c
index 2cbc56b14d3..ac7b6428217 100644
--- a/source/blender/editors/interface/interface_eyedropper.c
+++ b/source/blender/editors/interface/interface_eyedropper.c
@@ -805,7 +805,7 @@ static int depthdropper_init(bContext *C, wmOperator *op)
RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (rv3d && rv3d->persp == RV3D_CAMOB) {
View3D *v3d = CTX_wm_view3d(C);
- if (v3d->camera && v3d->camera->data && (((ID *)v3d->camera->data)->lib == NULL)) {
+ if (v3d->camera && v3d->camera->data && !ID_IS_LINKED_DATABLOCK(v3d->camera->data)) {
RNA_id_pointer_create(v3d->camera->data, &ddr->ptr);
ddr->prop = RNA_struct_find_property(&ddr->ptr, "dof_distance");
}
diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c
index e0b8003d8a9..79831cefde6 100644
--- a/source/blender/editors/interface/interface_handlers.c
+++ b/source/blender/editors/interface/interface_handlers.c
@@ -1057,7 +1057,9 @@ static void ui_multibut_free(uiHandleButtonData *data, uiBlock *block)
}
}
-static bool ui_multibut_states_tag(uiBut *but_active, uiHandleButtonData *data, const wmEvent *event)
+static bool ui_multibut_states_tag(
+ uiBut *but_active,
+ uiHandleButtonData *data, const wmEvent *event)
{
uiBut *but;
float seg[2][2];
@@ -1668,7 +1670,9 @@ static bool ui_but_contains_point_px_icon(uiBut *but, ARegion *ar, const wmEvent
return BLI_rcti_isect_pt(&rect, x, y);
}
-static bool ui_but_drag_init(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static bool ui_but_drag_init(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
/* prevent other WM gestures to start while we try to drag */
WM_gestures_remove(C);
@@ -1740,11 +1744,16 @@ static bool ui_but_drag_init(bContext *C, uiBut *but, uiHandleButtonData *data,
}
}
else {
- wmDrag *drag;
+ wmDrag *drag = WM_event_start_drag(
+ C, but->icon, but->dragtype, but->dragpoin,
+ ui_but_value_get(but), WM_DRAG_NOP);
- drag = WM_event_start_drag(C, but->icon, but->dragtype, but->dragpoin, ui_but_value_get(but), WM_DRAG_NOP);
- if (but->imb)
- WM_event_drag_image(drag, but->imb, but->imb_scale, BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect));
+ if (but->imb) {
+ WM_event_drag_image(
+ drag, but->imb, but->imb_scale,
+ BLI_rctf_size_x(&but->rect),
+ BLI_rctf_size_y(&but->rect));
+ }
}
return true;
}
@@ -1872,11 +1881,15 @@ static void ui_but_smart_controller_add(bContext *C, uiBut *from, uiBut *to)
/* (3) add a new controller */
if (WM_operator_name_call(C, "LOGIC_OT_controller_add", WM_OP_EXEC_DEFAULT, &props_ptr) & OPERATOR_FINISHED) {
cont = (bController *)ob->controllers.last;
- cont->type = CONT_LOGIC_AND; /* Quick fix to make sure we always have an AND controller. It might be nicer to make sure the operator gives us the right one though... */
+ /* Quick fix to make sure we always have an AND controller.
+ * It might be nicer to make sure the operator gives us the right one though... */
+ cont->type = CONT_LOGIC_AND;
/* (4) link the sensor->controller->actuator */
tmp_but = MEM_callocN(sizeof(uiBut), "uiBut");
- UI_but_link_set(tmp_but, (void **)&cont, (void ***)&(cont->links), &(cont->totlinks), from->link->tocode, (int)to->hardmin);
+ UI_but_link_set(
+ tmp_but, (void **)&cont, (void ***)&(cont->links),
+ &cont->totlinks, from->link->tocode, (int)to->hardmin);
tmp_but->hardmin = from->link->tocode;
tmp_but->poin = (char *)cont;
@@ -3180,7 +3193,9 @@ static void ui_textedit_prev_but(uiBlock *block, uiBut *actbut, uiHandleButtonDa
}
-static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static void ui_do_but_textedit(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int retval = WM_UI_HANDLER_CONTINUE;
bool changed = false, inbox = false, update = false;
@@ -3233,7 +3248,8 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
if (data->searchbox)
inbox = ui_searchbox_inside(data->searchbox, event->x, event->y);
- /* for double click: we do a press again for when you first click on button (selects all text, no cursor pos) */
+ /* for double click: we do a press again for when you first click on button
+ * (selects all text, no cursor pos) */
if (event->val == KM_PRESS || event->val == KM_DBL_CLICK) {
float mx, my;
@@ -3477,7 +3493,9 @@ static void ui_do_but_textedit(bContext *C, uiBlock *block, uiBut *but, uiHandle
ED_region_tag_redraw(data->region);
}
-static void ui_do_but_textedit_select(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static void ui_do_but_textedit_select(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my, retval = WM_UI_HANDLER_CONTINUE;
@@ -3682,7 +3700,9 @@ static uiBut *ui_but_list_row_text_activate(
/* ***************** events for different button types *************** */
-static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_BUT(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (event->type == LEFTMOUSE && event->val == KM_PRESS) {
@@ -3713,7 +3733,9 @@ static int ui_do_but_BUT(bContext *C, uiBut *but, uiHandleButtonData *data, cons
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_HOTKEYEVT(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
@@ -3776,7 +3798,9 @@ static int ui_do_but_HOTKEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_KEYEVT(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (ELEM(event->type, LEFTMOUSE, PADENTER, RETKEY) && event->val == KM_PRESS) {
@@ -3802,7 +3826,9 @@ static int ui_do_but_KEYEVT(bContext *C, uiBut *but, uiHandleButtonData *data, c
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_TEX(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
if (ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, PADENTER, RETKEY) && event->val == KM_PRESS) {
@@ -3830,7 +3856,9 @@ static int ui_do_but_TEX(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_SEARCH_UNLINK(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
uiButExtraIconType extra_icon_type;
@@ -3877,7 +3905,9 @@ static int ui_do_but_SEARCH_UNLINK(bContext *C, uiBlock *block, uiBut *but, uiHa
return ui_do_but_TEX(C, block, but, data, event);
}
-static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_TOG(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
#ifdef USE_DRAG_TOGGLE
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -3944,7 +3974,9 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_EXIT(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -4092,7 +4124,7 @@ static float ui_numedit_apply_snap(
static bool ui_numedit_but_NUM(
uiBut *but, uiHandleButtonData *data,
- int mx,
+ int mx, const bool is_motion,
const enum eSnapType snap, float fac)
{
float deler, tempf, softmin, softmax, softrange;
@@ -4100,8 +4132,10 @@ static bool ui_numedit_but_NUM(
bool changed = false;
const bool is_float = ui_but_is_float(but);
- /* prevent unwanted drag adjustments */
- if (ui_but_dragedit_update_mval(data, mx) == false) {
+ /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */
+ if ((is_motion || data->draglock) &&
+ (ui_but_dragedit_update_mval(data, mx) == false))
+ {
return changed;
}
@@ -4242,7 +4276,9 @@ static bool ui_numedit_but_NUM(
return changed;
}
-static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_NUM(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my; /* mouse location scaled to fit the UI */
int screen_mx, screen_my; /* mouse location kept at screen pixel coords */
@@ -4324,6 +4360,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
}
}
else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) {
+ const bool is_motion = (event->type == MOUSEMOVE);
const enum eSnapType snap = ui_event_to_snap(event);
float fac;
@@ -4335,8 +4372,9 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
fac = 1.0f;
if (event->shift) fac /= 10.0f;
- if (ui_numedit_but_NUM(but, data, (ui_but_is_cursor_warp(but) ? screen_mx : mx), snap, fac))
+ if (ui_numedit_but_NUM(but, data, (ui_but_is_cursor_warp(but) ? screen_mx : mx), is_motion, snap, fac)) {
ui_numedit_apply(C, block, but, data);
+ }
#ifdef USE_DRAG_MULTINUM
else if (data->multi_data.has_mbuts) {
if (data->multi_data.init == BUTTON_MULTI_INIT_ENABLE) {
@@ -4430,7 +4468,7 @@ static int ui_do_but_NUM(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
static bool ui_numedit_but_SLI(
uiBut *but, uiHandleButtonData *data,
- int mx, const bool is_horizontal,
+ int mx, const bool is_horizontal, const bool is_motion,
const bool snap, const bool shift)
{
float deler, f, tempf, softmin, softmax, softrange;
@@ -4440,8 +4478,9 @@ static bool ui_numedit_but_SLI(
/* note, 'offs' is really from the widget drawing rounded corners see 'widget_numslider' */
float offs;
- /* prevent unwanted drag adjustments */
+ /* prevent unwanted drag adjustments, test motion so modifier keys refresh. */
if ((but->type != UI_BTYPE_SCROLL) &&
+ (is_motion || data->draglock) &&
(ui_but_dragedit_update_mval(data, mx) == false))
{
return changed;
@@ -4541,7 +4580,9 @@ static bool ui_numedit_but_SLI(
return changed;
}
-static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_SLI(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my, click = 0;
int retval = WM_UI_HANDLER_CONTINUE;
@@ -4634,12 +4675,14 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
}
}
else if ((event->type == MOUSEMOVE) || ui_event_is_snap(event)) {
+ const bool is_motion = (event->type == MOUSEMOVE);
#ifdef USE_DRAG_MULTINUM
data->multi_data.drag_dir[0] += abs(data->draglastx - mx);
data->multi_data.drag_dir[1] += abs(data->draglasty - my);
#endif
- if (ui_numedit_but_SLI(but, data, mx, true, event->ctrl != 0, event->shift != 0))
+ if (ui_numedit_but_SLI(but, data, mx, true, is_motion, event->ctrl != 0, event->shift != 0)) {
ui_numedit_apply(C, block, but, data);
+ }
#ifdef USE_DRAG_MULTINUM
else if (data->multi_data.has_mbuts) {
@@ -4722,7 +4765,9 @@ static int ui_do_but_SLI(bContext *C, uiBlock *block, uiBut *but, uiHandleButton
return retval;
}
-static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_SCROLL(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my /*, click = 0 */;
int retval = WM_UI_HANDLER_CONTINUE;
@@ -4765,8 +4810,10 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut
button_activate_state(C, but, BUTTON_STATE_EXIT);
}
else if (event->type == MOUSEMOVE) {
- if (ui_numedit_but_SLI(but, data, (horizontal) ? mx : my, horizontal, false, false))
+ const bool is_motion = (event->type == MOUSEMOVE);
+ if (ui_numedit_but_SLI(but, data, (horizontal) ? mx : my, horizontal, is_motion, false, false)) {
ui_numedit_apply(C, block, but, data);
+ }
}
retval = WM_UI_HANDLER_BREAK;
@@ -4775,7 +4822,9 @@ static int ui_do_but_SCROLL(bContext *C, uiBlock *block, uiBut *but, uiHandleBut
return retval;
}
-static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_GRIP(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my;
int retval = WM_UI_HANDLER_CONTINUE;
@@ -4826,7 +4875,9 @@ static int ui_do_but_GRIP(bContext *C, uiBlock *block, uiBut *but, uiHandleButto
return retval;
}
-static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_LISTROW(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* hack to pass on ctrl+click and double click to overlapping text
@@ -4846,7 +4897,9 @@ static int ui_do_but_LISTROW(bContext *C, uiBut *but, uiHandleButtonData *data,
return ui_do_but_EXIT(C, but, data, event);
}
-static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_BLOCK(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
@@ -5021,7 +5074,9 @@ static void ui_palette_set_active(uiBut *but)
}
}
-static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_COLOR(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
if (data->state == BUTTON_STATE_HIGHLIGHT) {
/* first handle click on icondrag type button */
@@ -5154,7 +5209,9 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_UNITVEC(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_UNITVEC(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my;
@@ -5431,7 +5488,9 @@ static void ui_ndofedit_but_HSVCUBE(
ui_but_v3_set(but, data->vec);
}
-static int ui_do_but_HSVCUBE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_HSVCUBE(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my;
@@ -5607,8 +5666,7 @@ static bool ui_numedit_but_HSVCIRCLE(
ui_color_picker_to_rgb_v(hsv, rgb);
if ((but->flag & UI_BUT_VEC_SIZE_LOCK) && (rgb[0] || rgb[1] || rgb[2])) {
- normalize_v3(rgb);
- mul_v3_fl(rgb, but->a2);
+ normalize_v3_length(rgb, but->a2);
}
if (use_display_colorspace)
@@ -5684,8 +5742,7 @@ static void ui_ndofedit_but_HSVCIRCLE(
ui_color_picker_to_rgb_v(hsv, data->vec);
if ((but->flag & UI_BUT_VEC_SIZE_LOCK) && (data->vec[0] || data->vec[1] || data->vec[2])) {
- normalize_v3(data->vec);
- mul_v3_fl(data->vec, but->a2);
+ normalize_v3_length(data->vec, but->a2);
}
if (use_display_colorspace)
@@ -5695,7 +5752,9 @@ static void ui_ndofedit_but_HSVCIRCLE(
}
-static int ui_do_but_HSVCIRCLE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_HSVCIRCLE(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
ColorPicker *cpicker = but->custom_data;
float *hsv = cpicker->color_data;
@@ -5823,7 +5882,9 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m
return changed;
}
-static int ui_do_but_COLORBAND(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_COLORBAND(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
ColorBand *coba;
CBData *cbd;
@@ -5999,7 +6060,9 @@ static bool ui_numedit_but_CURVE(
return changed;
}
-static int ui_do_but_CURVE(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_CURVE(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my, a;
bool changed = false;
@@ -6167,7 +6230,9 @@ static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int m
return changed;
}
-static int ui_do_but_HISTOGRAM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_HISTOGRAM(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my;
@@ -6240,7 +6305,9 @@ static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx
return changed;
}
-static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_WAVEFORM(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my;
@@ -6294,7 +6361,9 @@ static int ui_do_but_WAVEFORM(bContext *C, uiBlock *block, uiBut *but, uiHandleB
return WM_UI_HANDLER_CONTINUE;
}
-static int ui_do_but_LINK(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_LINK(
+ bContext *C, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
VECCOPY2D(but->linkto, event->mval);
@@ -6357,7 +6426,9 @@ static bool ui_numedit_but_TRACKPREVIEW(
return changed;
}
-static int ui_do_but_TRACKPREVIEW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event)
+static int ui_do_but_TRACKPREVIEW(
+ bContext *C, uiBlock *block, uiBut *but,
+ uiHandleButtonData *data, const wmEvent *event)
{
int mx, my;
@@ -6853,7 +6924,8 @@ static bool ui_but_menu(bContext *C, uiBut *but)
WM_operator_properties_create(&ptr_props, "WM_OT_doc_view");
RNA_string_set(&ptr_props, "doc_id", buf);
- uiItemFullO(layout, "WM_OT_doc_view", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"),
+ uiItemFullO(layout, "WM_OT_doc_view",
+ CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Online Python Reference"),
ICON_NONE, ptr_props.data, WM_OP_EXEC_DEFAULT, 0);
/* XXX inactive option, not for public! */
@@ -6862,7 +6934,8 @@ static bool ui_but_menu(bContext *C, uiBut *but)
RNA_string_set(&ptr_props, "doc_id", buf);
RNA_string_set(&ptr_props, "doc_new", RNA_property_description(but->rnaprop));
- uiItemFullO(layout, "WM_OT_doc_edit", "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0);
+ uiItemFullO(layout, "WM_OT_doc_edit",
+ "Submit Description", ICON_NONE, ptr_props.data, WM_OP_INVOKE_DEFAULT, 0);
#endif
}
}
@@ -7442,7 +7515,8 @@ static bool ui_but_contains_point_px(ARegion *ar, uiBut *but, int x, int y)
/**
* Can we mouse over the button or is it hidden/disabled/layout.
- * Note: ctrl is kind of a hack currently, so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed.
+ * \note ctrl is kind of a hack currently,
+ * so that non-embossed UI_BTYPE_TEXT button behaves as a label when ctrl is not pressed.
*/
static bool ui_but_is_interactive(const uiBut *but, const bool labeledit)
{
@@ -7636,7 +7710,9 @@ static void button_activate_state(bContext *C, uiBut *but, uiHandleButtonState s
if (but->block->auto_open == true) { /* test for toolbox */
time = 1;
}
- else if ((but->block->flag & UI_BLOCK_LOOP && but->type != UI_BTYPE_BLOCK) || but->block->auto_open == true) {
+ else if ((but->block->flag & UI_BLOCK_LOOP && but->type != UI_BTYPE_BLOCK) ||
+ (but->block->auto_open == true))
+ {
time = 5 * U.menuthreshold2;
}
else if (U.uiflag & USER_MENUOPENAUTO) {
@@ -8722,8 +8798,7 @@ static bool ui_mouse_motion_towards_check(
float delta[2];
sub_v2_v2v2(delta, oldp, cent);
- normalize_v2(delta);
- mul_v2_fl(delta, MENU_TOWARDS_WIGGLE_ROOM);
+ normalize_v2_length(delta, MENU_TOWARDS_WIGGLE_ROOM);
add_v2_v2(oldp, delta);
}
@@ -10081,6 +10156,10 @@ static void ui_popup_handler_remove(bContext *C, void *userdata)
{
uiPopupBlockHandle *menu = userdata;
+ if (menu->cancel_func) {
+ menu->cancel_func(C, menu->popup_arg);
+ }
+
/* free menu block if window is closed for some reason */
ui_popup_block_free(C, menu);
diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c
index 6dc60f1d70b..4107414a240 100644
--- a/source/blender/editors/interface/interface_icons.c
+++ b/source/blender/editors/interface/interface_icons.c
@@ -505,6 +505,11 @@ static void vicon_keytype_jitter_draw(int x, int y, int w, int h, float alpha)
vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_JITTER);
}
+static void vicon_keytype_moving_hold_draw(int x, int y, int w, int h, float alpha)
+{
+ vicon_keytype_draw_wrapper(x, y, w, h, alpha, BEZT_KEYTYPE_MOVEHOLD);
+}
+
static void vicon_colorset_draw(int index, int x, int y, int w, int h, float UNUSED(alpha))
{
bTheme *btheme = UI_GetTheme();
@@ -792,6 +797,7 @@ static void init_internal_icons(void)
def_internal_vicon(VICO_KEYTYPE_BREAKDOWN_VEC, vicon_keytype_breakdown_draw);
def_internal_vicon(VICO_KEYTYPE_EXTREME_VEC, vicon_keytype_extreme_draw);
def_internal_vicon(VICO_KEYTYPE_JITTER_VEC, vicon_keytype_jitter_draw);
+ def_internal_vicon(VICO_KEYTYPE_MOVING_HOLD_VEC, vicon_keytype_moving_hold_draw);
def_internal_vicon(VICO_COLORSET_01_VEC, vicon_colorset_draw_01);
def_internal_vicon(VICO_COLORSET_02_VEC, vicon_colorset_draw_02);
@@ -1055,6 +1061,9 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size)
}
}
+static void ui_id_preview_image_render_size(
+ const bContext *C, Scene *scene, ID *id, PreviewImage *pi, int size, const bool use_job);
+
void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool big)
{
Icon *icon = BKE_icon_get(icon_id);
@@ -1070,22 +1079,20 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi
}
if (di) {
- if (di->type == ICON_TYPE_PREVIEW) {
- PreviewImage *prv = (icon->type != 0) ? BKE_previewimg_id_ensure((ID *)icon->obj) : icon->obj;
-
- if (prv) {
- const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON;
-
- if (!prv->use_deferred || prv->rect[size] || (prv->flag[size] & PRV_USER_EDITED)) {
- return;
+ switch (di->type) {
+ case ICON_TYPE_PREVIEW:
+ {
+ ID *id = (icon->type != 0) ? icon->obj : NULL;
+ PreviewImage *prv = id ? BKE_previewimg_id_ensure(id) : icon->obj;
+
+ if (prv) {
+ const int size = big ? ICON_SIZE_PREVIEW : ICON_SIZE_ICON;
+
+ if (id || prv->use_deferred) {
+ ui_id_preview_image_render_size(C, NULL, id, prv, size, true);
+ }
}
-
- icon_create_rect(prv, size);
-
- /* Always using job (background) version. */
- ED_preview_icon_job(C, prv, NULL, prv->rect[size], prv->w[size], prv->h[size]);
-
- prv->flag[size] &= ~PRV_CHANGED;
+ break;
}
}
}
diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h
index f02aad1ff87..8336efa381b 100644
--- a/source/blender/editors/interface/interface_intern.h
+++ b/source/blender/editors/interface/interface_intern.h
@@ -548,6 +548,8 @@ struct uiPopupBlockHandle {
/* store data for refreshing popups */
struct uiPopupBlockCreate popup_create_vars;
+ /* true if we can re-create the popup using 'popup_create_vars' */
+ bool can_refresh;
struct wmTimer *scrolltimer;
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index ff29a6f8e33..1af6d902b18 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -416,7 +416,7 @@ bool UI_context_copy_to_selected_list(
if ((id_data == NULL) ||
(id_data->tag & LIB_TAG_DOIT) == 0 ||
- (id_data->lib) ||
+ ID_IS_LINKED_DATABLOCK(id_data) ||
(GS(id_data->name) != id_code))
{
BLI_remlink(&lb, link);
diff --git a/source/blender/editors/interface/interface_regions.c b/source/blender/editors/interface/interface_regions.c
index d4d3e1af1fd..c621fcf493d 100644
--- a/source/blender/editors/interface/interface_regions.c
+++ b/source/blender/editors/interface/interface_regions.c
@@ -110,7 +110,7 @@ bool ui_but_menu_step_poll(const uiBut *but)
{
BLI_assert(but->type == UI_BTYPE_MENU);
- /* currenly only RNA buttons */
+ /* currently only RNA buttons */
return ((but->menu_step_func != NULL) ||
(but->rnaprop && RNA_property_type(but->rnaprop) == PROP_ENUM));
}
@@ -454,7 +454,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but)
if (but->rnapoin.id.data) {
ID *id = but->rnapoin.id.data;
- if (id->lib) {
+ if (ID_IS_LINKED_DATABLOCK(id)) {
BLI_snprintf(data->lines[data->totline], sizeof(data->lines[0]), TIP_("Library: %s"), id->lib->name);
data->format[data->totline].color_id = UI_TIP_LC_NORMAL;
data->totline++;
@@ -1701,7 +1701,9 @@ static void ui_block_region_draw(const bContext *C, ARegion *ar)
ar->do_draw &= ~RGN_DRAW_REFRESH_UI;
for (block = ar->uiblocks.first; block; block = block_next) {
block_next = block->next;
- ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL);
+ if (block->handle->can_refresh) {
+ ui_popup_block_refresh((bContext *)C, block->handle, NULL, NULL);
+ }
}
}
@@ -1811,6 +1813,8 @@ uiBlock *ui_popup_block_refresh(
bContext *C, uiPopupBlockHandle *handle,
ARegion *butregion, uiBut *but)
{
+ BLI_assert(handle->can_refresh == true);
+
const int margin = UI_POPUP_MARGIN;
wmWindow *window = CTX_wm_window(C);
ARegion *ar = handle->region;
@@ -2001,6 +2005,8 @@ uiPopupBlockHandle *ui_popup_block_create(
handle->popup_create_vars.arg = arg;
handle->popup_create_vars.butregion = but ? butregion : NULL;
copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x);
+ /* caller may free vars used to create this popup, in that case this variable should be disabled. */
+ handle->can_refresh = true;
/* create area region */
ar = ui_region_temp_add(CTX_wm_screen(C));
@@ -2800,6 +2806,7 @@ uiPopupBlockHandle *ui_popup_menu_create(
WM_event_add_mousemove(C);
}
+ handle->can_refresh = false;
MEM_freeN(pup);
return handle;
@@ -2807,14 +2814,17 @@ uiPopupBlockHandle *ui_popup_menu_create(
/******************** Popup Menu API with begin and end ***********************/
-/* only return handler, and set optional title */
-uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
+/**
+ * Only return handler, and set optional title.
+ * \param block_name: Assigned to uiBlock.name (useful info for debugging).
+ */
+uiPopupMenu *UI_popup_menu_begin_ex(bContext *C, const char *title, const char *block_name, int icon)
{
uiStyle *style = UI_style_get_dpi();
uiPopupMenu *pup = MEM_callocN(sizeof(uiPopupMenu), "popup menu");
uiBut *but;
- pup->block = UI_block_begin(C, NULL, __func__, UI_EMBOSS_PULLDOWN);
+ pup->block = UI_block_begin(C, NULL, block_name, UI_EMBOSS_PULLDOWN);
pup->block->flag |= UI_BLOCK_POPUP_MEMORY | UI_BLOCK_IS_FLIP;
pup->block->puphash = ui_popup_menu_hash(title);
pup->layout = UI_block_layout(pup->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_MENU, 0, 0, 200, 0, MENU_PADDING, style);
@@ -2845,6 +2855,11 @@ uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
return pup;
}
+uiPopupMenu *UI_popup_menu_begin(bContext *C, const char *title, int icon)
+{
+ return UI_popup_menu_begin_ex(C, title, __func__, icon);
+}
+
/* set the whole structure to work */
void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
{
@@ -2860,7 +2875,8 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup)
UI_popup_handlers_add(C, &window->modalhandlers, menu, 0);
WM_event_add_mousemove(C);
-
+
+ menu->can_refresh = false;
MEM_freeN(pup);
}
@@ -2991,6 +3007,7 @@ void UI_pie_menu_end(bContext *C, uiPieMenu *pie)
menu, WM_HANDLER_ACCEPT_DBL_CLICK);
WM_event_add_mousemove(C);
+ menu->can_refresh = false;
MEM_freeN(pie);
}
@@ -3203,7 +3220,8 @@ void UI_popup_menu_reports(bContext *C, ReportList *reports)
if (pup == NULL) {
char title[UI_MAX_DRAW_STR];
BLI_snprintf(title, sizeof(title), "%s: %s", IFACE_("Report"), report->typestr);
- pup = UI_popup_menu_begin(C, title, ICON_NONE);
+ /* popup_menu stuff does just what we need (but pass meaningful block name) */
+ pup = UI_popup_menu_begin_ex(C, title, __func__, ICON_NONE);
layout = UI_popup_menu_layout(pup);
}
else {
@@ -3331,6 +3349,11 @@ void UI_popup_block_close(bContext *C, wmWindow *win, uiBlock *block)
if (win) {
UI_popup_handlers_remove(&win->modalhandlers, block->handle);
ui_popup_block_free(C, block->handle);
+
+ /* In the case we have nested popups, closing one may need to redraw another, see: T48874 */
+ for (ARegion *ar = win->screen->regionbase.first; ar; ar = ar->next) {
+ ED_region_tag_refresh_ui(ar);
+ }
}
}
}
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 7f276bcc634..58cadf5587a 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -302,7 +302,7 @@ static void template_id_cb(bContext *C, void *arg_litem, void *arg_event)
break;
case UI_ID_LOCAL:
if (id) {
- if (id_make_local(id, false)) {
+ if (id_make_local(CTX_data_main(C), id, false, false)) {
/* reassign to get get proper updates/notifiers */
idptr = RNA_property_pointer_get(&template->ptr, template->prop);
RNA_property_pointer_set(&template->ptr, template->prop, idptr);
@@ -455,7 +455,7 @@ static void template_ID(
else {
but = uiDefIconBut(block, UI_BTYPE_BUT, 0, ICON_LIBRARY_DATA_DIRECT, 0, 0, UI_UNIT_X, UI_UNIT_Y,
NULL, 0, 0, 0, 0, TIP_("Direct linked library datablock, click to make local"));
- if (!id_make_local(id, true /* test */) || (idfrom && idfrom->lib))
+ if (!id_make_local(CTX_data_main(C), id, true /* test */, false) || (idfrom && idfrom->lib))
UI_but_flag_enable(but, UI_BUT_DISABLED);
}
@@ -475,7 +475,7 @@ static void template_ID(
UI_but_funcN_set(but, template_id_cb, MEM_dupallocN(template), SET_INT_IN_POINTER(UI_ID_ALONE));
if (/* test only */
- (id_copy(id, NULL, true) == false) ||
+ (id_copy(CTX_data_main(C), id, NULL, true) == false) ||
(idfrom && idfrom->lib) ||
(!editable) ||
/* object in editmode - don't change data */
@@ -974,7 +974,7 @@ static uiLayout *draw_modifier(
}
UI_block_lock_clear(block);
- UI_block_lock_set(block, ob && ob->id.lib, ERROR_LIBDATA_MESSAGE);
+ UI_block_lock_set(block, ob && ID_IS_LINKED_DATABLOCK(ob), ERROR_LIBDATA_MESSAGE);
if (!ELEM(md->type, eModifierType_Fluidsim, eModifierType_Softbody, eModifierType_ParticleSystem,
eModifierType_Cloth, eModifierType_Smoke))
@@ -1021,7 +1021,7 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr)
return NULL;
}
- UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ob->id.lib), ERROR_LIBDATA_MESSAGE);
+ UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED_DATABLOCK(ob)), ERROR_LIBDATA_MESSAGE);
/* find modifier and draw it */
cageIndex = modifiers_getCageIndex(scene, ob, &lastCageIndex, 0);
@@ -1249,7 +1249,7 @@ uiLayout *uiTemplateConstraint(uiLayout *layout, PointerRNA *ptr)
return NULL;
}
- UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ob->id.lib), ERROR_LIBDATA_MESSAGE);
+ UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED_DATABLOCK(ob)), ERROR_LIBDATA_MESSAGE);
/* hrms, the temporal constraint should not draw! */
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
@@ -1611,7 +1611,7 @@ void uiTemplateColorRamp(uiLayout *layout, PointerRNA *ptr, const char *propname
block = uiLayoutAbsoluteBlock(layout);
id = cptr.id.data;
- UI_block_lock_set(block, (id && id->lib), ERROR_LIBDATA_MESSAGE);
+ UI_block_lock_set(block, (id && ID_IS_LINKED_DATABLOCK(id)), ERROR_LIBDATA_MESSAGE);
colorband_buttons_layout(layout, block, cptr.data, &rect, cb, expand);
@@ -1940,6 +1940,7 @@ enum {
UICURVE_FUNC_RESET_VIEW,
UICURVE_FUNC_HANDLE_VECTOR,
UICURVE_FUNC_HANDLE_AUTO,
+ UICURVE_FUNC_HANDLE_AUTO_ANIM,
UICURVE_FUNC_EXTEND_HOZ,
UICURVE_FUNC_EXTEND_EXP,
};
@@ -1960,13 +1961,16 @@ static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event)
cumap->curr = cumap->clipr;
break;
case UICURVE_FUNC_HANDLE_VECTOR: /* set vector */
- curvemap_sethandle(cuma, 1);
+ curvemap_handle_set(cuma, HD_VECT);
curvemapping_changed(cumap, false);
break;
case UICURVE_FUNC_HANDLE_AUTO: /* set auto */
- curvemap_sethandle(cuma, 0);
+ curvemap_handle_set(cuma, HD_AUTO);
curvemapping_changed(cumap, false);
break;
+ case UICURVE_FUNC_HANDLE_AUTO_ANIM: /* set auto-clamped */
+ curvemap_handle_set(cuma, HD_AUTO_ANIM);
+ curvemapping_changed(cumap, false);
case UICURVE_FUNC_EXTEND_HOZ: /* extend horiz */
cuma->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
curvemapping_changed(cumap, false);
@@ -2000,6 +2004,9 @@ static uiBlock *curvemap_tools_func(
uiDefIconTextBut(
block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Handle"),
0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO, "");
+ uiDefIconTextBut(
+ block, UI_BTYPE_BUT_MENU, 1, ICON_BLANK1, IFACE_("Auto Clamped Handle"),
+ 0, yco -= UI_UNIT_Y, menuwidth, UI_UNIT_Y, NULL, 0.0, 0.0, 0, UICURVE_FUNC_HANDLE_AUTO_ANIM, "");
}
if (show_extend) {
@@ -2263,7 +2270,7 @@ void uiTemplateCurveMapping(
cb->prop = prop;
id = cptr.id.data;
- UI_block_lock_set(block, (id && id->lib), ERROR_LIBDATA_MESSAGE);
+ UI_block_lock_set(block, (id && ID_IS_LINKED_DATABLOCK(id)), ERROR_LIBDATA_MESSAGE);
curvemap_buttons_layout(layout, &cptr, type, levels, brush, neg_slope, cb);
diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c
index 5098e701638..02981b543f3 100644
--- a/source/blender/editors/interface/interface_widgets.c
+++ b/source/blender/editors/interface/interface_widgets.c
@@ -3042,7 +3042,7 @@ static void widget_swatch(uiBut *but, uiWidgetColors *wcol, rcti *rect, int stat
if (state & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) {
/* Now we reduce alpha of the inner color (i.e. the color shown)
- * so that this setting can look greyed out, while retaining
+ * so that this setting can look grayed out, while retaining
* the checkboard (for transparent values). This is needed
* here as the effects of ui_widget_color_disabled() are overwritten.
*/
diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c
index e2e2413c717..c8ff335f2a0 100644
--- a/source/blender/editors/interface/resources.c
+++ b/source/blender/editors/interface/resources.c
@@ -1011,6 +1011,8 @@ void ui_theme_init_default(void)
rgba_char_args_set(btheme->tact.keyborder, 0, 0, 0, 255);
rgba_char_args_set(btheme->tact.keyborder_select, 0, 0, 0, 255);
+ btheme->tact.keyframe_scale_fac = 1.0f;
+
/* space nla */
btheme->tnla = btheme->tact;
@@ -1471,6 +1473,30 @@ void UI_GetThemeColor3ubv(int colorid, unsigned char col[3])
col[2] = cp[2];
}
+/* get the color, range 0.0-1.0, complete with shading offset */
+void UI_GetThemeColorShade4fv(int colorid, int offset, float col[4])
+{
+ int r, g, b, a;
+ const unsigned char *cp;
+
+ cp = UI_ThemeGetColorPtr(theme_active, theme_spacetype, colorid);
+
+ r = offset + (int) cp[0];
+ CLAMP(r, 0, 255);
+ g = offset + (int) cp[1];
+ CLAMP(g, 0, 255);
+ b = offset + (int) cp[2];
+ CLAMP(b, 0, 255);
+
+ a = (int) cp[3]; /* no shading offset... */
+ CLAMP(a, 0, 255);
+
+ col[0] = ((float)r) / 255.0f;
+ col[1] = ((float)g) / 255.0f;
+ col[2] = ((float)b) / 255.0f;
+ col[3] = ((float)a) / 255.0f;
+}
+
/* get the color, in char pointer */
void UI_GetThemeColor4ubv(int colorid, unsigned char col[4])
{
@@ -2699,6 +2725,14 @@ void init_userdef_do_versions(void)
}
}
}
+
+ if (!USER_VERSION_ATLEAST(277, 2)) {
+ bTheme *btheme;
+ for (btheme = U.themes.first; btheme; btheme = btheme->next) {
+ if (btheme->tact.keyframe_scale_fac < 0.1f)
+ btheme->tact.keyframe_scale_fac = 1.0f;
+ }
+ }
/**
* Include next version bump.
diff --git a/source/blender/editors/io/io_collada.c b/source/blender/editors/io/io_collada.c
index b74c4b5f526..d62651cef81 100644
--- a/source/blender/editors/io/io_collada.c
+++ b/source/blender/editors/io/io_collada.c
@@ -175,7 +175,7 @@ static int wm_collada_export_exec(bContext *C, wmOperator *op)
open_sim);
if (export_count == 0) {
- BKE_report(op->reports, RPT_WARNING, "Export file is empty");
+ BKE_report(op->reports, RPT_WARNING, "No objects selected -- Created empty export file");
return OPERATOR_CANCELLED;
}
else if (export_count < 0) {
diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c
index 1dfb1cddd2c..d4c49833c2c 100644
--- a/source/blender/editors/mesh/editmesh_extrude.c
+++ b/source/blender/editors/mesh/editmesh_extrude.c
@@ -287,8 +287,7 @@ static int edbm_extrude_repeat_exec(bContext *C, wmOperator *op)
short a;
/* dvec */
- normalize_v3_v3(dvec, rv3d->persinv[2]);
- mul_v3_fl(dvec, offs);
+ normalize_v3_v3_length(dvec, rv3d->persinv[2], offs);
/* base correction */
copy_m3_m4(bmat, obedit->obmat);
diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c
index 69588928253..281a8b2a02d 100644
--- a/source/blender/editors/mesh/editmesh_intersect.c
+++ b/source/blender/editors/mesh/editmesh_intersect.c
@@ -563,7 +563,7 @@ static BMEdge *bm_face_split_edge_find(
if (UNLIKELY(BM_edge_exists(v_pivot, l_iter->e->v1) ||
BM_edge_exists(v_pivot, l_iter->e->v2)))
{
- /* very unlikley but will cause complications splicing the verts together,
+ /* very unlikely but will cause complications splicing the verts together,
* so just skip this case */
ok = false;
}
diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c
index 8868827a11f..e31e4096ded 100644
--- a/source/blender/editors/mesh/editmesh_rip.c
+++ b/source/blender/editors/mesh/editmesh_rip.c
@@ -153,8 +153,7 @@ static float edbm_rip_edge_side_measure(
ED_view3d_project_float_v2_m4(ar, e->v2->co, e_v2_co, projectMat);
sub_v2_v2v2(vec, cent, mid);
- normalize_v2(vec);
- mul_v2_fl(vec, 0.01f);
+ normalize_v2_length(vec, 0.01f);
/* rather then adding to both verts, subtract from the mouse */
sub_v2_v2v2(fmval_tweak, fmval, vec);
@@ -396,7 +395,7 @@ static void edbm_ripsel_deselect_helper(BMesh *bm, EdgeLoopPair *eloop_pairs,
* return an un-ordered array of loop pairs
* use for rebuilding face-fill
*
- * \note the method currenly used fails for edges with 3+ face users and gives
+ * \note the method currently used fails for edges with 3+ face users and gives
* nasty holes in the mesh, there isnt a good way of knowing ahead of time
* which loops will be split apart (its possible to figure out but quite involved).
* So for now this is a known limitation of current rip-fill option.
@@ -749,10 +748,8 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve
}
if (do_fill) {
- if (do_fill) {
- /* match extrude vert-order */
- BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP);
- }
+ /* match extrude vert-order */
+ BM_edge_create(bm, vout[1], vout[0], NULL, BM_CREATE_NOP);
}
MEM_freeN(vout);
diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c
index 9f1602ccfaf..1a14fad8650 100644
--- a/source/blender/editors/mesh/editmesh_tools.c
+++ b/source/blender/editors/mesh/editmesh_tools.c
@@ -49,6 +49,7 @@
#include "BKE_material.h"
#include "BKE_context.h"
+#include "BKE_deform.h"
#include "BKE_depsgraph.h"
#include "BKE_report.h"
#include "BKE_texture.h"
@@ -79,6 +80,8 @@
#include "mesh_intern.h" /* own include */
+#include "bmesh_tools.h"
+
#define USE_FACE_CREATE_SEL_EXTEND
static int edbm_subdivide_exec(bContext *C, wmOperator *op)
@@ -3292,7 +3295,7 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
Object *ob = base_iter->object;
if (ob->type == OB_MESH) {
Mesh *me = ob->data;
- if (me->id.lib == NULL) {
+ if (!ID_IS_LINKED_DATABLOCK(me)) {
BMesh *bm_old = NULL;
int retval_iter = 0;
@@ -3979,6 +3982,196 @@ void MESH_OT_tris_convert_to_quads(wmOperatorType *ot)
join_triangle_props(ot);
}
+
+/* -------------------------------------------------------------------- */
+
+/** \name Decimate
+ *
+ * \note The function to decimate is intended for use as a modifier,
+ * while its handy allow access as a tool - this does cause access to be a little awkward
+ * (passing selection as weights for eg).
+ *
+ * \{ */
+
+static int edbm_decimate_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ BMesh *bm = em->bm;
+
+ const float ratio = RNA_float_get(op->ptr, "ratio");
+ bool use_vertex_group = RNA_boolean_get(op->ptr, "use_vertex_group");
+ const float vertex_group_factor = RNA_float_get(op->ptr, "vertex_group_factor");
+ const bool invert_vertex_group = RNA_boolean_get(op->ptr, "invert_vertex_group");
+ const bool use_symmetry = RNA_boolean_get(op->ptr, "use_symmetry");
+ const float symmetry_eps = 0.00002f;
+ const int symmetry_axis = use_symmetry ? RNA_enum_get(op->ptr, "symmetry_axis") : -1;
+
+ /* nop */
+ if (ratio == 1.0f) {
+ return OPERATOR_FINISHED;
+ }
+
+ float *vweights = MEM_mallocN(sizeof(*vweights) * bm->totvert, __func__);
+ {
+ const int cd_dvert_offset = CustomData_get_offset(&bm->vdata, CD_MDEFORMVERT);
+ const int defbase_act = obedit->actdef - 1;
+
+ if (use_vertex_group && (cd_dvert_offset == -1)) {
+ BKE_report(op->reports, RPT_WARNING, "No active vertex group");
+ use_vertex_group = false;
+ }
+
+ BMIter iter;
+ BMVert *v;
+ int i;
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ float weight = 0.0f;
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ if (use_vertex_group) {
+ const MDeformVert *dv = BM_ELEM_CD_GET_VOID_P(v, cd_dvert_offset);
+ weight = defvert_find_weight(dv, defbase_act);
+ if (invert_vertex_group) {
+ weight = 1.0f - weight;
+ }
+ }
+ else {
+ weight = 1.0f;
+ }
+ }
+
+ vweights[i] = weight;
+ BM_elem_index_set(v, i); /* set_inline */
+ }
+ bm->elem_index_dirty &= ~BM_VERT;
+ }
+
+ float ratio_adjust;
+
+ if ((bm->totface == bm->totfacesel) || (ratio == 0.0f)) {
+ ratio_adjust = ratio;
+ }
+ else {
+ /**
+ * Calculate a new ratio based on faces that could be remoevd during decimation.
+ * needed so 0..1 has a meaningful range when operating on the selection.
+ *
+ * This doesn't have to be totally accurate,
+ * but needs to be greater than the number of selected faces
+ */
+
+ int totface_basis = 0;
+ int totface_adjacent = 0;
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ /* count faces during decimation, ngons are triangulated */
+ const int f_len = f->len > 4 ? (f->len - 2) : 1;
+ totface_basis += f_len;
+
+ BMLoop *l_iter, *l_first;
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ if (vweights[BM_elem_index_get(l_iter->v)] != 0.0f) {
+ totface_adjacent += f_len;
+ break;
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+ }
+
+ ratio_adjust = ratio;
+ ratio_adjust = 1.0f - ratio_adjust;
+ ratio_adjust *= (float)totface_adjacent / (float)totface_basis;
+ ratio_adjust = 1.0f - ratio_adjust;
+ }
+
+ BM_mesh_decimate_collapse(
+ em->bm, ratio_adjust, vweights, vertex_group_factor, false,
+ symmetry_axis, symmetry_eps);
+
+ MEM_freeN(vweights);
+
+ {
+ short selectmode = em->selectmode;
+ if ((selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
+ /* ensure we flush edges -> faces */
+ selectmode |= SCE_SELECT_EDGE;
+ }
+ EDBM_selectmode_flush_ex(em, selectmode);
+ }
+
+ EDBM_update_generic(em, true, true);
+
+ return OPERATOR_FINISHED;
+}
+
+
+static bool edbm_decimate_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
+{
+ return true;
+}
+
+
+static void edbm_decimate_ui(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout, *box, *row, *col;
+ PointerRNA ptr;
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ uiItemR(layout, &ptr, "ratio", 0, NULL, ICON_NONE);
+
+ box = uiLayoutBox(layout);
+ uiItemR(box, &ptr, "use_vertex_group", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(box, false);
+ uiLayoutSetActive(col, RNA_boolean_get(&ptr, "use_vertex_group"));
+ uiItemR(col, &ptr, "vertex_group_factor", 0, NULL, ICON_NONE);
+ uiItemR(col, &ptr, "invert_vertex_group", 0, NULL, ICON_NONE);
+
+ box = uiLayoutBox(layout);
+ uiItemR(box, &ptr, "use_symmetry", 0, NULL, ICON_NONE);
+ row = uiLayoutRow(box, true);
+ uiLayoutSetActive(row, RNA_boolean_get(&ptr, "use_symmetry"));
+ uiItemR(row, &ptr, "symmetry_axis", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+}
+
+
+void MESH_OT_decimate(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Decimate Geometry";
+ ot->idname = "MESH_OT_decimate";
+ ot->description = "Simplify geometry by collapsing edges";
+
+ /* api callbacks */
+ ot->exec = edbm_decimate_exec;
+ ot->check = edbm_decimate_check;
+ ot->ui = edbm_decimate_ui;
+ ot->poll = ED_operator_editmesh;
+
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* Note, keep in sync with 'rna_def_modifier_decimate' */
+ RNA_def_float(ot->srna, "ratio", 1.0f, 0.0f, 1.0f, "Ratio", "", 0.0f, 1.0f);
+
+ RNA_def_boolean(ot->srna, "use_vertex_group", false, "Vertex Group",
+ "Use active vertex group as an influence");
+ RNA_def_float(ot->srna, "vertex_group_factor", 1.0f, 0.0f, 1000.0f, "Weight",
+ "Vertex group strength", 0.0f, 10.0f);
+ RNA_def_boolean(ot->srna, "invert_vertex_group", false, "Invert",
+ "Invert vertex group influence");
+
+ RNA_def_boolean(ot->srna, "use_symmetry", false, "Symmetry",
+ "Maintain symmetry on an axis");
+
+ RNA_def_enum(ot->srna, "symmetry_axis", rna_enum_axis_xyz_items, 1, "Axis", "Axis of symmetry");
+}
+
+/** \} */
+
+
/* -------------------------------------------------------------------- */
/* Dissolve */
diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c
index b44fbc3ce45..c9814d189a4 100644
--- a/source/blender/editors/mesh/editmesh_undo.c
+++ b/source/blender/editors/mesh/editmesh_undo.c
@@ -114,7 +114,7 @@ static struct {
TaskPool *task_pool;
#endif
-} um_arraystore = {NULL};
+} um_arraystore = {{NULL}};
static void um_arraystore_cd_compact(
struct CustomData *cdata, const size_t data_len,
diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c
index 5101608246a..4fc61e0912e 100644
--- a/source/blender/editors/mesh/editmesh_utils.c
+++ b/source/blender/editors/mesh/editmesh_utils.c
@@ -1440,13 +1440,9 @@ bool BMBVH_EdgeVisible(struct BMBVHTree *tree, BMEdge *e, ARegion *ar, View3D *v
sub_v3_v3v3(dir2, origin, co2);
sub_v3_v3v3(dir3, origin, co3);
- normalize_v3(dir1);
- normalize_v3(dir2);
- normalize_v3(dir3);
-
- mul_v3_fl(dir1, epsilon);
- mul_v3_fl(dir2, epsilon);
- mul_v3_fl(dir3, epsilon);
+ normalize_v3_length(dir1, epsilon);
+ normalize_v3_length(dir2, epsilon);
+ normalize_v3_length(dir3, epsilon);
/* offset coordinates slightly along view vectors, to avoid
* hitting the faces that own the edge.*/
diff --git a/source/blender/editors/mesh/mesh_data.c b/source/blender/editors/mesh/mesh_data.c
index e0ddc017e93..772bb1bd308 100644
--- a/source/blender/editors/mesh/mesh_data.c
+++ b/source/blender/editors/mesh/mesh_data.c
@@ -511,7 +511,7 @@ static int layers_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib);
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED_DATABLOCK(data));
}
static int mesh_uv_texture_add_exec(bContext *C, wmOperator *UNUSED(op))
@@ -759,7 +759,7 @@ static int mesh_customdata_mask_clear_poll(bContext *C)
return false;
}
- if (me->id.lib == NULL) {
+ if (!ID_IS_LINKED_DATABLOCK(me)) {
CustomData *data = GET_CD_DATA(me, vdata);
if (CustomData_has_layer(data, CD_PAINT_MASK)) {
return true;
@@ -813,7 +813,7 @@ static int mesh_customdata_skin_state(bContext *C)
if (ob && ob->type == OB_MESH) {
Mesh *me = ob->data;
- if (me->id.lib == NULL) {
+ if (!ID_IS_LINKED_DATABLOCK(me)) {
CustomData *data = GET_CD_DATA(me, vdata);
return CustomData_has_layer(data, CD_MVERT_SKIN);
}
diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h
index 08b4d1aee45..300b21a052d 100644
--- a/source/blender/editors/mesh/mesh_intern.h
+++ b/source/blender/editors/mesh/mesh_intern.h
@@ -216,6 +216,7 @@ void MESH_OT_fill_holes(struct wmOperatorType *ot);
void MESH_OT_beautify_fill(struct wmOperatorType *ot);
void MESH_OT_quads_convert_to_tris(struct wmOperatorType *ot);
void MESH_OT_tris_convert_to_quads(struct wmOperatorType *ot);
+void MESH_OT_decimate(struct wmOperatorType *ot);
void MESH_OT_dissolve_verts(struct wmOperatorType *ot);
void MESH_OT_dissolve_edges(struct wmOperatorType *ot);
void MESH_OT_dissolve_faces(struct wmOperatorType *ot);
diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c
index 2853ae3a84c..697a92f36d1 100644
--- a/source/blender/editors/mesh/mesh_ops.c
+++ b/source/blender/editors/mesh/mesh_ops.c
@@ -106,6 +106,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_beautify_fill);
WM_operatortype_append(MESH_OT_quads_convert_to_tris);
WM_operatortype_append(MESH_OT_tris_convert_to_quads);
+ WM_operatortype_append(MESH_OT_decimate);
WM_operatortype_append(MESH_OT_dissolve_verts);
WM_operatortype_append(MESH_OT_dissolve_edges);
WM_operatortype_append(MESH_OT_dissolve_faces);
diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c
index 1b2d4b7c130..b26989113d4 100644
--- a/source/blender/editors/mesh/meshtools.c
+++ b/source/blender/editors/mesh/meshtools.c
@@ -172,7 +172,7 @@ int join_mesh_exec(bContext *C, wmOperator *op)
*/
if (key) {
/* make a duplicate copy that will only be used here... (must remember to free it!) */
- nkey = BKE_key_copy(key);
+ nkey = BKE_key_copy(bmain, key);
/* for all keys in old block, clear data-arrays */
for (kb = key->block.first; kb; kb = kb->next) {
@@ -540,7 +540,7 @@ int join_mesh_exec(bContext *C, wmOperator *op)
if (matmap) MEM_freeN(matmap);
/* other mesh users */
- test_object_materials(bmain, (ID *)me);
+ test_all_objects_materials(bmain, (ID *)me);
/* free temp copy of destination shapekeys (if applicable) */
if (nkey) {
@@ -564,21 +564,10 @@ int join_mesh_exec(bContext *C, wmOperator *op)
BKE_key_sort(key);
}
-
DAG_relations_tag_update(bmain); // removed objects, need to rebuild dag
-#if 0
- ED_object_editmode_enter(C, EM_WAITCURSOR);
- ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR | EM_DO_UNDO);
-#else
- /* toggle editmode using lower level functions so this can be called from python */
- EDBM_mesh_make(scene->toolsettings, ob, false);
- EDBM_mesh_load(ob);
- EDBM_mesh_free(me->edit_btmesh);
- MEM_freeN(me->edit_btmesh);
- me->edit_btmesh = NULL;
DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA);
-#endif
+
WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c
index 08ed9813456..57e08d36f85 100644
--- a/source/blender/editors/object/object_add.c
+++ b/source/blender/editors/object/object_add.c
@@ -48,6 +48,7 @@
#include "DNA_scene_types.h"
#include "DNA_vfont_types.h"
#include "DNA_actuator_types.h"
+#include "DNA_gpencil_types.h"
#include "BLI_utildefines.h"
#include "BLI_ghash.h"
@@ -1149,10 +1150,22 @@ static int object_delete_exec(bContext *C, wmOperator *op)
}
else if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) {
BKE_reportf(op->reports, RPT_WARNING,
- "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user",
- base->object->id.name + 2, scene->id.name + 2);
+ "Cannot delete object '%s' from scene '%s', indirectly used objects need at least one user",
+ base->object->id.name + 2, scene->id.name + 2);
continue;
}
+ /* remove from Grease Pencil parent */
+ for (bGPdata *gpd = bmain->gpencil.first; gpd; gpd = gpd->id.next) {
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ if (gpl->parent != NULL) {
+ Object *ob = gpl->parent;
+ Object *curob = base->object;
+ if (ob == curob) {
+ gpl->parent = NULL;
+ }
+ }
+ }
+ }
/* deselect object -- it could be used in other scenes */
base->object->flag &= ~SELECT;
@@ -1166,7 +1179,7 @@ static int object_delete_exec(bContext *C, wmOperator *op)
Base *base_other;
for (scene_iter = bmain->scene.first; scene_iter; scene_iter = scene_iter->id.next) {
- if (scene_iter != scene && !(scene_iter->id.lib)) {
+ if (scene_iter != scene && !ID_IS_LINKED_DATABLOCK(scene_iter)) {
base_other = BKE_scene_base_find(scene_iter, base->object);
if (base_other) {
if (is_indirectly_used && ID_REAL_USERS(base->object) <= 1) {
@@ -1303,7 +1316,7 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base,
for (dob = lb->first; dob; dob = dob->next) {
Base *basen;
- Object *ob = BKE_object_copy(dob->ob);
+ Object *ob = BKE_object_copy(bmain, dob->ob);
/* font duplis can have a totcol without material, we get them from parent
* should be implemented better...
@@ -1519,11 +1532,12 @@ static int convert_poll(bContext *C)
Object *obact = CTX_data_active_object(C);
Scene *scene = CTX_data_scene(C);
- return (!scene->id.lib && obact && scene->obedit != obact && (obact->flag & SELECT) && !(obact->id.lib));
+ return (!ID_IS_LINKED_DATABLOCK(scene) && obact && scene->obedit != obact &&
+ (obact->flag & SELECT) && !ID_IS_LINKED_DATABLOCK(obact));
}
/* Helper for convert_exec */
-static Base *duplibase_for_convert(Scene *scene, Base *base, Object *ob)
+static Base *duplibase_for_convert(Main *bmain, Scene *scene, Base *base, Object *ob)
{
Object *obn;
Base *basen;
@@ -1532,7 +1546,7 @@ static Base *duplibase_for_convert(Scene *scene, Base *base, Object *ob)
ob = base->object;
}
- obn = BKE_object_copy(ob);
+ obn = BKE_object_copy(bmain, ob);
DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
basen = MEM_mallocN(sizeof(Base), "duplibase");
@@ -1612,7 +1626,7 @@ static int convert_exec(bContext *C, wmOperator *op)
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(scene, base, NULL);
+ basen = duplibase_for_convert(bmain, scene, base, NULL);
newob = basen->object;
/* decrement original mesh's usage count */
@@ -1620,7 +1634,7 @@ static int convert_exec(bContext *C, wmOperator *op)
id_us_min(&me->id);
/* make a new copy of the mesh */
- newob->data = BKE_mesh_copy(me);
+ newob->data = BKE_mesh_copy(bmain, me);
}
else {
newob = ob;
@@ -1637,7 +1651,7 @@ static int convert_exec(bContext *C, wmOperator *op)
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(scene, base, NULL);
+ basen = duplibase_for_convert(bmain, scene, base, NULL);
newob = basen->object;
/* decrement original mesh's usage count */
@@ -1645,7 +1659,7 @@ static int convert_exec(bContext *C, wmOperator *op)
id_us_min(&me->id);
/* make a new copy of the mesh */
- newob->data = BKE_mesh_copy(me);
+ newob->data = BKE_mesh_copy(bmain, me);
}
else {
newob = ob;
@@ -1669,14 +1683,14 @@ static int convert_exec(bContext *C, wmOperator *op)
ob->flag |= OB_DONE;
if (keep_original) {
- basen = duplibase_for_convert(scene, base, NULL);
+ basen = duplibase_for_convert(bmain, scene, base, NULL);
newob = basen->object;
/* decrement original curve's usage count */
id_us_min(&((Curve *)newob->data)->id);
/* make a new copy of the curve */
- newob->data = BKE_curve_copy(ob->data);
+ newob->data = BKE_curve_copy(bmain, ob->data);
}
else {
newob = ob;
@@ -1740,14 +1754,14 @@ static int convert_exec(bContext *C, wmOperator *op)
if (target == OB_MESH) {
if (keep_original) {
- basen = duplibase_for_convert(scene, base, NULL);
+ basen = duplibase_for_convert(bmain, scene, base, NULL);
newob = basen->object;
/* decrement original curve's usage count */
id_us_min(&((Curve *)newob->data)->id);
/* make a new copy of the curve */
- newob->data = BKE_curve_copy(ob->data);
+ newob->data = BKE_curve_copy(bmain, ob->data);
}
else {
newob = ob;
@@ -1775,7 +1789,7 @@ static int convert_exec(bContext *C, wmOperator *op)
if (!(baseob->flag & OB_DONE)) {
baseob->flag |= OB_DONE;
- basen = duplibase_for_convert(scene, base, baseob);
+ basen = duplibase_for_convert(bmain, scene, base, baseob);
newob = basen->object;
mb = newob->data;
@@ -1913,7 +1927,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
; /* nothing? */
}
else {
- obn = BKE_object_copy(ob);
+ obn = BKE_object_copy(bmain, ob);
DAG_id_tag_update(&obn->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
basen = MEM_mallocN(sizeof(Base), "duplibase");
@@ -1944,7 +1958,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (id) {
ID_NEW_US(obn->mat[a])
else
- obn->mat[a] = BKE_material_copy(obn->mat[a]);
+ obn->mat[a] = BKE_material_copy(bmain, obn->mat[a]);
id_us_min(id);
if (dupflag & USER_DUP_ACT) {
@@ -1960,7 +1974,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (id) {
ID_NEW_US(psys->part)
else
- psys->part = BKE_particlesettings_copy(psys->part);
+ psys->part = BKE_particlesettings_copy(bmain, psys->part);
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(&psys->part->id);
@@ -1979,7 +1993,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag & USER_DUP_MESH) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_mesh_copy(obn->data);
+ obn->data = BKE_mesh_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -1989,7 +2003,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag & USER_DUP_CURVE) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_curve_copy(obn->data);
+ obn->data = BKE_curve_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -1999,7 +2013,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag & USER_DUP_SURF) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_curve_copy(obn->data);
+ obn->data = BKE_curve_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -2009,7 +2023,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag & USER_DUP_FONT) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_curve_copy(obn->data);
+ obn->data = BKE_curve_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -2019,7 +2033,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag & USER_DUP_MBALL) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_mball_copy(obn->data);
+ obn->data = BKE_mball_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -2029,7 +2043,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag & USER_DUP_LAMP) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_lamp_copy(obn->data);
+ obn->data = BKE_lamp_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -2042,7 +2056,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag & USER_DUP_ARM) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_armature_copy(obn->data);
+ obn->data = BKE_armature_copy(bmain, obn->data);
BKE_pose_rebuild(obn, obn->data);
didit = 1;
}
@@ -2053,7 +2067,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag != 0) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_lattice_copy(obn->data);
+ obn->data = BKE_lattice_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -2063,7 +2077,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag != 0) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_camera_copy(obn->data);
+ obn->data = BKE_camera_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -2073,7 +2087,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (dupflag != 0) {
ID_NEW_US2(obn->data)
else {
- obn->data = BKE_speaker_copy(obn->data);
+ obn->data = BKE_speaker_copy(bmain, obn->data);
didit = 1;
}
id_us_min(id);
@@ -2112,7 +2126,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, Base *base
if (id) {
ID_NEW_US((*matarar)[a])
else
- (*matarar)[a] = BKE_material_copy((*matarar)[a]);
+ (*matarar)[a] = BKE_material_copy(bmain, (*matarar)[a]);
id_us_min(id);
}
}
@@ -2309,7 +2323,7 @@ static int join_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (!ob || ob->id.lib) return 0;
+ if (!ob || ID_IS_LINKED_DATABLOCK(ob)) return 0;
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE))
return ED_operator_screenactive(C);
@@ -2362,7 +2376,7 @@ static int join_shapes_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
- if (!ob || ob->id.lib) return 0;
+ if (!ob || ID_IS_LINKED_DATABLOCK(ob)) return 0;
/* only meshes supported at the moment */
if (ob->type == OB_MESH)
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index d7b7fd7040e..db8a4c1960f 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -584,7 +584,7 @@ static int edit_constraint_poll_generic(bContext *C, StructRNA *rna_type)
return 0;
}
- if (ob->id.lib || (ptr.id.data && ((ID *)ptr.id.data)->lib)) {
+ if (ID_IS_LINKED_DATABLOCK(ob) || (ptr.id.data && ID_IS_LINKED_DATABLOCK(ptr.id.data))) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit library data");
return 0;
}
diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c
index 47ee6752e51..acee69daab7 100644
--- a/source/blender/editors/object/object_data_transfer.c
+++ b/source/blender/editors/object/object_data_transfer.c
@@ -300,7 +300,7 @@ static void data_transfer_exec_preprocess_objects(
}
me = ob->data;
- if (me->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(me)) {
/* Do not transfer to linked data, not supported. */
BKE_reportf(op->reports, RPT_WARNING, "Skipping object '%s', linked data '%s' cannot be modified",
ob->id.name + 2, me->id.name + 2);
@@ -330,7 +330,7 @@ static bool data_transfer_exec_is_object_valid(
me->id.tag &= ~LIB_TAG_DOIT;
return true;
}
- else if (me->id.lib == NULL) {
+ else if (!ID_IS_LINKED_DATABLOCK(me)) {
/* Do not transfer apply operation more than once. */
/* XXX This is not nice regarding vgroups, which are half-Object data... :/ */
BKE_reportf(op->reports, RPT_WARNING,
@@ -387,7 +387,7 @@ static int data_transfer_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
- if (reverse_transfer && ((ID *)(ob_src->data))->lib) {
+ if (reverse_transfer && ID_IS_LINKED_DATABLOCK(ob_src->data)) {
/* Do not transfer to linked data, not supported. */
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index 01a567931b3..3dc7d8ebd4b 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -470,7 +470,7 @@ void ED_object_editmode_enter(bContext *C, int flag)
View3D *v3d = NULL;
bool ok = false;
- if (scene->id.lib) return;
+ if (ID_IS_LINKED_DATABLOCK(scene)) return;
if (sa && sa->spacetype == SPACE_VIEW3D)
v3d = sa->spacedata.first;
@@ -539,7 +539,7 @@ void ED_object_editmode_enter(bContext *C, int flag)
* BKE_object_obdata_is_libdata that prevent the bugfix #6614, so
* i add this little hack here.
*/
- if (arm->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(arm)) {
error_libdata();
return;
}
@@ -621,7 +621,7 @@ static int editmode_toggle_poll(bContext *C)
Object *ob = CTX_data_active_object(C);
/* covers proxies too */
- if (ELEM(NULL, ob, ob->data) || ((ID *)ob->data)->lib)
+ if (ELEM(NULL, ob, ob->data) || ID_IS_LINKED_DATABLOCK(ob->data))
return 0;
/* if hidden but in edit mode, we still display */
@@ -848,7 +848,7 @@ static void copy_attr(Main *bmain, Scene *scene, View3D *v3d, short event)
Nurb *nu;
bool do_depgraph_update = false;
- if (scene->id.lib) return;
+ if (ID_IS_LINKED_DATABLOCK(scene)) return;
if (!(ob = OBACT)) return;
@@ -1436,7 +1436,7 @@ static int shade_smooth_exec(bContext *C, wmOperator *op)
{
data = ob->data;
- if (data && data->lib) {
+ if (data && ID_IS_LINKED_DATABLOCK(data)) {
linked_data = true;
continue;
}
@@ -1519,7 +1519,7 @@ static void UNUSED_FUNCTION(image_aspect) (Scene *scene, View3D *v3d)
int a, b, done;
if (scene->obedit) return; // XXX get from context
- if (scene->id.lib) return;
+ if (ID_IS_LINKED_DATABLOCK(scene)) return;
for (base = FIRSTBASE; base; base = base->next) {
if (TESTBASELIB(v3d, base)) {
diff --git a/source/blender/editors/object/object_group.c b/source/blender/editors/object/object_group.c
index bcdd170c53c..0fe43c44d7d 100644
--- a/source/blender/editors/object/object_group.c
+++ b/source/blender/editors/object/object_group.c
@@ -440,7 +440,7 @@ static int group_link_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
/* Early return check, if the object is already in group
- * we could sckip all the dependency check and just consider
+ * we could skip all the dependency check and just consider
* operator is finished.
*/
if (BKE_group_object_exists(group, ob)) {
diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c
index 264945b00bf..b44ddf925a8 100644
--- a/source/blender/editors/object/object_modifier.c
+++ b/source/blender/editors/object/object_modifier.c
@@ -817,9 +817,9 @@ int edit_modifier_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", rna_type);
Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C);
- if (!ob || ob->id.lib) return 0;
+ if (!ob || ID_IS_LINKED_DATABLOCK(ob)) return 0;
if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) return 0;
- if (ptr.id.data && ((ID *)ptr.id.data)->lib) return 0;
+ if (ptr.id.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0;
return 1;
}
diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c
index bda4b6630fe..7c7e80edbf7 100644
--- a/source/blender/editors/object/object_ops.c
+++ b/source/blender/editors/object/object_ops.c
@@ -368,9 +368,21 @@ void ED_keymap_object(wmKeyConfig *keyconf)
WM_keymap_verify_item(keymap, "OBJECT_OT_constraint_add_with_targets", CKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0);
WM_keymap_verify_item(keymap, "OBJECT_OT_constraints_clear", CKEY, KM_PRESS, KM_CTRL | KM_ALT, 0);
- WM_keymap_verify_item(keymap, "OBJECT_OT_location_clear", GKEY, KM_PRESS, KM_ALT, 0);
- WM_keymap_verify_item(keymap, "OBJECT_OT_rotation_clear", RKEY, KM_PRESS, KM_ALT, 0);
- WM_keymap_verify_item(keymap, "OBJECT_OT_scale_clear", SKEY, KM_PRESS, KM_ALT, 0);
+
+ kmi = WM_keymap_add_item(keymap, "OBJECT_OT_location_clear", GKEY, KM_PRESS, KM_ALT, 0);
+ RNA_boolean_set(kmi->ptr, "clear_delta", false);
+ kmi = WM_keymap_add_item(keymap, "OBJECT_OT_rotation_clear", RKEY, KM_PRESS, KM_ALT, 0);
+ RNA_boolean_set(kmi->ptr, "clear_delta", false);
+ kmi = WM_keymap_add_item(keymap, "OBJECT_OT_scale_clear", SKEY, KM_PRESS, KM_ALT, 0);
+ RNA_boolean_set(kmi->ptr, "clear_delta", false);
+
+ kmi = WM_keymap_add_item(keymap, "OBJECT_OT_location_clear", GKEY, KM_PRESS, KM_ALT | KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "clear_delta", true);
+ kmi = WM_keymap_add_item(keymap, "OBJECT_OT_rotation_clear", RKEY, KM_PRESS, KM_ALT | KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "clear_delta", true);
+ kmi = WM_keymap_add_item(keymap, "OBJECT_OT_scale_clear", SKEY, KM_PRESS, KM_ALT | KM_SHIFT, 0);
+ RNA_boolean_set(kmi->ptr, "clear_delta", true);
+
WM_keymap_verify_item(keymap, "OBJECT_OT_origin_clear", OKEY, KM_PRESS, KM_ALT, 0);
WM_keymap_add_item(keymap, "OBJECT_OT_hide_view_clear", HKEY, KM_PRESS, KM_ALT, 0);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 94d1a258063..ff2accf9d82 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -48,6 +48,7 @@
#include "DNA_world_types.h"
#include "DNA_object_types.h"
#include "DNA_vfont_types.h"
+#include "DNA_gpencil_types.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
@@ -291,17 +292,17 @@ static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Object *ob = ED_object_active_context(C);
/* sanity checks */
- if (!scene || scene->id.lib || !ob)
+ if (!scene || ID_IS_LINKED_DATABLOCK(scene) || !ob)
return OPERATOR_CANCELLED;
/* Get object to work on - use a menu if we need to... */
- if (ob->dup_group && ob->dup_group->id.lib) {
+ if (ob->dup_group && ID_IS_LINKED_DATABLOCK(ob->dup_group)) {
/* gives menu with list of objects in group */
/* proxy_group_objects_menu(C, op, ob, ob->dup_group); */
WM_enum_search_invoke(C, op, event);
return OPERATOR_CANCELLED;
}
- else if (ob->id.lib) {
+ else if (ID_IS_LINKED_DATABLOCK(ob)) {
uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION);
uiLayout *layout = UI_popup_menu_layout(pup);
@@ -1466,7 +1467,7 @@ static int make_links_scene_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (scene_to->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(scene_to)) {
BKE_report(op->reports, RPT_ERROR, "Cannot link objects into a linked scene");
return OPERATOR_CANCELLED;
}
@@ -1564,7 +1565,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
ob_dst->data = obdata_id;
/* if amount of material indices changed: */
- test_object_materials(bmain, ob_dst->data);
+ test_object_materials(ob_dst, ob_dst->data);
DAG_id_tag_update(&ob_dst->id, OB_RECALC_DATA);
break;
@@ -1579,7 +1580,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
case MAKE_LINKS_ANIMDATA:
BKE_animdata_copy_id((ID *)ob_dst, (ID *)ob_src, false);
if (ob_dst->data && ob_src->data) {
- if (obdata_id->lib) {
+ if (ID_IS_LINKED_DATABLOCK(obdata_id)) {
is_lib = true;
break;
}
@@ -1621,7 +1622,7 @@ static int make_links_data_exec(bContext *C, wmOperator *op)
Curve *cu_src = ob_src->data;
Curve *cu_dst = ob_dst->data;
- if (obdata_id->lib) {
+ if (ID_IS_LINKED_DATABLOCK(obdata_id)) {
is_lib = true;
break;
}
@@ -1749,9 +1750,9 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in
ob = base->object;
if ((base->flag & flag) == flag) {
- if (ob->id.lib == NULL && ob->id.us > 1) {
+ if (!ID_IS_LINKED_DATABLOCK(ob) && ob->id.us > 1) {
/* base gets copy of object */
- obn = BKE_object_copy(ob);
+ obn = BKE_object_copy(bmain, ob);
base->object = obn;
if (copy_groups) {
@@ -1762,6 +1763,14 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in
else {
/* copy already clears */
}
+ /* remap gpencil parenting */
+ bGPdata *gpd = scene->gpd;
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ if (gpl->parent == ob) {
+ gpl->parent = obn;
+ }
+ }
+
base->flag = obn->flag;
id_us_min(&ob->id);
@@ -1784,7 +1793,7 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in
}
if (all_duplicated) {
- groupn = BKE_group_copy(group);
+ groupn = BKE_group_copy(bmain, group);
for (go = groupn->gobject.first; go; go = go->next)
go->ob = (Object *)go->ob->id.newid;
@@ -1821,21 +1830,21 @@ void ED_object_single_user(Main *bmain, Scene *scene, Object *ob)
single_object_users(bmain, scene, NULL, OB_DONE, copy_groups);
}
-static void new_id_matar(Material **matar, const int totcol)
+static void new_id_matar(Main *bmain, Material **matar, const int totcol)
{
ID *id;
int a;
for (a = 0; a < totcol; a++) {
id = (ID *)matar[a];
- if (id && id->lib == NULL) {
+ if (id && !ID_IS_LINKED_DATABLOCK(id)) {
if (id->newid) {
matar[a] = (Material *)id->newid;
id_us_plus(id->newid);
id_us_min(id);
}
else if (id->us > 1) {
- matar[a] = BKE_material_copy(matar[a]);
+ matar[a] = BKE_material_copy(bmain, matar[a]);
id_us_min(id);
id->newid = (ID *)matar[a];
}
@@ -1857,15 +1866,15 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag)
for (base = FIRSTBASE; base; base = base->next) {
ob = base->object;
- if (ob->id.lib == NULL && (base->flag & flag) == flag) {
+ if (!ID_IS_LINKED_DATABLOCK(ob) && (base->flag & flag) == flag) {
id = ob->data;
- if (id && id->us > 1 && id->lib == NULL) {
+ if (id && id->us > 1 && !ID_IS_LINKED_DATABLOCK(id)) {
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
switch (ob->type) {
case OB_LAMP:
- ob->data = la = BKE_lamp_copy(ob->data);
+ ob->data = la = BKE_lamp_copy(bmain, ob->data);
for (a = 0; a < MAX_MTEX; a++) {
if (la->mtex[a]) {
ID_NEW(la->mtex[a]->object);
@@ -1873,37 +1882,37 @@ static void single_obdata_users(Main *bmain, Scene *scene, const int flag)
}
break;
case OB_CAMERA:
- ob->data = BKE_camera_copy(ob->data);
+ ob->data = BKE_camera_copy(bmain, ob->data);
break;
case OB_MESH:
- ob->data = me = BKE_mesh_copy(ob->data);
+ ob->data = me = BKE_mesh_copy(bmain, ob->data);
if (me->key)
BKE_animdata_copy_id_action((ID *)me->key);
break;
case OB_MBALL:
- ob->data = BKE_mball_copy(ob->data);
+ ob->data = BKE_mball_copy(bmain, ob->data);
break;
case OB_CURVE:
case OB_SURF:
case OB_FONT:
- ob->data = cu = BKE_curve_copy(ob->data);
+ ob->data = cu = BKE_curve_copy(bmain, ob->data);
ID_NEW(cu->bevobj);
ID_NEW(cu->taperobj);
if (cu->key)
BKE_animdata_copy_id_action((ID *)cu->key);
break;
case OB_LATTICE:
- ob->data = lat = BKE_lattice_copy(ob->data);
+ ob->data = lat = BKE_lattice_copy(bmain, ob->data);
if (lat->key)
BKE_animdata_copy_id_action((ID *)lat->key);
break;
case OB_ARMATURE:
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
- ob->data = BKE_armature_copy(ob->data);
+ ob->data = BKE_armature_copy(bmain, ob->data);
BKE_pose_rebuild(ob, ob->data);
break;
case OB_SPEAKER:
- ob->data = BKE_speaker_copy(ob->data);
+ ob->data = BKE_speaker_copy(bmain, ob->data);
break;
default:
if (G.debug & G_DEBUG)
@@ -1938,14 +1947,14 @@ static void single_object_action_users(Scene *scene, const int flag)
for (base = FIRSTBASE; base; base = base->next) {
ob = base->object;
- if (ob->id.lib == NULL && (flag == 0 || (base->flag & SELECT)) ) {
+ if (!ID_IS_LINKED_DATABLOCK(ob) && (flag == 0 || (base->flag & SELECT)) ) {
DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
BKE_animdata_copy_id_action(&ob->id);
}
}
}
-static void single_mat_users(Scene *scene, const int flag, const bool do_textures)
+static void single_mat_users(Main *bmain, Scene *scene, const int flag, const bool do_textures)
{
Object *ob;
Base *base;
@@ -1955,14 +1964,14 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture
for (base = FIRSTBASE; base; base = base->next) {
ob = base->object;
- if (ob->id.lib == NULL && (flag == 0 || (base->flag & SELECT)) ) {
+ if (!ID_IS_LINKED_DATABLOCK(ob) && (flag == 0 || (base->flag & SELECT)) ) {
for (a = 1; a <= ob->totcol; a++) {
ma = give_current_material(ob, a);
if (ma) {
/* do not test for LIB_TAG_NEW: this functions guaranteed delivers single_users! */
if (ma->id.us > 1) {
- man = BKE_material_copy(ma);
+ man = BKE_material_copy(bmain, ma);
BKE_animdata_copy_id_action(&man->id);
man->id.us = 0;
@@ -1973,7 +1982,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture
if (ma->mtex[b] && (tex = ma->mtex[b]->tex)) {
if (tex->id.us > 1) {
id_us_min(&tex->id);
- tex = BKE_texture_copy(tex);
+ tex = BKE_texture_copy(bmain, tex);
BKE_animdata_copy_id_action(&tex->id);
man->mtex[b]->tex = tex;
}
@@ -1987,7 +1996,7 @@ static void single_mat_users(Scene *scene, const int flag, const bool do_texture
}
}
-static void do_single_tex_user(Tex **from)
+static void do_single_tex_user(Main *bmain, Tex **from)
{
Tex *tex, *texn;
@@ -2000,7 +2009,7 @@ static void do_single_tex_user(Tex **from)
id_us_min(&tex->id);
}
else if (tex->id.us > 1) {
- texn = BKE_texture_copy(tex);
+ texn = BKE_texture_copy(bmain, tex);
BKE_animdata_copy_id_action(&texn->id);
tex->id.newid = (ID *)texn;
id_us_min(&tex->id);
@@ -2020,7 +2029,7 @@ static void single_tex_users_expand(Main *bmain)
if (ma->id.tag & LIB_TAG_NEW) {
for (b = 0; b < MAX_MTEX; b++) {
if (ma->mtex[b] && ma->mtex[b]->tex) {
- do_single_tex_user(&(ma->mtex[b]->tex));
+ do_single_tex_user(bmain, &(ma->mtex[b]->tex));
}
}
}
@@ -2030,7 +2039,7 @@ static void single_tex_users_expand(Main *bmain)
if (la->id.tag & LIB_TAG_NEW) {
for (b = 0; b < MAX_MTEX; b++) {
if (la->mtex[b] && la->mtex[b]->tex) {
- do_single_tex_user(&(la->mtex[b]->tex));
+ do_single_tex_user(bmain, &(la->mtex[b]->tex));
}
}
}
@@ -2040,7 +2049,7 @@ static void single_tex_users_expand(Main *bmain)
if (wo->id.tag & LIB_TAG_NEW) {
for (b = 0; b < MAX_MTEX; b++) {
if (wo->mtex[b] && wo->mtex[b]->tex) {
- do_single_tex_user(&(wo->mtex[b]->tex));
+ do_single_tex_user(bmain, &(wo->mtex[b]->tex));
}
}
}
@@ -2059,19 +2068,19 @@ static void single_mat_users_expand(Main *bmain)
for (ob = bmain->object.first; ob; ob = ob->id.next)
if (ob->id.tag & LIB_TAG_NEW)
- new_id_matar(ob->mat, ob->totcol);
+ new_id_matar(bmain, ob->mat, ob->totcol);
for (me = bmain->mesh.first; me; me = me->id.next)
if (me->id.tag & LIB_TAG_NEW)
- new_id_matar(me->mat, me->totcol);
+ new_id_matar(bmain, me->mat, me->totcol);
for (cu = bmain->curve.first; cu; cu = cu->id.next)
if (cu->id.tag & LIB_TAG_NEW)
- new_id_matar(cu->mat, cu->totcol);
+ new_id_matar(bmain, cu->mat, cu->totcol);
for (mb = bmain->mball.first; mb; mb = mb->id.next)
if (mb->id.tag & LIB_TAG_NEW)
- new_id_matar(mb->mat, mb->totcol);
+ new_id_matar(bmain, mb->mat, mb->totcol);
/* material imats */
for (ma = bmain->mat.first; ma; ma = ma->id.next)
@@ -2104,11 +2113,11 @@ static void make_local_makelocalmaterial(Material *ma)
AnimData *adt;
int b;
- id_make_local(&ma->id, false);
+ id_make_local(G.main, &ma->id, false, false);
for (b = 0; b < MAX_MTEX; b++)
if (ma->mtex[b] && ma->mtex[b]->tex)
- id_make_local(&ma->mtex[b]->tex->id, false);
+ id_make_local(G.main, &ma->mtex[b]->tex->id, false, false);
adt = BKE_animdata_from_id(&ma->id);
if (adt) BKE_animdata_make_local(adt);
@@ -2184,7 +2193,7 @@ static bool make_local_all__instance_indirect_unused(Main *bmain, Scene *scene)
bool changed = false;
for (ob = bmain->object.first; ob; ob = ob->id.next) {
- if (ob->id.lib && (ob->id.us == 0)) {
+ if (ID_IS_LINKED_DATABLOCK(ob) && (ob->id.us == 0)) {
Base *base;
id_us_plus(&ob->id);
@@ -2237,7 +2246,7 @@ static int make_local_exec(bContext *C, wmOperator *op)
}
if (ob->id.lib)
- id_make_local(&ob->id, false);
+ id_make_local(bmain, &ob->id, false, false);
}
CTX_DATA_END;
@@ -2259,7 +2268,7 @@ static int make_local_exec(bContext *C, wmOperator *op)
id = ob->data;
if (id && (ELEM(mode, MAKE_LOCAL_SELECT_OBDATA, MAKE_LOCAL_SELECT_OBDATA_MATERIAL))) {
- id_make_local(id, false);
+ id_make_local(bmain, id, false, false);
adt = BKE_animdata_from_id(id);
if (adt) BKE_animdata_make_local(adt);
@@ -2275,7 +2284,7 @@ static int make_local_exec(bContext *C, wmOperator *op)
}
for (psys = ob->particlesystem.first; psys; psys = psys->next)
- id_make_local(&psys->part->id, false);
+ id_make_local(bmain, &psys->part->id, false, false);
adt = BKE_animdata_from_id(&ob->id);
if (adt) BKE_animdata_make_local(adt);
@@ -2294,7 +2303,7 @@ static int make_local_exec(bContext *C, wmOperator *op)
for (b = 0; b < MAX_MTEX; b++)
if (la->mtex[b] && la->mtex[b]->tex)
- id_make_local(&la->mtex[b]->tex->id, false);
+ id_make_local(bmain, &la->mtex[b]->tex->id, false, false);
}
else {
for (a = 0; a < ob->totcol; a++) {
@@ -2376,7 +2385,7 @@ static int make_single_user_exec(bContext *C, wmOperator *op)
}
if (RNA_boolean_get(op->ptr, "material")) {
- single_mat_users(scene, flag, RNA_boolean_get(op->ptr, "texture"));
+ single_mat_users(bmain, scene, flag, RNA_boolean_get(op->ptr, "texture"));
}
#if 0 /* can't do this separate from materials */
diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c
index 0cbbe46f461..f1b7186f8a1 100644
--- a/source/blender/editors/object/object_select.c
+++ b/source/blender/editors/object/object_select.c
@@ -577,7 +577,6 @@ static bool select_grouped_parent(bContext *C) /* Makes parent active and de-sel
/* can be NULL if parent in other scene */
if (baspar && BASE_SELECTABLE(v3d, baspar)) {
- ED_base_object_select(basact, BA_DESELECT);
ED_base_object_select(baspar, BA_SELECT);
ED_base_object_activate(C, baspar);
changed = true;
diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c
index 39bd34456be..e04114761e4 100644
--- a/source/blender/editors/object/object_shapekey.c
+++ b/source/blender/editors/object/object_shapekey.c
@@ -225,7 +225,7 @@ static int shape_key_mode_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib && data && !data->lib && ob->mode != OB_MODE_EDIT);
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data) && ob->mode != OB_MODE_EDIT);
}
static int shape_key_mode_exists_poll(bContext *C)
@@ -234,7 +234,7 @@ static int shape_key_mode_exists_poll(bContext *C)
ID *data = (ob) ? ob->data : NULL;
/* same as shape_key_mode_poll */
- return (ob && !ob->id.lib && data && !data->lib && ob->mode != OB_MODE_EDIT) &&
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data) && ob->mode != OB_MODE_EDIT) &&
/* check a keyblock exists */
(BKE_keyblock_from_object(ob) != NULL);
}
@@ -246,14 +246,15 @@ static int shape_key_move_poll(bContext *C)
ID *data = (ob) ? ob->data : NULL;
Key *key = BKE_key_from_object(ob);
- return (ob && !ob->id.lib && data && !data->lib && ob->mode != OB_MODE_EDIT && key && key->totkey > 1);
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data) &&
+ ob->mode != OB_MODE_EDIT && key && key->totkey > 1);
}
static int shape_key_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib && data && !data->lib);
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data));
}
static int shape_key_add_exec(bContext *C, wmOperator *op)
diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c
index d2bbb73b597..4d7d7df0d2f 100644
--- a/source/blender/editors/object/object_transform.c
+++ b/source/blender/editors/object/object_transform.c
@@ -76,60 +76,87 @@
/*************************** Clear Transformation ****************************/
/* clear location of object */
-static void object_clear_loc(Object *ob)
+static void object_clear_loc(Object *ob, const bool clear_delta)
{
/* clear location if not locked */
- if ((ob->protectflag & OB_LOCK_LOCX) == 0)
- ob->loc[0] = ob->dloc[0] = 0.0f;
- if ((ob->protectflag & OB_LOCK_LOCY) == 0)
- ob->loc[1] = ob->dloc[1] = 0.0f;
- if ((ob->protectflag & OB_LOCK_LOCZ) == 0)
- ob->loc[2] = ob->dloc[2] = 0.0f;
+ if ((ob->protectflag & OB_LOCK_LOCX) == 0) {
+ ob->loc[0] = 0.0f;
+ if (clear_delta) ob->dloc[0] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_LOCY) == 0) {
+ ob->loc[1] = 0.0f;
+ if (clear_delta) ob->dloc[1] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_LOCZ) == 0) {
+ ob->loc[2] = 0.0f;
+ if (clear_delta) ob->dloc[2] = 0.0f;
+ }
}
/* clear rotation of object */
-static void object_clear_rot(Object *ob)
+static void object_clear_rot(Object *ob, const bool clear_delta)
{
/* clear rotations that aren't locked */
if (ob->protectflag & (OB_LOCK_ROTX | OB_LOCK_ROTY | OB_LOCK_ROTZ | OB_LOCK_ROTW)) {
if (ob->protectflag & OB_LOCK_ROT4D) {
/* perform clamping on a component by component basis */
if (ob->rotmode == ROT_MODE_AXISANGLE) {
- if ((ob->protectflag & OB_LOCK_ROTW) == 0)
- ob->rotAngle = ob->drotAngle = 0.0f;
- if ((ob->protectflag & OB_LOCK_ROTX) == 0)
- ob->rotAxis[0] = ob->drotAxis[0] = 0.0f;
- if ((ob->protectflag & OB_LOCK_ROTY) == 0)
- ob->rotAxis[1] = ob->drotAxis[1] = 0.0f;
- if ((ob->protectflag & OB_LOCK_ROTZ) == 0)
- ob->rotAxis[2] = ob->drotAxis[2] = 0.0f;
+ if ((ob->protectflag & OB_LOCK_ROTW) == 0) {
+ ob->rotAngle = 0.0f;
+ if (clear_delta) ob->drotAngle = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTX) == 0) {
+ ob->rotAxis[0] = 0.0f;
+ if (clear_delta) ob->drotAxis[0] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTY) == 0) {
+ ob->rotAxis[1] = 0.0f;
+ if (clear_delta) ob->drotAxis[1] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTZ) == 0) {
+ ob->rotAxis[2] = 0.0f;
+ if (clear_delta) ob->drotAxis[2] = 0.0f;
+ }
/* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */
if (IS_EQF(ob->rotAxis[0], ob->rotAxis[1]) && IS_EQF(ob->rotAxis[1], ob->rotAxis[2]))
ob->rotAxis[1] = 1.0f;
- if (IS_EQF(ob->drotAxis[0], ob->drotAxis[1]) && IS_EQF(ob->drotAxis[1], ob->drotAxis[2]))
+ if (IS_EQF(ob->drotAxis[0], ob->drotAxis[1]) && IS_EQF(ob->drotAxis[1], ob->drotAxis[2]) && clear_delta)
ob->drotAxis[1] = 1.0f;
}
else if (ob->rotmode == ROT_MODE_QUAT) {
- if ((ob->protectflag & OB_LOCK_ROTW) == 0)
- ob->quat[0] = ob->dquat[0] = 1.0f;
- if ((ob->protectflag & OB_LOCK_ROTX) == 0)
- ob->quat[1] = ob->dquat[1] = 0.0f;
- if ((ob->protectflag & OB_LOCK_ROTY) == 0)
- ob->quat[2] = ob->dquat[2] = 0.0f;
- if ((ob->protectflag & OB_LOCK_ROTZ) == 0)
- ob->quat[3] = ob->dquat[3] = 0.0f;
-
+ if ((ob->protectflag & OB_LOCK_ROTW) == 0) {
+ ob->quat[0] = 1.0f;
+ if (clear_delta) ob->dquat[0] = 1.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTX) == 0) {
+ ob->quat[1] = 0.0f;
+ if (clear_delta) ob->dquat[1] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTY) == 0) {
+ ob->quat[2] = 0.0f;
+ if (clear_delta) ob->dquat[2] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTZ) == 0) {
+ ob->quat[3] = 0.0f;
+ if (clear_delta) ob->dquat[3] = 0.0f;
+ }
/* TODO: does this quat need normalizing now? */
}
else {
/* the flag may have been set for the other modes, so just ignore the extra flag... */
- if ((ob->protectflag & OB_LOCK_ROTX) == 0)
- ob->rot[0] = ob->drot[0] = 0.0f;
- if ((ob->protectflag & OB_LOCK_ROTY) == 0)
- ob->rot[1] = ob->drot[1] = 0.0f;
- if ((ob->protectflag & OB_LOCK_ROTZ) == 0)
- ob->rot[2] = ob->drot[2] = 0.0f;
+ if ((ob->protectflag & OB_LOCK_ROTX) == 0) {
+ ob->rot[0] = 0.0f;
+ if (clear_delta) ob->drot[0] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTY) == 0) {
+ ob->rot[1] = 0.0f;
+ if (clear_delta) ob->drot[1] = 0.0f;
+ }
+ if ((ob->protectflag & OB_LOCK_ROTZ) == 0) {
+ ob->rot[2] = 0.0f;
+ if (clear_delta) ob->drot[2] = 0.0f;
+ }
}
}
else {
@@ -175,34 +202,34 @@ static void object_clear_rot(Object *ob)
else {
if (ob->rotmode == ROT_MODE_QUAT) {
unit_qt(ob->quat);
- unit_qt(ob->dquat);
+ if (clear_delta) unit_qt(ob->dquat);
}
else if (ob->rotmode == ROT_MODE_AXISANGLE) {
unit_axis_angle(ob->rotAxis, &ob->rotAngle);
- unit_axis_angle(ob->drotAxis, &ob->drotAngle);
+ if (clear_delta) unit_axis_angle(ob->drotAxis, &ob->drotAngle);
}
else {
zero_v3(ob->rot);
- zero_v3(ob->drot);
+ if (clear_delta) zero_v3(ob->drot);
}
}
}
/* clear scale of object */
-static void object_clear_scale(Object *ob)
+static void object_clear_scale(Object *ob, const bool clear_delta)
{
/* clear scale factors which are not locked */
if ((ob->protectflag & OB_LOCK_SCALEX) == 0) {
- ob->dscale[0] = 1.0f;
ob->size[0] = 1.0f;
+ if (clear_delta) ob->dscale[0] = 1.0f;
}
if ((ob->protectflag & OB_LOCK_SCALEY) == 0) {
- ob->dscale[1] = 1.0f;
ob->size[1] = 1.0f;
+ if (clear_delta) ob->dscale[1] = 1.0f;
}
if ((ob->protectflag & OB_LOCK_SCALEZ) == 0) {
- ob->dscale[2] = 1.0f;
ob->size[2] = 1.0f;
+ if (clear_delta) ob->dscale[2] = 1.0f;
}
}
@@ -210,10 +237,12 @@ static void object_clear_scale(Object *ob)
/* generic exec for clear-transform operators */
static int object_clear_transform_generic_exec(bContext *C, wmOperator *op,
- void (*clear_func)(Object *), const char default_ksName[])
+ void (*clear_func)(Object *, const bool),
+ const char default_ksName[])
{
Scene *scene = CTX_data_scene(C);
KeyingSet *ks;
+ const bool clear_delta = RNA_boolean_get(op->ptr, "clear_delta");
/* sanity checks */
if (ELEM(NULL, clear_func, default_ksName)) {
@@ -231,10 +260,10 @@ static int object_clear_transform_generic_exec(bContext *C, wmOperator *op,
{
if (!(ob->mode & OB_MODE_WEIGHT_PAINT)) {
/* run provided clearing function */
- clear_func(ob);
-
+ clear_func(ob, clear_delta);
+
ED_autokeyframe_object(C, scene, ob, ks);
-
+
/* tag for updates */
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
}
@@ -268,6 +297,11 @@ void OBJECT_OT_location_clear(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+
+ /* properties */
+ ot->prop = RNA_def_boolean(ot->srna, "clear_delta", false, "Clear Delta",
+ "Clear delta location in addition to clearing the normal location transform");
}
static int object_rotation_clear_exec(bContext *C, wmOperator *op)
@@ -288,6 +322,10 @@ void OBJECT_OT_rotation_clear(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_boolean(ot->srna, "clear_delta", false, "Clear Delta",
+ "Clear delta rotation in addition to clearing the normal rotation transform");
}
static int object_scale_clear_exec(bContext *C, wmOperator *op)
@@ -308,6 +346,10 @@ void OBJECT_OT_scale_clear(wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_boolean(ot->srna, "clear_delta", false, "Clear Delta",
+ "Clear delta scale in addition to clearing the normal scale transform");
}
/* --------------- */
@@ -391,7 +433,7 @@ static int apply_objects_internal(bContext *C, ReportList *reports, bool apply_l
changed = false;
}
- if (obdata->lib) {
+ if (ID_IS_LINKED_DATABLOCK(obdata)) {
BKE_reportf(reports, RPT_ERROR,
"Cannot apply to library data: Object \"%s\", %s \"%s\", aborting",
ob->id.name + 2, BKE_idcode_to_name(GS(obdata->name)), obdata->name + 2);
@@ -792,7 +834,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
if (ob->data == NULL) {
/* special support for dupligroups */
if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group && (ob->dup_group->id.tag & LIB_TAG_DOIT) == 0) {
- if (ob->dup_group->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(ob->dup_group)) {
tot_lib_error++;
}
else {
@@ -817,7 +859,7 @@ static int object_origin_set_exec(bContext *C, wmOperator *op)
}
}
}
- else if (((ID *)ob->data)->lib) {
+ else if (ID_IS_LINKED_DATABLOCK(ob->data)) {
tot_lib_error++;
}
diff --git a/source/blender/editors/object/object_vgroup.c b/source/blender/editors/object/object_vgroup.c
index 414cc476be5..ad41fb23a69 100644
--- a/source/blender/editors/object/object_vgroup.c
+++ b/source/blender/editors/object/object_vgroup.c
@@ -2447,8 +2447,8 @@ static int vertex_group_poll(bContext *C)
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib &&
- data && !data->lib &&
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) &&
+ data && !ID_IS_LINKED_DATABLOCK(data) &&
OB_TYPE_SUPPORT_VGROUP(ob->type) &&
ob->defbase.first);
}
@@ -2457,7 +2457,8 @@ static int vertex_group_supported_poll(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib && OB_TYPE_SUPPORT_VGROUP(ob->type) && data && !data->lib);
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) && OB_TYPE_SUPPORT_VGROUP(ob->type) &&
+ data && !ID_IS_LINKED_DATABLOCK(data));
}
static int vertex_group_mesh_poll(bContext *C)
@@ -2465,8 +2466,8 @@ static int vertex_group_mesh_poll(bContext *C)
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib &&
- data && !data->lib &&
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) &&
+ data && !ID_IS_LINKED_DATABLOCK(data) &&
ob->type == OB_MESH &&
ob->defbase.first);
}
@@ -2475,7 +2476,7 @@ static int UNUSED_FUNCTION(vertex_group_mesh_supported_poll)(bContext *C)
{
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- return (ob && !ob->id.lib && ob->type == OB_MESH && data && !data->lib);
+ return (ob && !ID_IS_LINKED_DATABLOCK(ob) && ob->type == OB_MESH && data && !ID_IS_LINKED_DATABLOCK(data));
}
@@ -2484,7 +2485,7 @@ static int UNUSED_FUNCTION(vertex_group_poll_edit) (bContext *C)
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ob->id.lib && data && !data->lib))
+ if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data)))
return 0;
return BKE_object_is_in_editmode_vgroup(ob);
@@ -2496,7 +2497,7 @@ static int vertex_group_vert_select_poll_ex(bContext *C, const short ob_type_fla
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ob->id.lib && data && !data->lib))
+ if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data)))
return false;
if (ob_type_flag && (((1 << ob->type) & ob_type_flag)) == 0) {
@@ -2536,7 +2537,7 @@ static int vertex_group_vert_select_unlocked_poll(bContext *C)
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ob->id.lib && data && !data->lib))
+ if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data)))
return 0;
if (!(BKE_object_is_in_editmode_vgroup(ob) ||
@@ -2559,7 +2560,7 @@ static int vertex_group_vert_select_mesh_poll(bContext *C)
Object *ob = ED_object_context(C);
ID *data = (ob) ? ob->data : NULL;
- if (!(ob && !ob->id.lib && data && !data->lib))
+ if (!(ob && !ID_IS_LINKED_DATABLOCK(ob) && data && !ID_IS_LINKED_DATABLOCK(data)))
return 0;
/* only difference to #vertex_group_vert_select_poll */
@@ -2748,7 +2749,7 @@ static int vertex_group_select_exec(bContext *C, wmOperator *UNUSED(op))
{
Object *ob = ED_object_context(C);
- if (!ob || ob->id.lib)
+ if (!ob || ID_IS_LINKED_DATABLOCK(ob))
return OPERATOR_CANCELLED;
vgroup_select_verts(ob, 1);
@@ -3537,8 +3538,10 @@ static int vgroup_sort_name(const void *def_a_ptr, const void *def_b_ptr)
return BLI_natstrcmp(def_a->name, def_b->name);
}
-/* Sorts the weight groups according to the bone hierarchy of the
- associated armature (similar to how bones are ordered in the Outliner) */
+/**
+ * Sorts the weight groups according to the bone hierarchy of the
+ * associated armature (similar to how bones are ordered in the Outliner)
+ */
static void vgroup_sort_bone_hierarchy(Object *ob, ListBase *bonebase)
{
if (bonebase == NULL) {
diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c
index 30c102b70c5..e22a145b3a6 100644
--- a/source/blender/editors/physics/particle_edit.c
+++ b/source/blender/editors/physics/particle_edit.c
@@ -4693,7 +4693,7 @@ static int particle_edit_toggle_poll(bContext *C)
if (ob == NULL || ob->type != OB_MESH)
return 0;
- if (!ob->data || ((ID *)ob->data)->lib)
+ if (!ob->data || ID_IS_LINKED_DATABLOCK(ob->data))
return 0;
if (CTX_data_edit_object(C))
return 0;
diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c
index 1297133e1a2..0dd00df6182 100644
--- a/source/blender/editors/physics/particle_object.c
+++ b/source/blender/editors/physics/particle_object.c
@@ -195,7 +195,7 @@ static int new_particle_settings_exec(bContext *C, wmOperator *UNUSED(op))
/* add or copy particle setting */
if (psys->part)
- part= BKE_particlesettings_copy(psys->part);
+ part= BKE_particlesettings_copy(bmain, psys->part);
else
part= psys_new_settings("ParticleSettings", bmain);
@@ -974,7 +974,7 @@ static void remove_particle_systems_from_object(Object *ob_to)
if (ob_to->type != OB_MESH)
return;
- if (!ob_to->data || ((ID *)ob_to->data)->lib)
+ if (!ob_to->data || ID_IS_LINKED_DATABLOCK(ob_to->data))
return;
for (md = ob_to->modifiers.first; md; md = md_next) {
@@ -1004,7 +1004,7 @@ static bool copy_particle_systems_to_object(Scene *scene, Object *ob_from, Parti
if (ob_to->type != OB_MESH)
return false;
- if (!ob_to->data || ((ID *)ob_to->data)->lib)
+ if (!ob_to->data || ID_IS_LINKED_DATABLOCK(ob_to->data))
return false;
/* For remapping we need a valid DM.
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 1203889cf0e..8c5d25ad44d 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -1482,7 +1482,7 @@ static void render_view3d_do(RenderEngine *engine, const bContext *C)
void render_view3d_update(RenderEngine *engine, const bContext *C)
{
/* this shouldn't be needed and causes too many database rebuilds, but we
- * aren't actually tracking updates for all relevent datablocks so this is
+ * aren't actually tracking updates for all relevant datablocks so this is
* a catch-all for updates */
engine->update_flag |= RE_ENGINE_UPDATE_DATABASE;
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 06291487809..4477c491dd9 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -43,6 +43,7 @@
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
+#include "DNA_gpencil_types.h"
#include "BKE_camera.h"
#include "BKE_context.h"
@@ -414,6 +415,99 @@ static void screen_opengl_render_write(OGLRender *oglrender)
else printf("OpenGL Render failed to write '%s'\n", name);
}
+static void addAlphaOverFloat(float dest[4], const float source[4])
+{
+ /* d = s + (1-alpha_s)d*/
+ float mul;
+
+ mul = 1.0f - source[3];
+
+ dest[0] = (mul * dest[0]) + source[0];
+ dest[1] = (mul * dest[1]) + source[1];
+ dest[2] = (mul * dest[2]) + source[2];
+ dest[3] = (mul * dest[3]) + source[3];
+
+}
+
+/* add renderlayer and renderpass for each grease pencil layer for using in composition */
+static void add_gpencil_renderpass(OGLRender *oglrender, RenderResult *rr, RenderView *rv)
+{
+ bGPdata *gpd = oglrender->scene->gpd;
+ Scene *scene = oglrender->scene;
+
+ /* sanity checks */
+ if (gpd == NULL) {
+ return;
+ }
+ if (scene == NULL) {
+ return;
+ }
+ if (BLI_listbase_is_empty(&gpd->layers)) {
+ return;
+ }
+
+ /* save old alpha mode */
+ short oldalphamode = scene->r.alphamode;
+ /* set alpha transparent for gp */
+ scene->r.alphamode = R_ALPHAPREMUL;
+
+ /* saves layer status */
+ short *oldsts = MEM_mallocN(BLI_listbase_count(&gpd->layers) * sizeof(short), "temp_gplayers_flag");
+ int i = 0;
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ oldsts[i] = gpl->flag;
+ ++i;
+ }
+ /* loop all layers to create separate render */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* dont draw layer if hidden */
+ if (gpl->flag & GP_LAYER_HIDE)
+ continue;
+ /* hide all layer except current */
+ for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) {
+ if (gpl != gph) {
+ gph->flag |= GP_LAYER_HIDE;
+ }
+ }
+
+ /* render this gp layer */
+ screen_opengl_render_doit(oglrender, rr);
+
+ /* add RendePass composite */
+ RenderPass *rp = RE_create_gp_pass(rr, gpl->info, rv->name);
+
+ /* copy image data from rectf */
+ float *src = RE_RenderViewGetById(rr, oglrender->view_id)->rectf;
+ float *dest = rp->rect;
+
+ float *pixSrc, *pixDest;
+ int x, y, rectx, recty;
+ rectx = rr->rectx;
+ recty = rr->recty;
+ for (y = 0; y < recty; y++) {
+ for (x = 0; x < rectx; x++) {
+ pixSrc = src + 4 * (rectx * y + x);
+ if (pixSrc[3] > 0.0) {
+ pixDest = dest + 4 * (rectx * y + x);
+ addAlphaOverFloat(pixDest, pixSrc);
+ }
+ }
+ }
+
+ /* back layer status */
+ i = 0;
+ for (bGPDlayer *gph = gpd->layers.first; gph; gph = gph->next) {
+ gph->flag = oldsts[i];
+ ++i;
+ }
+ }
+ /* free memory */
+ MEM_freeN(oldsts);
+
+ /* back default alpha mode */
+ scene->r.alphamode = oldalphamode;
+}
+
static void screen_opengl_render_apply(OGLRender *oglrender)
{
RenderResult *rr;
@@ -449,6 +543,9 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
BLI_assert(view_id < oglrender->views_len);
RE_SetActiveRenderView(oglrender->re, rv->name);
oglrender->view_id = view_id;
+ /* add grease pencil passes */
+ add_gpencil_renderpass(oglrender, rr, rv);
+ /* render composite */
screen_opengl_render_doit(oglrender, rr);
}
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 132c3fa5438..b4f3426677a 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -864,7 +864,7 @@ static void shader_preview_free(void *customdata)
/* get rid of copied world */
BLI_remlink(&pr_main->world, sp->worldcopy);
/* T32865 - we need to unlink the texture copies, unlike for materials */
- BKE_libblock_relink_ex(sp->worldcopy, NULL, NULL, true);
+ BKE_libblock_relink_ex(pr_main, sp->worldcopy, NULL, NULL, true);
BKE_world_free(sp->worldcopy);
properties = IDP_GetProperties((ID *)sp->worldcopy, false);
@@ -881,7 +881,7 @@ static void shader_preview_free(void *customdata)
/* get rid of copied lamp */
BLI_remlink(&pr_main->lamp, sp->lampcopy);
- BKE_libblock_relink_ex(sp->lampcopy, NULL, NULL, true);
+ BKE_libblock_relink_ex(pr_main, sp->lampcopy, NULL, NULL, true);
BKE_lamp_free(sp->lampcopy);
properties = IDP_GetProperties((ID *)sp->lampcopy, false);
@@ -1210,7 +1210,7 @@ void ED_preview_icon_job(const bContext *C, void *owner, ID *id, unsigned int *r
/* setup job */
WM_jobs_customdata_set(wm_job, ip, icon_preview_free);
- WM_jobs_timer(wm_job, 0.1, NC_MATERIAL, NC_MATERIAL);
+ WM_jobs_timer(wm_job, 0.1, NC_WINDOW, NC_WINDOW);
WM_jobs_callbacks(wm_job, icon_preview_startjob_all_sizes, NULL, NULL, icon_preview_endjob);
WM_jobs_start(CTX_wm_manager(C), wm_job);
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index 20e3c40360f..49cb8a8730a 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -105,7 +105,7 @@ static int material_slot_add_exec(bContext *C, wmOperator *UNUSED(op))
if (!ob)
return OPERATOR_CANCELLED;
- object_add_material_slot(ob);
+ BKE_object_material_slot_add(ob);
if (ob->mode & OB_MODE_TEXTURE_PAINT) {
Scene *scene = CTX_data_scene(C);
@@ -148,7 +148,7 @@ static int material_slot_remove_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- object_remove_material_slot(ob);
+ BKE_object_material_slot_remove(ob);
if (ob->mode & OB_MODE_TEXTURE_PAINT) {
Scene *scene = CTX_data_scene(C);
@@ -467,7 +467,7 @@ static int new_material_exec(bContext *C, wmOperator *UNUSED(op))
/* add or copy material */
if (ma) {
- ma = BKE_material_copy(ma);
+ ma = BKE_material_copy(bmain, ma);
}
else {
ma = BKE_material_add(bmain, DATA_("Material"));
@@ -521,7 +521,7 @@ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op))
/* add or copy texture */
if (tex) {
- tex = BKE_texture_copy(tex);
+ tex = BKE_texture_copy(bmain, tex);
}
else {
tex = BKE_texture_add(bmain, DATA_("Texture"));
@@ -532,7 +532,7 @@ static int new_texture_exec(bContext *C, wmOperator *UNUSED(op))
if (prop) {
/* when creating new ID blocks, use is already 1, but RNA
- * pointer se also increases user, so this compensates it */
+ * pointer use also increases user, so this compensates it */
id_us_min(&tex->id);
if (ptr.id.data && GS(((ID *)ptr.id.data)->name) == ID_MA &&
@@ -579,7 +579,7 @@ static int new_world_exec(bContext *C, wmOperator *UNUSED(op))
/* add or copy world */
if (wo) {
- wo = BKE_world_copy(wo);
+ wo = BKE_world_copy(bmain, wo);
}
else {
wo = add_world(bmain, DATA_("World"));
@@ -595,7 +595,7 @@ static int new_world_exec(bContext *C, wmOperator *UNUSED(op))
if (prop) {
/* when creating new ID blocks, use is already 1, but RNA
- * pointer se also increases user, so this compensates it */
+ * pointer use also increases user, so this compensates it */
id_us_min(&wo->id);
RNA_id_pointer_create(&wo->id, &idptr);
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index ab8b7d4e138..8d058ed2081 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -580,15 +580,20 @@ void ED_region_tag_refresh_ui(ARegion *ar)
void ED_region_tag_redraw_partial(ARegion *ar, const rcti *rct)
{
if (ar && !(ar->do_draw & RGN_DRAWING)) {
- if (!(ar->do_draw & RGN_DRAW)) {
+ if (!(ar->do_draw & (RGN_DRAW | RGN_DRAW_PARTIAL))) {
/* no redraw set yet, set partial region */
ar->do_draw |= RGN_DRAW_PARTIAL;
ar->drawrct = *rct;
}
else if (ar->drawrct.xmin != ar->drawrct.xmax) {
+ BLI_assert((ar->do_draw & RGN_DRAW_PARTIAL) != 0);
/* partial redraw already set, expand region */
BLI_rcti_union(&ar->drawrct, rct);
}
+ else {
+ BLI_assert((ar->do_draw & RGN_DRAW) != 0);
+ /* Else, full redraw is already requested, nothing to do here. */
+ }
}
}
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index f61ad348501..22d95d77d55 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -85,7 +85,8 @@ const char *screen_context_dir[] = {
"sequences", "selected_sequences", "selected_editable_sequences", /* sequencer */
"gpencil_data", "gpencil_data_owner", /* grease pencil data */
"visible_gpencil_layers", "editable_gpencil_layers", "editable_gpencil_strokes",
- "active_gpencil_layer", "active_gpencil_frame",
+ "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_palette",
+ "active_gpencil_palettecolor", "active_gpencil_brush",
"active_operator",
NULL};
@@ -474,6 +475,44 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
}
}
}
+ else if (CTX_data_equals(member, "active_gpencil_palette")) {
+ /* XXX: see comment for gpencil_data case... */
+ bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
+
+ if (gpd) {
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+
+ if (palette) {
+ CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPalette, palette);
+ return 1;
+ }
+ }
+ }
+ else if (CTX_data_equals(member, "active_gpencil_palettecolor")) {
+ /* XXX: see comment for gpencil_data case... */
+ bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
+
+ if (gpd) {
+ bGPDpalette *palette = gpencil_palette_getactive(gpd);
+
+ if (palette) {
+ bGPDpalettecolor *palcolor = gpencil_palettecolor_getactive(palette);
+ if (palcolor) {
+ CTX_data_pointer_set(result, &gpd->id, &RNA_GPencilPaletteColor, palcolor);
+ return 1;
+ }
+ }
+ }
+ }
+ else if (CTX_data_equals(member, "active_gpencil_brush")) {
+ /* XXX: see comment for gpencil_data case... */
+ bGPDbrush *brush = gpencil_brush_getactive(scene->toolsettings);
+
+ if (brush) {
+ CTX_data_pointer_set(result, NULL, &RNA_GPencilBrush, brush);
+ return 1;
+ }
+ }
else if (CTX_data_equals(member, "active_gpencil_frame")) {
/* XXX: see comment for gpencil_data case... */
bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact);
@@ -533,6 +572,11 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult
for (gps = gpf->strokes.first; gps; gps = gps->next) {
if (ED_gpencil_stroke_can_use_direct(sa, gps)) {
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps);
}
}
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index 62aeca4b9d1..677a6472c72 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -1943,6 +1943,7 @@ ScrArea *ED_screen_state_toggle(bContext *C, wmWindow *win, ScrArea *sa, const s
sc = ED_screen_add(win, oldscreen->scene, newname);
sc->state = state;
sc->redraws_flag = oldscreen->redraws_flag;
+ sc->temp = oldscreen->temp;
/* timer */
sc->animtimer = oldscreen->animtimer;
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 0c0a6c93b3e..6dbb5db53d0 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -168,7 +168,7 @@ int ED_operator_screen_mainwinactive(bContext *C)
int ED_operator_scene_editable(bContext *C)
{
Scene *scene = CTX_data_scene(C);
- if (scene && scene->id.lib == NULL)
+ if (scene && !ID_IS_LINKED_DATABLOCK(scene))
return 1;
return 0;
}
@@ -178,7 +178,7 @@ int ED_operator_objectmode(bContext *C)
Scene *scene = CTX_data_scene(C);
Object *obact = CTX_data_active_object(C);
- if (scene == NULL || scene->id.lib)
+ if (scene == NULL || ID_IS_LINKED_DATABLOCK(scene))
return 0;
if (CTX_data_edit_object(C))
return 0;
@@ -279,7 +279,7 @@ int ED_operator_node_editable(bContext *C)
{
SpaceNode *snode = CTX_wm_space_node(C);
- if (snode && snode->edittree && snode->edittree->id.lib == NULL)
+ if (snode && snode->edittree && !ID_IS_LINKED_DATABLOCK(snode->edittree))
return 1;
return 0;
@@ -341,20 +341,20 @@ int ED_operator_object_active(bContext *C)
int ED_operator_object_active_editable(bContext *C)
{
Object *ob = ED_object_active_context(C);
- return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob));
+ return ((ob != NULL) && !ID_IS_LINKED_DATABLOCK(ob) && !ed_object_hidden(ob));
}
int ED_operator_object_active_editable_mesh(bContext *C)
{
Object *ob = ED_object_active_context(C);
- return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) &&
- (ob->type == OB_MESH) && !(((ID *)ob->data)->lib));
+ return ((ob != NULL) && !ID_IS_LINKED_DATABLOCK(ob) && !ed_object_hidden(ob) &&
+ (ob->type == OB_MESH) && !ID_IS_LINKED_DATABLOCK(ob->data));
}
int ED_operator_object_active_editable_font(bContext *C)
{
Object *ob = ED_object_active_context(C);
- return ((ob != NULL) && !(ob->id.lib) && !ed_object_hidden(ob) &&
+ return ((ob != NULL) && !ID_IS_LINKED_DATABLOCK(ob) && !ed_object_hidden(ob) &&
(ob->type == OB_FONT));
}
@@ -3955,7 +3955,7 @@ static int scene_new_exec(bContext *C, wmOperator *op)
newscene = BKE_scene_add(bmain, DATA_("Scene"));
}
else { /* different kinds of copying */
- newscene = BKE_scene_copy(scene, type);
+ newscene = BKE_scene_copy(bmain, scene, type);
/* these can't be handled in blenkernel currently, so do them here */
if (type == SCE_COPY_LINK_DATA) {
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index eba9448aa40..53c11e2a6a9 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1016,7 +1016,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
translation[1] = y;
outline_alpha = 0.5;
outline_col = brush->add_col;
- final_radius = (BKE_brush_size_get(scene, brush) * zoomx) / U.pixelsize;
+ final_radius = (BKE_brush_size_get(scene, brush) * zoomx);
/* don't calculate rake angles while a stroke is active because the rake variables are global and
* we may get interference with the stroke itself. For line strokes, such interference is visible */
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 941f19ca0f0..0924c9a9288 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -1342,7 +1342,7 @@ static int texture_paint_toggle_poll(bContext *C)
Object *ob = CTX_data_active_object(C);
if (ob == NULL || ob->type != OB_MESH)
return 0;
- if (!ob->data || ((ID *)ob->data)->lib)
+ if (!ob->data || ID_IS_LINKED_DATABLOCK(ob->data))
return 0;
if (CTX_data_edit_object(C))
return 0;
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 080bd5b73c7..9474a46d716 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -1126,8 +1126,8 @@ static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsign
/* blend into canvas */
for (a = 0; a < tot; a++) {
ED_imapaint_dirty_region(s->image, s->canvas,
- region[a].destx, region[a].desty,
- region[a].width, region[a].height, true);
+ region[a].destx, region[a].desty,
+ region[a].width, region[a].height, true);
if (s->do_masking) {
/* masking, find original pixels tiles from undo buffer to composite over */
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 6ed969cb270..d60c8e8dbd9 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -183,8 +183,8 @@ BLI_INLINE unsigned char f_to_char(const float val)
/* to avoid locking in tile initialization */
#define TILE_PENDING SET_INT_IN_POINTER(-1)
-/* This is mainly a convenience struct used so we can keep an array of images we use
- * Thir imbufs, etc, in 1 array, When using threads this array is copied for each thread
+/* This is mainly a convenience struct used so we can keep an array of images we use -
+ * their imbufs, etc, in 1 array, When using threads this array is copied for each thread
* because 'partRedrawRect' and 'touch' values would not be thread safe */
typedef struct ProjPaintImage {
Image *ima;
@@ -202,7 +202,7 @@ typedef struct ProjPaintImage {
*/
typedef struct ProjStrokeHandle {
/* Support for painting from multiple views at once,
- * currently used to impliment symmetry painting,
+ * currently used to implement symmetry painting,
* we can assume at least the first is set while painting. */
struct ProjPaintState *ps_views[8];
int ps_views_tot;
@@ -2174,7 +2174,7 @@ static void project_bucket_clip_face(
if ((*tot) < 3) {
/* no intersections to speak of, but more probable is that all face is just outside the
- * rectangle and culled due to float precision issues. Since above teste have failed,
+ * rectangle and culled due to float precision issues. Since above tests have failed,
* just dump triangle as is for painting */
*tot = 0;
copy_v2_v2(bucket_bounds_uv[*tot], uv1co); (*tot)++;
diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c
index bf923415f01..f88b64129e7 100644
--- a/source/blender/editors/sculpt_paint/paint_ops.c
+++ b/source/blender/editors/sculpt_paint/paint_ops.c
@@ -69,7 +69,7 @@ static int brush_add_exec(bContext *C, wmOperator *UNUSED(op))
PaintMode mode = BKE_paintmode_get_active_from_context(C);
if (br)
- br = BKE_brush_copy(br);
+ br = BKE_brush_copy(bmain, br);
else
br = BKE_brush_add(bmain, "Brush", BKE_paint_object_mode_from_paint_mode(mode));
diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c
index aa17cb02fe5..1431958501d 100644
--- a/source/blender/editors/sculpt_paint/paint_stroke.c
+++ b/source/blender/editors/sculpt_paint/paint_stroke.c
@@ -491,31 +491,30 @@ static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, const float
}
/* Returns zero if no sculpt changes should be made, non-zero otherwise */
-static int paint_smooth_stroke(PaintStroke *stroke, float output[2], float *outpressure,
- const PaintSample *sample, PaintMode mode)
+static bool paint_smooth_stroke(
+ PaintStroke *stroke, const PaintSample *sample, PaintMode mode,
+ float r_mouse[2], float *r_pressure)
{
if (paint_supports_smooth_stroke(stroke->brush, mode)) {
float radius = stroke->brush->smooth_stroke_radius * stroke->zoom_2d;
- float u = stroke->brush->smooth_stroke_factor, v = 1.0f - u;
- float dx = stroke->last_mouse_position[0] - sample->mouse[0];
- float dy = stroke->last_mouse_position[1] - sample->mouse[1];
+ float u = stroke->brush->smooth_stroke_factor;
/* If the mouse is moving within the radius of the last move,
* don't update the mouse position. This allows sharp turns. */
- if (dx * dx + dy * dy < radius * radius)
- return 0;
+ if (len_squared_v2v2(stroke->last_mouse_position, sample->mouse) < SQUARE(radius)) {
+ return false;
+ }
- output[0] = sample->mouse[0] * v + stroke->last_mouse_position[0] * u;
- output[1] = sample->mouse[1] * v + stroke->last_mouse_position[1] * u;
- *outpressure = sample->pressure * v + stroke->last_pressure * u;
+ interp_v2_v2v2(r_mouse, sample->mouse, stroke->last_mouse_position, u);
+ *r_pressure = interpf(sample->pressure, stroke->last_pressure, u);
}
else {
- output[0] = sample->mouse[0];
- output[1] = sample->mouse[1];
- *outpressure = sample->pressure;
+ r_mouse[0] = sample->mouse[0];
+ r_mouse[1] = sample->mouse[1];
+ *r_pressure = sample->pressure;
}
- return 1;
+ return true;
}
static float paint_space_stroke_spacing(const Scene *scene, PaintStroke *stroke, float size_pressure, float spacing_pressure)
@@ -1190,7 +1189,7 @@ int paint_stroke_modal(bContext *C, wmOperator *op, const wmEvent *event)
/* airbrush */
((br->flag & BRUSH_AIRBRUSH) && event->type == TIMER && event->customdata == stroke->timer))
{
- if (paint_smooth_stroke(stroke, mouse, &pressure, &sample_average, mode)) {
+ if (paint_smooth_stroke(stroke, &sample_average, mode, mouse, &pressure)) {
if (stroke->stroke_started) {
if (paint_space_stroke_enabled(br, mode)) {
if (paint_space_stroke(C, op, mouse, pressure))
diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c
index 87855879ec5..991025a4d5d 100644
--- a/source/blender/editors/sculpt_paint/paint_vertex.c
+++ b/source/blender/editors/sculpt_paint/paint_vertex.c
@@ -247,7 +247,7 @@ static bool make_vertexcol(Object *ob) /* single ob */
{
Mesh *me;
- if ((ob->id.lib) ||
+ if (ID_IS_LINKED_DATABLOCK(ob) ||
((me = BKE_mesh_from_object(ob)) == NULL) ||
(me->totpoly == 0) ||
(me->edit_btmesh))
@@ -257,9 +257,7 @@ static bool make_vertexcol(Object *ob) /* single ob */
/* copies from shadedisplist to mcol */
if (!me->mloopcol && me->totloop) {
- if (!me->mloopcol) {
- CustomData_add_layer(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop);
- }
+ CustomData_add_layer(&me->ldata, CD_MLOOPCOL, CD_DEFAULT, NULL, me->totloop);
BKE_mesh_update_customdata_pointers(me, true);
}
@@ -1841,7 +1839,7 @@ static int paint_poll_test(bContext *C)
Object *ob = CTX_data_active_object(C);
if (ob == NULL || ob->type != OB_MESH)
return 0;
- if (!ob->data || ((ID *)ob->data)->lib)
+ if (!ob->data || ID_IS_LINKED_DATABLOCK(ob->data))
return 0;
if (CTX_data_edit_object(C))
return 0;
@@ -2380,9 +2378,8 @@ static void wpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
swap_m4m4(vc->rv3d->persmat, mat);
/* calculate pivot for rotation around seletion if needed */
- if (U.uiflag & USER_ORBIT_SELECTION) {
- paint_last_stroke_update(scene, vc->ar, mval);
- }
+ /* also needed for "View Selected" on last stroke */
+ paint_last_stroke_update(scene, vc->ar, mval);
DAG_id_tag_update(ob->data, 0);
ED_region_tag_redraw(vc->ar);
@@ -2858,9 +2855,8 @@ static void vpaint_stroke_update_step(bContext *C, struct PaintStroke *stroke, P
}
/* calculate pivot for rotation around seletion if needed */
- if (U.uiflag & USER_ORBIT_SELECTION) {
- paint_last_stroke_update(scene, vc->ar, mval);
- }
+ /* also needed for "View Selected" on last stroke */
+ paint_last_stroke_update(scene, vc->ar, mval);
ED_region_tag_redraw(vc->ar);
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 0931456058d..fe0fb3f5035 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1145,7 +1145,9 @@ static void calc_area_normal_and_center(
/* Return modified brush strength. Includes the direction of the brush, positive
* values pull vertices, negative values push. Uses tablet pressure and a
* special multiplier found experimentally to scale the strength factor. */
-static float brush_strength(const Sculpt *sd, const StrokeCache *cache, const float feather, const UnifiedPaintSettings *ups)
+static float brush_strength(
+ const Sculpt *sd, const StrokeCache *cache,
+ const float feather, const UnifiedPaintSettings *ups)
{
const Scene *scene = cache->vc->scene;
const Brush *brush = BKE_paint_brush((Paint *)&sd->paint);
@@ -1237,7 +1239,7 @@ static float brush_strength(const Sculpt *sd, const StrokeCache *cache, const fl
/* Return a multiplier for brush strength on a particular vertex. */
static float tex_strength(SculptSession *ss, Brush *br,
- const float point[3],
+ const float brush_point[3],
const float len,
const short vno[3],
const float fno[3],
@@ -1249,6 +1251,9 @@ static float tex_strength(SculptSession *ss, Brush *br,
MTex *mtex = &br->mtex;
float avg = 1;
float rgba[4];
+ float point[3];
+
+ sub_v3_v3v3(point, brush_point, cache->plane_offset);
if (!mtex->tex) {
avg = 1;
@@ -3985,7 +3990,9 @@ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss)
}
/* Initialize the stroke cache invariants from operator properties */
-static void sculpt_update_cache_invariants(bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2])
+static void sculpt_update_cache_invariants(
+ bContext *C, Sculpt *sd, SculptSession *ss,
+ wmOperator *op, const float mouse[2])
{
StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache");
Scene *scene = CTX_data_scene(C);
@@ -4448,7 +4455,9 @@ static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin)
}
}
-static float sculpt_raycast_init(ViewContext *vc, const float mouse[2], float ray_start[3], float ray_end[3], float ray_normal[3], bool original)
+static float sculpt_raycast_init(
+ ViewContext *vc, const float mouse[2],
+ float ray_start[3], float ray_end[3], float ray_normal[3], bool original)
{
float obimat[4][4];
float dist;
@@ -4956,8 +4965,9 @@ void sculpt_dyntopo_node_layers_add(SculptSession *ss)
cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id);
}
- ss->cd_vert_node_offset = CustomData_get_n_offset(&ss->bm->vdata, CD_PROP_INT,
- cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT));
+ ss->cd_vert_node_offset = CustomData_get_n_offset(
+ &ss->bm->vdata, CD_PROP_INT,
+ cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT));
ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
@@ -4967,8 +4977,9 @@ void sculpt_dyntopo_node_layers_add(SculptSession *ss)
cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id);
}
- ss->cd_face_node_offset = CustomData_get_n_offset(&ss->bm->pdata, CD_PROP_INT,
- cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT));
+ ss->cd_face_node_offset = CustomData_get_n_offset(
+ &ss->bm->pdata, CD_PROP_INT,
+ cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT));
ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY;
}
@@ -5462,7 +5473,10 @@ static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op)
}
if (message_unsupported == NULL) {
+ /* undo push is needed to prevent memory leak */
+ sculpt_undo_push_begin("Dynamic topology enable");
sculpt_dynamic_topology_enable(C);
+ sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN);
}
else {
BKE_reportf(op->reports, RPT_WARNING,
@@ -5664,7 +5678,8 @@ static void SCULPT_OT_sample_detail_size(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_int_array(ot->srna, "location", 2, NULL, 0, SHRT_MAX, "Location", "Screen Coordinates of sampling", 0, SHRT_MAX);
+ RNA_def_int_array(ot->srna, "location", 2, NULL, 0, SHRT_MAX,
+ "Location", "Screen Coordinates of sampling", 0, SHRT_MAX);
}
diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c
index 4931426d62e..03f2e146b7d 100644
--- a/source/blender/editors/sound/sound_ops.c
+++ b/source/blender/editors/sound/sound_ops.c
@@ -136,7 +136,7 @@ static int sound_open_exec(bContext *C, wmOperator *op)
if (pprop->prop) {
/* when creating new ID blocks, use is already 1, but RNA
- * pointer se also increases user, so this compensates it */
+ * pointer use also increases user, so this compensates it */
id_us_min(&sound->id);
RNA_id_pointer_create(&sound->id, &idptr);
diff --git a/source/blender/editors/space_action/action_buttons.c b/source/blender/editors/space_action/action_buttons.c
index 063a477d2b6..bfc808f6d83 100644
--- a/source/blender/editors/space_action/action_buttons.c
+++ b/source/blender/editors/space_action/action_buttons.c
@@ -122,7 +122,7 @@ void ACTION_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "ACTION_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = action_properties_toggle_exec;
ot->poll = ED_operator_action_active;
diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c
index a3be7791f9a..6755a6316cc 100644
--- a/source/blender/editors/space_action/action_data.c
+++ b/source/blender/editors/space_action/action_data.c
@@ -121,7 +121,7 @@ static bAction *action_create_new(bContext *C, bAction *oldact)
*/
if (oldact && GS(oldact->id.name) == ID_AC) {
/* make a copy of the existing action */
- action = BKE_action_copy(oldact);
+ action = BKE_action_copy(CTX_data_main(C), oldact);
}
else {
/* just make a new (empty) action */
diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c
index 10748c2fe15..9fc96e06299 100644
--- a/source/blender/editors/space_action/action_draw.c
+++ b/source/blender/editors/space_action/action_draw.c
@@ -81,7 +81,7 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- height = ((items * ACHANNEL_STEP) + (ACHANNEL_HEIGHT));
+ height = ((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac)));
if (height > BLI_rcti_size_y(&v2d->mask)) {
/* don't use totrect set, as the width stays the same
* (NOTE: this is ok here, the configuration is pretty straightforward)
@@ -95,11 +95,11 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
{ /* first pass: just the standard GL-drawing for backdrop + text */
size_t channel_index = 0;
- y = (float)ACHANNEL_FIRST;
+ y = (float)ACHANNEL_FIRST(ac);
for (ale = anim_data.first; ale; ale = ale->next) {
- float yminc = (float)(y - ACHANNEL_HEIGHT_HALF);
- float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF);
+ float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
+ float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
/* check if visible */
if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
@@ -110,7 +110,7 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
}
/* adjust y-position for next one */
- y -= ACHANNEL_STEP;
+ y -= ACHANNEL_STEP(ac);
channel_index++;
}
}
@@ -118,11 +118,11 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
size_t channel_index = 0;
- y = (float)ACHANNEL_FIRST;
+ y = (float)ACHANNEL_FIRST(ac);
for (ale = anim_data.first; ale; ale = ale->next) {
- float yminc = (float)(y - ACHANNEL_HEIGHT_HALF);
- float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF);
+ float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
+ float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
/* check if visible */
if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
@@ -133,7 +133,7 @@ void draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
}
/* adjust y-position for next one */
- y -= ACHANNEL_STEP;
+ y -= ACHANNEL_STEP(ac);
channel_index++;
}
@@ -195,19 +195,19 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
items = ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
- height = ((items * ACHANNEL_STEP) + (ACHANNEL_HEIGHT));
+ height = ((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac)));
/* don't use totrect set, as the width stays the same
* (NOTE: this is ok here, the configuration is pretty straightforward)
*/
v2d->tot.ymin = (float)(-height);
/* first backdrop strips */
- y = (float)(-ACHANNEL_HEIGHT);
+ y = (float)(-ACHANNEL_HEIGHT(ac));
glEnable(GL_BLEND);
for (ale = anim_data.first; ale; ale = ale->next) {
- const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF);
- const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF);
+ const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
+ const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
/* check if visible */
if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
@@ -260,39 +260,39 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
}
/* draw region twice: firstly backdrop, then the current range */
- glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF);
+ glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac));
if (ac->datatype == ANIMCONT_ACTION)
- glRectf(act_start, (float)y - ACHANNEL_HEIGHT_HALF, act_end, (float)y + ACHANNEL_HEIGHT_HALF);
+ glRectf(act_start, (float)y - ACHANNEL_HEIGHT_HALF(ac), act_end, (float)y + ACHANNEL_HEIGHT_HALF(ac));
}
else if (ac->datatype == ANIMCONT_GPENCIL) {
/* frames less than one get less saturated background */
if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22);
else glColor4ub(col2[0], col2[1], col2[2], 0x22);
- glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF);
+ glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF(ac));
/* frames one and higher get a saturated background */
if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44);
else glColor4ub(col2[0], col2[1], col2[2], 0x44);
- glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF);
+ glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac));
}
else if (ac->datatype == ANIMCONT_MASK) {
/* TODO --- this is a copy of gpencil */
/* frames less than one get less saturated background */
if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x22);
else glColor4ub(col2[0], col2[1], col2[2], 0x22);
- glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF);
+ glRectf(0.0f, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmin, (float)y + ACHANNEL_HEIGHT_HALF(ac));
/* frames one and higher get a saturated background */
if (sel) glColor4ub(col1[0], col1[1], col1[2], 0x44);
else glColor4ub(col2[0], col2[1], col2[2], 0x44);
- glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF, v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF);
+ glRectf(v2d->cur.xmin, (float)y - ACHANNEL_HEIGHT_HALF(ac), v2d->cur.xmax + EXTRA_SCROLL_PAD, (float)y + ACHANNEL_HEIGHT_HALF(ac));
}
}
}
/* Increment the step */
- y -= ACHANNEL_STEP;
+ y -= ACHANNEL_STEP(ac);
}
glDisable(GL_BLEND);
@@ -301,11 +301,11 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
* This is to try to optimize this for heavier data sets
* 2) Keyframes which are out of view horizontally are disregarded
*/
- y = (float)(-ACHANNEL_HEIGHT);
+ y = (float)(-ACHANNEL_HEIGHT(ac));
for (ale = anim_data.first; ale; ale = ale->next) {
- const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF);
- const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF);
+ const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
+ const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
/* check if visible */
if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
@@ -318,34 +318,34 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *ar)
/* draw 'keyframes' for each specific datatype */
switch (ale->datatype) {
case ALE_ALL:
- draw_summary_channel(v2d, ale->data, y);
+ draw_summary_channel(v2d, ale->data, y, ac->yscale_fac);
break;
case ALE_SCE:
- draw_scene_channel(v2d, ads, ale->key_data, y);
+ draw_scene_channel(v2d, ads, ale->key_data, y, ac->yscale_fac);
break;
case ALE_OB:
- draw_object_channel(v2d, ads, ale->key_data, y);
+ draw_object_channel(v2d, ads, ale->key_data, y, ac->yscale_fac);
break;
case ALE_ACT:
- draw_action_channel(v2d, adt, ale->key_data, y);
+ draw_action_channel(v2d, adt, ale->key_data, y, ac->yscale_fac);
break;
case ALE_GROUP:
- draw_agroup_channel(v2d, adt, ale->data, y);
+ draw_agroup_channel(v2d, adt, ale->data, y, ac->yscale_fac);
break;
case ALE_FCURVE:
- draw_fcurve_channel(v2d, adt, ale->key_data, y);
+ draw_fcurve_channel(v2d, adt, ale->key_data, y, ac->yscale_fac);
break;
case ALE_GPFRAME:
- draw_gpl_channel(v2d, ads, ale->data, y);
+ draw_gpl_channel(v2d, ads, ale->data, y, ac->yscale_fac);
break;
case ALE_MASKLAY:
- draw_masklay_channel(v2d, ads, ale->data, y);
+ draw_masklay_channel(v2d, ads, ale->data, y, ac->yscale_fac);
break;
}
}
}
- y -= ACHANNEL_STEP;
+ y -= ACHANNEL_STEP(ac);
}
/* free tempolary channels used for drawing */
diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c
index 8261a211ed0..55b087c40e7 100644
--- a/source/blender/editors/space_action/action_edit.c
+++ b/source/blender/editors/space_action/action_edit.c
@@ -333,7 +333,7 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min,
ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype);
/* loop through all channels, finding the first one that's selected */
- y = (float)ACHANNEL_FIRST;
+ y = (float)ACHANNEL_FIRST(ac);
for (ale = anim_data.first; ale; ale = ale->next) {
const bAnimChannelType *acf = ANIM_channel_get_typeinfo(ale);
@@ -343,8 +343,8 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min,
ANIM_channel_setting_get(ac, ale, ACHANNEL_SETTING_SELECT))
{
/* update best estimate */
- *min = (float)(y - ACHANNEL_HEIGHT_HALF);
- *max = (float)(y + ACHANNEL_HEIGHT_HALF);
+ *min = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
+ *max = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
/* is this high enough priority yet? */
found = acf->channel_role;
@@ -358,7 +358,7 @@ static bool actkeys_channels_get_selected_extents(bAnimContext *ac, float *min,
}
/* adjust y-position for next one */
- y -= ACHANNEL_STEP;
+ y -= ACHANNEL_STEP(ac);
}
/* free all temp data */
diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c
index 7900217e28e..553be0ad290 100644
--- a/source/blender/editors/space_action/action_select.c
+++ b/source/blender/editors/space_action/action_select.c
@@ -213,7 +213,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s
KeyframeEditFunc ok_cb, select_cb;
View2D *v2d = &ac->ar->v2d;
rctf rectf;
- float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF);
+ float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac));
/* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
UI_view2d_region_to_view(v2d, rect.xmin, rect.ymin + 2, &rectf.xmin, &rectf.ymin);
@@ -239,7 +239,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
/* get new vertical minimum extent of channel */
- ymin = ymax - ACHANNEL_STEP;
+ ymin = ymax - ACHANNEL_STEP(ac);
/* set horizontal range (if applicable) */
if (ELEM(mode, ACTKEYS_BORDERSEL_FRAMERANGE, ACTKEYS_BORDERSEL_ALLKEYS)) {
@@ -392,7 +392,7 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view,
KeyframeEditFunc ok_cb, select_cb;
View2D *v2d = &ac->ar->v2d;
rctf rectf, scaled_rectf;
- float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF);
+ float ymin = 0, ymax = (float)(-ACHANNEL_HEIGHT_HALF(ac));
/* convert mouse coordinates to frame ranges and channel coordinates corrected for view pan/zoom */
UI_view2d_region_to_view_rctf(v2d, rectf_view, &rectf);
@@ -426,10 +426,10 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view,
AnimData *adt = ANIM_nla_mapping_get(ac, ale);
/* get new vertical minimum extent of channel */
- ymin = ymax - ACHANNEL_STEP;
+ ymin = ymax - ACHANNEL_STEP(ac);
/* compute midpoint of channel (used for testing if the key is in the region or not) */
- ked.channel_y = ymin + ACHANNEL_HEIGHT_HALF;
+ ked.channel_y = ymin + ACHANNEL_HEIGHT_HALF(ac);
/* if channel is mapped in NLA, apply correction
* - Apply to the bounds being checked, not all the keyframe points,
@@ -1351,6 +1351,7 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_
bool found = false;
float frame = 0.0f; /* frame of keyframe under mouse - NLA corrections not applied/included */
float selx = 0.0f; /* frame of keyframe under mouse */
+ float key_hsize;
float x, y;
rctf rectf;
@@ -1360,11 +1361,16 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_
/* use View2D to determine the index of the channel (i.e a row in the list) where keyframe was */
UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y);
- UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP, 0, (float)ACHANNEL_HEIGHT_HALF, x, y, NULL, &channel_index);
+ UI_view2d_listview_view_to_cell(v2d, 0, ACHANNEL_STEP(ac), 0, (float)ACHANNEL_HEIGHT_HALF(ac), x, y, NULL, &channel_index);
- /* x-range to check is +/- 7 (in screen/region-space) on either side of mouse click (size of keyframe icon) */
- UI_view2d_region_to_view(v2d, mval[0] - 7, mval[1], &rectf.xmin, &rectf.ymin);
- UI_view2d_region_to_view(v2d, mval[0] + 7, mval[1], &rectf.xmax, &rectf.ymax);
+ /* x-range to check is +/- 7px for standard keyframe under standard dpi/y-scale (in screen/region-space),
+ * on either side of mouse click (size of keyframe icon)
+ */
+ key_hsize = ACHANNEL_HEIGHT(ac) * 0.8f; /* standard channel height (to allow for some slop) */
+ key_hsize = roundf(key_hsize / 2.0f); /* half-size (for either side), but rounded up to nearest int (for easier targetting) */
+
+ UI_view2d_region_to_view(v2d, mval[0] - (int)key_hsize, mval[1], &rectf.xmin, &rectf.ymin);
+ UI_view2d_region_to_view(v2d, mval[0] + (int)key_hsize, mval[1], &rectf.xmax, &rectf.ymax);
/* filter data */
filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
diff --git a/source/blender/editors/space_clip/clip_editor.c b/source/blender/editors/space_clip/clip_editor.c
index 1ff656243d6..14d0f909d23 100644
--- a/source/blender/editors/space_clip/clip_editor.c
+++ b/source/blender/editors/space_clip/clip_editor.c
@@ -634,7 +634,7 @@ typedef struct PrefetchQueue {
short render_size, render_flag;
/* If true prefecthing goes forward in time,
- * othwewise it goes backwards in time (starting from current frame).
+ * otherwise it goes backwards in time (starting from current frame).
*/
bool forward;
diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c
index 085fdd57309..83876ae2669 100644
--- a/source/blender/editors/space_clip/clip_ops.c
+++ b/source/blender/editors/space_clip/clip_ops.c
@@ -234,7 +234,7 @@ static int open_exec(bContext *C, wmOperator *op)
if (pprop->prop) {
/* when creating new ID blocks, use is already 1, but RNA
- * pointer se also increases user, so this compensates it */
+ * pointer use also increases user, so this compensates it */
id_us_min(&clip->id);
RNA_id_pointer_create(&clip->id, &idptr);
diff --git a/source/blender/editors/space_clip/clip_toolbar.c b/source/blender/editors/space_clip/clip_toolbar.c
index d31d7f53b30..b042bbe8fea 100644
--- a/source/blender/editors/space_clip/clip_toolbar.c
+++ b/source/blender/editors/space_clip/clip_toolbar.c
@@ -106,7 +106,7 @@ void CLIP_OT_properties(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Properties";
- ot->description = "Toggle clip properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "CLIP_OT_properties";
/* api callbacks */
diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c
index 415839ab761..396d71f0a20 100644
--- a/source/blender/editors/space_clip/space_clip.c
+++ b/source/blender/editors/space_clip/space_clip.c
@@ -818,7 +818,7 @@ static void clip_keymap(struct wmKeyConfig *keyconf)
keymap = WM_keymap_find(keyconf, "Clip Dopesheet Editor", SPACE_CLIP, 0);
- kmi = WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_select_channel", ACTIONMOUSE, KM_PRESS, 0, 0);
+ kmi = WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_select_channel", LEFTMOUSE, KM_PRESS, 0, 0);
RNA_boolean_set(kmi->ptr, "extend", true); /* toggle */
WM_keymap_add_item(keymap, "CLIP_OT_dopesheet_view_all", HOMEKEY, KM_PRESS, 0, 0);
diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c
index 175d83506c6..ab33d452d3c 100644
--- a/source/blender/editors/space_file/filesel.c
+++ b/source/blender/editors/space_file/filesel.c
@@ -618,7 +618,7 @@ int file_select_match(struct SpaceFile *sfile, const char *pattern, char *matche
*/
for (i = 0; i < n; i++) {
file = filelist_file(sfile->files, i);
- /* Do not check wether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */
+ /* Do not check whether file is a file or dir here! Causes T44243 (we do accept dirs at this stage). */
if (fnmatch(pattern, file->relpath, 0) == 0) {
filelist_entry_select_set(sfile->files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL);
if (!match) {
diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c
index a9ab1502e16..516814b63b4 100644
--- a/source/blender/editors/space_graph/graph_buttons.c
+++ b/source/blender/editors/space_graph/graph_buttons.c
@@ -812,6 +812,11 @@ static void graph_panel_drivers(const bContext *C, Panel *pa)
}
col = uiLayoutColumn(pa->layout, true);
+
+ if (driver->type == DRIVER_TYPE_PYTHON) {
+ uiItemR(col, &driver_ptr, "use_self", 0, NULL, ICON_NONE);
+ }
+
/* debug setting */
uiItemR(col, &driver_ptr, "show_debug_info", 0, NULL, ICON_NONE);
@@ -1062,7 +1067,7 @@ void GRAPH_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "GRAPH_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = graph_properties_toggle_exec;
ot->poll = ED_operator_graphedit_active;
diff --git a/source/blender/editors/space_graph/graph_draw.c b/source/blender/editors/space_graph/graph_draw.c
index 3ac90200aea..ae91a466495 100644
--- a/source/blender/editors/space_graph/graph_draw.c
+++ b/source/blender/editors/space_graph/graph_draw.c
@@ -826,7 +826,7 @@ static void graph_draw_driver_debug(bAnimContext *ac, ID *id, FCurve *fcu)
//if ((driver->flag & DRIVER_FLAG_SHOWDEBUG) == 0)
// return;
- /* No curve to modify/visualise the result?
+ /* No curve to modify/visualize the result?
* => We still want to show the 1-1 default...
*/
if ((fcu->totvert == 0) && BLI_listbase_is_empty(&fcu->modifiers)) {
@@ -1130,18 +1130,18 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
* - offset of ACHANNEL_HEIGHT*2 is added to the height of the channels, as first is for
* start of list offset, and the second is as a correction for the scrollers.
*/
- height = (float)((items * ACHANNEL_STEP) + (ACHANNEL_HEIGHT * 2));
+ height = (float)((items * ACHANNEL_STEP(ac)) + (ACHANNEL_HEIGHT(ac) * 2));
UI_view2d_totRect_set(v2d, ar->winx, height);
/* loop through channels, and set up drawing depending on their type */
{ /* first pass: just the standard GL-drawing for backdrop + text */
size_t channel_index = 0;
- y = (float)ACHANNEL_FIRST;
+ y = (float)ACHANNEL_FIRST(ac);
for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
- const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF);
- const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF);
+ const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
+ const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
/* check if visible */
if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
@@ -1152,7 +1152,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
}
/* adjust y-position for next one */
- y -= ACHANNEL_STEP;
+ y -= ACHANNEL_STEP(ac);
channel_index++;
}
}
@@ -1160,15 +1160,15 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
uiBlock *block = UI_block_begin(C, ar, __func__, UI_EMBOSS);
size_t channel_index = 0;
- y = (float)ACHANNEL_FIRST;
+ y = (float)ACHANNEL_FIRST(ac);
/* set blending again, as may not be set in previous step */
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
- const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF);
- const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF);
+ const float yminc = (float)(y - ACHANNEL_HEIGHT_HALF(ac));
+ const float ymaxc = (float)(y + ACHANNEL_HEIGHT_HALF(ac));
/* check if visible */
if (IN_RANGE(yminc, v2d->cur.ymin, v2d->cur.ymax) ||
@@ -1179,7 +1179,7 @@ void graph_draw_channel_names(bContext *C, bAnimContext *ac, ARegion *ar)
}
/* adjust y-position for next one */
- y -= ACHANNEL_STEP;
+ y -= ACHANNEL_STEP(ac);
channel_index++;
}
diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c
index 8ae5932f3fd..2582ba4be8d 100644
--- a/source/blender/editors/space_graph/space_graph.c
+++ b/source/blender/editors/space_graph/space_graph.c
@@ -525,12 +525,130 @@ static void graph_listener(bScreen *UNUSED(sc), ScrArea *sa, wmNotifier *wmn)
}
}
-
+/* Update F-Curve colors */
+static void graph_refresh_fcurve_colors(const bContext *C)
+{
+ bAnimContext ac;
+
+ ListBase anim_data = {NULL, NULL};
+ bAnimListElem *ale;
+ size_t items;
+ int filter;
+ int i;
+
+ if (ANIM_animdata_get_context(C, &ac) == false)
+ return;
+
+ UI_SetTheme(SPACE_IPO, RGN_TYPE_WINDOW);
+
+ /* build list of F-Curves which will be visible as channels in channel-region
+ * - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a
+ * mismatch between channel-colors and the drawn curves
+ */
+ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
+ items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
+
+ /* loop over F-Curves, assigning colors */
+ for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
+ FCurve *fcu = (FCurve *)ale->data;
+
+ /* set color of curve here */
+ switch (fcu->color_mode) {
+ case FCURVE_COLOR_CUSTOM:
+ {
+ /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors),
+ * which should be left alone... Nothing needs to be done here.
+ */
+ break;
+ }
+ case FCURVE_COLOR_AUTO_RGB:
+ {
+ /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors.
+ * TODO: find a way to module the hue so that not all curves have same color...
+ */
+ float *col = fcu->color;
+
+ switch (fcu->array_index) {
+ case 0:
+ UI_GetThemeColor3fv(TH_AXIS_X, col);
+ break;
+ case 1:
+ UI_GetThemeColor3fv(TH_AXIS_Y, col);
+ break;
+ case 2:
+ UI_GetThemeColor3fv(TH_AXIS_Z, col);
+ break;
+ default:
+ /* 'unknown' color - bluish so as to not conflict with handles */
+ col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f;
+ break;
+ }
+ break;
+ }
+ case FCURVE_COLOR_AUTO_YRGB:
+ {
+ /* Like FCURVE_COLOR_AUTO_RGB, except this is for quaternions... */
+ float *col = fcu->color;
+
+ switch (fcu->array_index) {
+ case 1:
+ UI_GetThemeColor3fv(TH_AXIS_X, col);
+ break;
+ case 2:
+ UI_GetThemeColor3fv(TH_AXIS_Y, col);
+ break;
+ case 3:
+ UI_GetThemeColor3fv(TH_AXIS_Z, col);
+ break;
+
+ case 0:
+ {
+ /* Special Case: "W" channel should be yellowish, so blend X and Y channel colors... */
+ float c1[3], c2[3];
+ float h1[3], h2[3];
+ float hresult[3];
+
+ /* - get colors (rgb) */
+ UI_GetThemeColor3fv(TH_AXIS_X, c1);
+ UI_GetThemeColor3fv(TH_AXIS_Y, c2);
+
+ /* - perform blending in HSV space (to keep brightness similar) */
+ rgb_to_hsv_v(c1, h1);
+ rgb_to_hsv_v(c2, h2);
+
+ interp_v3_v3v3(hresult, h1, h2, 0.5f);
+
+ /* - convert back to RGB for display */
+ hsv_to_rgb_v(hresult, col);
+ break;
+ }
+
+ default:
+ /* 'unknown' color - bluish so as to not conflict with handles */
+ col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f;
+ break;
+ }
+ break;
+ }
+ case FCURVE_COLOR_AUTO_RAINBOW:
+ default:
+ {
+ /* determine color 'automatically' using 'magic function' which uses the given args
+ * of current item index + total items to determine some RGB color
+ */
+ getcolor_fcurve_rainbow(i, items, fcu->color);
+ break;
+ }
+ }
+ }
+
+ /* free temp list */
+ ANIM_animdata_freelist(&anim_data);
+}
static void graph_refresh(const bContext *C, ScrArea *sa)
{
SpaceIpo *sipo = (SpaceIpo *)sa->spacedata.first;
- bAnimContext ac;
/* updates to data needed depends on Graph Editor mode... */
switch (sipo->mode) {
@@ -558,119 +676,7 @@ static void graph_refresh(const bContext *C, ScrArea *sa)
}
/* init/adjust F-Curve colors */
- if (ANIM_animdata_get_context(C, &ac)) {
- ListBase anim_data = {NULL, NULL};
- bAnimListElem *ale;
- size_t items;
- int filter;
- int i;
-
- UI_SetTheme(SPACE_IPO, RGN_TYPE_WINDOW);
-
- /* build list of F-Curves which will be visible as channels in channel-region
- * - we don't include ANIMFILTER_CURVEVISIBLE filter, as that will result in a
- * mismatch between channel-colors and the drawn curves
- */
- filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_NODUPLIS);
- items = ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype);
-
- /* loop over F-Curves, assigning colors */
- for (ale = anim_data.first, i = 0; ale; ale = ale->next, i++) {
- FCurve *fcu = (FCurve *)ale->data;
-
- /* set color of curve here */
- switch (fcu->color_mode) {
- case FCURVE_COLOR_CUSTOM:
- {
- /* User has defined a custom color for this curve already (we assume it's not going to cause clashes with text colors),
- * which should be left alone... Nothing needs to be done here.
- */
- break;
- }
- case FCURVE_COLOR_AUTO_RGB:
- {
- /* F-Curve's array index is automatically mapped to RGB values. This works best of 3-value vectors.
- * TODO: find a way to module the hue so that not all curves have same color...
- */
- float *col = fcu->color;
-
- switch (fcu->array_index) {
- case 0:
- UI_GetThemeColor3fv(TH_AXIS_X, col);
- break;
- case 1:
- UI_GetThemeColor3fv(TH_AXIS_Y, col);
- break;
- case 2:
- UI_GetThemeColor3fv(TH_AXIS_Z, col);
- break;
- default:
- /* 'unknown' color - bluish so as to not conflict with handles */
- col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f;
- break;
- }
- break;
- }
- case FCURVE_COLOR_AUTO_YRGB:
- {
- /* Like FCURVE_COLOR_AUTO_RGB, except this is for quaternions... */
- float *col = fcu->color;
-
- switch (fcu->array_index) {
- case 1:
- UI_GetThemeColor3fv(TH_AXIS_X, col);
- break;
- case 2:
- UI_GetThemeColor3fv(TH_AXIS_Y, col);
- break;
- case 3:
- UI_GetThemeColor3fv(TH_AXIS_Z, col);
- break;
-
- case 0:
- {
- /* Special Case: "W" channel should be yellowish, so blend X and Y channel colors... */
- float c1[3], c2[3];
- float h1[3], h2[3];
- float hresult[3];
-
- /* - get colors (rgb) */
- UI_GetThemeColor3fv(TH_AXIS_X, c1);
- UI_GetThemeColor3fv(TH_AXIS_Y, c2);
-
- /* - perform blending in HSV space (to keep brightness similar) */
- rgb_to_hsv_v(c1, h1);
- rgb_to_hsv_v(c2, h2);
-
- interp_v3_v3v3(hresult, h1, h2, 0.5f);
-
- /* - convert back to RGB for display */
- hsv_to_rgb_v(hresult, col);
- break;
- }
-
- default:
- /* 'unknown' color - bluish so as to not conflict with handles */
- col[0] = 0.3f; col[1] = 0.8f; col[2] = 1.0f;
- break;
- }
- break;
- }
- case FCURVE_COLOR_AUTO_RAINBOW:
- default:
- {
- /* determine color 'automatically' using 'magic function' which uses the given args
- * of current item index + total items to determine some RGB color
- */
- getcolor_fcurve_rainbow(i, items, fcu->color);
- break;
- }
- }
- }
-
- /* free temp list */
- ANIM_animdata_freelist(&anim_data);
- }
+ graph_refresh_fcurve_colors(C);
}
static void graph_id_remap(ScrArea *UNUSED(sa), SpaceLink *slink, ID *old_id, ID *new_id)
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index a2db6827b0e..b9d98dfe794 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -345,6 +345,13 @@ struct ImageUI_Data {
int rpass_index;
};
+static struct ImageUI_Data *ui_imageuser_data_copy(const struct ImageUI_Data *rnd_pt_src)
+{
+ struct ImageUI_Data *rnd_pt_dst = MEM_mallocN(sizeof(*rnd_pt_src), __func__);
+ memcpy(rnd_pt_dst, rnd_pt_src, sizeof(*rnd_pt_src));
+ return rnd_pt_dst;
+}
+
static void ui_imageuser_layer_menu(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt)
{
struct ImageUI_Data *rnd_data = rnd_pt;
@@ -532,9 +539,10 @@ static void ui_imageuser_view_menu_multiview(bContext *UNUSED(C), uiLayout *layo
}
/* 5 layer button callbacks... */
-static void image_multi_cb(bContext *C, void *rr_v, void *iuser_v)
+static void image_multi_cb(bContext *C, void *rnd_pt, void *rr_v)
{
- ImageUser *iuser = iuser_v;
+ struct ImageUI_Data *rnd_data = rnd_pt;
+ ImageUser *iuser = rnd_data->iuser;
BKE_image_multilayer_index(rr_v, iuser);
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
@@ -575,6 +583,8 @@ static bool ui_imageuser_layer_menu_step(bContext *C, int direction, void *rnd_p
BLI_assert(0);
}
+ BKE_image_release_renderresult(scene, image);
+
if (changed) {
BKE_image_multilayer_index(rr, iuser);
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
@@ -662,10 +672,11 @@ static bool ui_imageuser_pass_menu_step(bContext *C, int direction, void *rnd_pt
}
/* 5 view button callbacks... */
-static void image_multiview_cb(bContext *C, void *ima_v, void *iuser_v)
+static void image_multiview_cb(bContext *C, void *rnd_pt, void *UNUSED(arg_v))
{
- Image *ima = ima_v;
- ImageUser *iuser = iuser_v;
+ struct ImageUI_Data *rnd_data = rnd_pt;
+ Image *ima = rnd_data->image;
+ ImageUser *iuser = rnd_data->iuser;
BKE_image_multiview_index(ima, iuser);
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
@@ -692,7 +703,7 @@ static void uiblock_layer_pass_buttons(
uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w,
short *render_slot)
{
- static struct ImageUI_Data rnd_pt; /* XXX, workaround */
+ struct ImageUI_Data rnd_pt_local, *rnd_pt = NULL;
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but;
RenderLayer *rl = NULL;
@@ -708,10 +719,10 @@ static void uiblock_layer_pass_buttons(
wmenu2 = (3 * w) / 5;
wmenu3 = (3 * w) / 6;
wmenu4 = (3 * w) / 6;
-
- rnd_pt.image = image;
- rnd_pt.iuser = iuser;
- rnd_pt.rpass_index = 0;
+
+ rnd_pt_local.image = image;
+ rnd_pt_local.iuser = iuser;
+ rnd_pt_local.rpass_index = 0;
/* menu buts */
if (render_slot) {
@@ -723,10 +734,12 @@ static void uiblock_layer_pass_buttons(
BLI_snprintf(str, sizeof(str), IFACE_("Slot %d"), *render_slot + 1);
}
+ rnd_pt = ui_imageuser_data_copy(&rnd_pt_local);
but = uiDefMenuBut(block, ui_imageuser_slot_menu, image, str, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select Slot"));
UI_but_func_menu_step_set(but, ui_imageuser_slot_menu_step);
- UI_but_func_set(but, image_multi_cb, rr, iuser);
+ UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr);
UI_but_type_set_menu_from_pulldown(but);
+ rnd_pt = NULL;
}
if (rr) {
@@ -738,15 +751,18 @@ static void uiblock_layer_pass_buttons(
fake_name = ui_imageuser_layer_fake_name(rr);
rpass_index = iuser->layer - (fake_name ? 1 : 0);
rl = BLI_findlink(&rr->layers, rpass_index);
- rnd_pt.rpass_index = rpass_index;
+ rnd_pt_local.rpass_index = rpass_index;
if (RE_layers_have_name(rr)) {
display_name = rl ? rl->name : (fake_name ? fake_name : "");
- but = uiDefMenuBut(block, ui_imageuser_layer_menu, &rnd_pt, display_name,
- 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer"));
+ rnd_pt = ui_imageuser_data_copy(&rnd_pt_local);
+ but = uiDefMenuBut(
+ block, ui_imageuser_layer_menu, rnd_pt, display_name,
+ 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer"));
UI_but_func_menu_step_set(but, ui_imageuser_layer_menu_step);
- UI_but_func_set(but, image_multi_cb, rr, iuser);
+ UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr);
UI_but_type_set_menu_from_pulldown(but);
+ rnd_pt = NULL;
}
/* pass */
@@ -754,11 +770,14 @@ static void uiblock_layer_pass_buttons(
rpass = (rl ? BLI_findlink(&rl->passes, iuser->pass - (fake_name ? 1 : 0)) : NULL);
display_name = rpass ? rpass->internal_name : (fake_name ? fake_name : "");
- but = uiDefMenuBut(block, ui_imageuser_pass_menu, &rnd_pt, IFACE_(display_name),
- 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass"));
+ rnd_pt = ui_imageuser_data_copy(&rnd_pt_local);
+ but = uiDefMenuBut(
+ block, ui_imageuser_pass_menu, rnd_pt, IFACE_(display_name),
+ 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass"));
UI_but_func_menu_step_set(but, ui_imageuser_pass_menu_step);
- UI_but_func_set(but, image_multi_cb, rr, iuser);
+ UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr);
UI_but_type_set_menu_from_pulldown(but);
+ rnd_pt = NULL;
/* view */
if (BLI_listbase_count_ex(&rr->views, 2) > 1 &&
@@ -767,9 +786,13 @@ static void uiblock_layer_pass_buttons(
rview = BLI_findlink(&rr->views, iuser->view);
display_name = rview ? rview->name : "";
- but = uiDefMenuBut(block, ui_imageuser_view_menu_rr, &rnd_pt, display_name, 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View"));
- UI_but_func_set(but, image_multi_cb, rr, iuser);
+ rnd_pt = ui_imageuser_data_copy(&rnd_pt_local);
+ but = uiDefMenuBut(
+ block, ui_imageuser_view_menu_rr, rnd_pt, display_name,
+ 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View"));
+ UI_but_funcN_set(but, image_multi_cb, rnd_pt, rr);
UI_but_type_set_menu_from_pulldown(but);
+ rnd_pt = NULL;
}
}
@@ -787,9 +810,13 @@ static void uiblock_layer_pass_buttons(
}
}
- but = uiDefMenuBut(block, ui_imageuser_view_menu_multiview, &rnd_pt, display_name, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View"));
- UI_but_func_set(but, image_multiview_cb, image, iuser);
+ rnd_pt = ui_imageuser_data_copy(&rnd_pt_local);
+ but = uiDefMenuBut(
+ block, ui_imageuser_view_menu_multiview, rnd_pt, display_name,
+ 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View"));
+ UI_but_funcN_set(but, image_multiview_cb, rnd_pt, NULL);
UI_but_type_set_menu_from_pulldown(but);
+ rnd_pt = NULL;
}
}
@@ -1303,7 +1330,7 @@ void IMAGE_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "IMAGE_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = image_properties_toggle_exec;
ot->poll = ED_operator_image_active;
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 06caf930988..1158e692182 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -1061,6 +1061,12 @@ typedef struct ImageOpenData {
ImageFormatData im_format;
} ImageOpenData;
+typedef struct ImageFrameRange {
+ struct ImageFrameRange *next, *prev;
+ ListBase frames;
+ char filepath[FILE_MAX];
+} ImageFrameRange;
+
typedef struct ImageFrame {
struct ImageFrame *next, *prev;
int framenr;
@@ -1086,10 +1092,10 @@ static void image_open_cancel(bContext *UNUSED(C), wmOperator *op)
* \param frames [out] the list of frame numbers found in the files matching the first one by name
* \param path [out] the full path of the first file in the list of image files
*/
-static void image_sequence_get_frames(PointerRNA *ptr, ListBase *frames, char *path, const size_t maxlen)
+static void image_sequence_get_frame_ranges(PointerRNA *ptr, ListBase *frames_all)
{
char dir[FILE_MAXDIR];
- bool is_first_entry = true;
+ ImageFrameRange *frame_range = NULL;
RNA_string_get(ptr, "directory", dir);
RNA_BEGIN (ptr, itemptr, "files")
@@ -1101,29 +1107,26 @@ static void image_sequence_get_frames(PointerRNA *ptr, ListBase *frames, char *p
ImageFrame *frame = MEM_callocN(sizeof(ImageFrame), "image_frame");
/* use the first file in the list as base filename */
- if (is_first_entry) {
- BLI_join_dirfile(path, maxlen, dir, filename);
- frame->framenr = BLI_stringdec(filename, base_head, base_tail, &digits);
- BLI_addtail(frames, frame);
- is_first_entry = false;
+ frame->framenr = BLI_stringdec(filename, head, tail, &digits);
+
+ /* still in the same sequence */
+ if ((frame_range != NULL) &&
+ (STREQLEN(base_head, head, FILE_MAX)) &&
+ (STREQLEN(base_tail, tail, FILE_MAX)))
+ {
+ /* pass */
}
else {
- frame->framenr = BLI_stringdec(filename, head, tail, &digits);
+ /* start a new frame range */
+ frame_range = MEM_callocN(sizeof(*frame_range), __func__);
+ BLI_join_dirfile(frame_range->filepath, sizeof(frame_range->filepath), dir, filename);
+ BLI_addtail(frames_all, frame_range);
- /* still in the same sequence */
- if ((STREQLEN(base_head, head, FILE_MAX)) &&
- (STREQLEN(base_tail, tail, FILE_MAX)))
- {
- BLI_addtail(frames, frame);
- }
- else {
- /* different file base name found, is ignored */
- MEM_freeN(filename);
- MEM_freeN(frame);
- break;
- }
+ BLI_strncpy(base_head, head, sizeof(base_head));
+ BLI_strncpy(base_tail, tail, sizeof(base_tail));
}
+ BLI_addtail(&frame_range->frames, frame);
MEM_freeN(filename);
}
RNA_END
@@ -1164,6 +1167,52 @@ static int image_sequence_get_len(ListBase *frames, int *ofs)
return 0;
}
+static Image *image_open_single(
+ wmOperator *op, const char *filepath, const char *relbase,
+ bool is_relative_path, bool use_multiview, int frame_seq_len)
+{
+ bool exists = false;
+ Image *ima = NULL;
+
+ errno = 0;
+ ima = BKE_image_load_exists_ex(filepath, &exists);
+
+ if (!ima) {
+ if (op->customdata) MEM_freeN(op->customdata);
+ BKE_reportf(op->reports, RPT_ERROR, "Cannot read '%s': %s",
+ filepath, errno ? strerror(errno) : TIP_("unsupported image format"));
+ return NULL;
+ }
+
+ if (!exists) {
+ /* only image path after save, never ibuf */
+ if (is_relative_path) {
+ BLI_path_rel(ima->name, relbase);
+ }
+
+ /* handle multiview images */
+ if (use_multiview) {
+ ImageOpenData *iod = op->customdata;
+ ImageFormatData *imf = &iod->im_format;
+
+ ima->flag |= IMA_USE_VIEWS;
+ ima->views_format = imf->views_format;
+ *ima->stereo3d_format = imf->stereo3d_format;
+ }
+ else {
+ ima->flag &= ~IMA_USE_VIEWS;
+ BKE_image_free_views(ima);
+ }
+
+ if ((frame_seq_len > 1) && (ima->source == IMA_SRC_FILE)) {
+ ima->source = IMA_SRC_SEQUENCE;
+ }
+ }
+
+ return ima;
+}
+
+
static int image_open_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@@ -1174,70 +1223,60 @@ static int image_open_exec(bContext *C, wmOperator *op)
ImageOpenData *iod = op->customdata;
PointerRNA idptr;
Image *ima = NULL;
- char path[FILE_MAX];
+ char filepath[FILE_MAX];
int frame_seq_len = 0;
int frame_ofs = 1;
- bool exists = false;
const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path");
+ const bool use_multiview = RNA_boolean_get(op->ptr, "use_multiview");
- RNA_string_get(op->ptr, "filepath", path);
+ if (!op->customdata)
+ image_open_init(C, op);
+
+ RNA_string_get(op->ptr, "filepath", filepath);
if (RNA_struct_property_is_set(op->ptr, "directory") &&
RNA_struct_property_is_set(op->ptr, "files"))
{
- /* only to pass to imbuf */
- char path_full[FILE_MAX];
- BLI_strncpy(path_full, path, sizeof(path_full));
- BLI_path_abs(path_full, G.main->name);
+ bool was_relative = BLI_path_is_rel(filepath);
+ ListBase frame_ranges_all;
- if (!IMB_isanim(path_full)) {
- bool was_relative = BLI_path_is_rel(path);
- ListBase frames;
+ BLI_listbase_clear(&frame_ranges_all);
+ image_sequence_get_frame_ranges(op->ptr, &frame_ranges_all);
+ for (ImageFrameRange *frame_range = frame_ranges_all.first; frame_range; frame_range = frame_range->next) {
+ int frame_range_ofs;
+ int frame_range_seq_len = image_sequence_get_len(&frame_range->frames, &frame_range_ofs);
+ BLI_freelistN(&frame_range->frames);
- BLI_listbase_clear(&frames);
- image_sequence_get_frames(op->ptr, &frames, path, sizeof(path));
- frame_seq_len = image_sequence_get_len(&frames, &frame_ofs);
- BLI_freelistN(&frames);
+ char filepath_range[FILE_MAX];
+ BLI_strncpy(filepath_range, frame_range->filepath, sizeof(filepath_range));
if (was_relative) {
- BLI_path_rel(path, G.main->name);
+ BLI_path_rel(filepath_range, bmain->name);
}
- }
- }
-
- errno = 0;
- ima = BKE_image_load_exists_ex(path, &exists);
+ Image *ima_range = image_open_single(
+ op, filepath_range, bmain->name,
+ is_relative_path, use_multiview, frame_range_seq_len);
- if (!ima) {
- if (op->customdata) MEM_freeN(op->customdata);
- BKE_reportf(op->reports, RPT_ERROR, "Cannot read '%s': %s",
- path, errno ? strerror(errno) : TIP_("unsupported image format"));
- return OPERATOR_CANCELLED;
- }
-
- if (!op->customdata)
- image_open_init(C, op);
-
- /* handle multiview images */
- if (RNA_boolean_get(op->ptr, "use_multiview")) {
- ImageFormatData *imf = &iod->im_format;
-
- ima->flag |= IMA_USE_VIEWS;
- ima->views_format = imf->views_format;
- *ima->stereo3d_format = imf->stereo3d_format;
+ /* take the first image */
+ if ((ima == NULL) && ima_range) {
+ ima = ima_range;
+ frame_seq_len = frame_range_seq_len;
+ frame_ofs = frame_range_ofs;
+ }
+ }
+ BLI_freelistN(&frame_ranges_all);
}
else {
- ima->flag &= ~IMA_USE_VIEWS;
- BKE_image_free_views(ima);
+ /* for drag & drop etc. */
+ ima = image_open_single(
+ op, filepath, bmain->name,
+ is_relative_path, use_multiview, 1);
}
- /* only image path after save, never ibuf */
- if (is_relative_path) {
- if (!exists) {
- BLI_path_rel(ima->name, bmain->name);
- }
+ if (ima == NULL) {
+ return OPERATOR_CANCELLED;
}
/* hook into UI */
@@ -1245,11 +1284,9 @@ static int image_open_exec(bContext *C, wmOperator *op)
if (iod->pprop.prop) {
/* when creating new ID blocks, use is already 1, but RNA
- * pointer se also increases user, so this compensates it */
+ * pointer use also increases user, so this compensates it */
id_us_min(&ima->id);
- if ((frame_seq_len > 1) && ima->source == IMA_SRC_FILE) {
- ima->source = IMA_SRC_SEQUENCE;
- }
+
RNA_id_pointer_create(&ima->id, &idptr);
RNA_property_pointer_set(&iod->pprop.ptr, iod->pprop.prop, idptr);
RNA_property_update(C, &iod->pprop.ptr, iod->pprop.prop);
@@ -2359,7 +2396,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
if (prop) {
/* when creating new ID blocks, use is already 1, but RNA
- * pointer se also increases user, so this compensates it */
+ * pointer use also increases user, so this compensates it */
id_us_min(&ima->id);
RNA_id_pointer_create(&ima->id, &idptr);
diff --git a/source/blender/editors/space_logic/logic_buttons.c b/source/blender/editors/space_logic/logic_buttons.c
index 12c7ef3d3ec..e5eee21ed08 100644
--- a/source/blender/editors/space_logic/logic_buttons.c
+++ b/source/blender/editors/space_logic/logic_buttons.c
@@ -63,7 +63,7 @@ static int logic_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
void LOGIC_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "LOGIC_OT_properties";
ot->exec = logic_properties_toggle_exec;
diff --git a/source/blender/editors/space_logic/logic_ops.c b/source/blender/editors/space_logic/logic_ops.c
index 51f4a61047d..074368a82c5 100644
--- a/source/blender/editors/space_logic/logic_ops.c
+++ b/source/blender/editors/space_logic/logic_ops.c
@@ -65,7 +65,7 @@ static int edit_sensor_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "sensor", &RNA_Sensor);
- if (ptr.data && ((ID *)ptr.id.data)->lib) return 0;
+ if (ptr.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0;
return 1;
}
@@ -73,7 +73,7 @@ static int edit_controller_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "controller", &RNA_Controller);
- if (ptr.data && ((ID *)ptr.id.data)->lib) return 0;
+ if (ptr.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0;
return 1;
}
@@ -81,7 +81,7 @@ static int edit_actuator_poll(bContext *C)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "actuator", &RNA_Actuator);
- if (ptr.data && ((ID *)ptr.id.data)->lib) return 0;
+ if (ptr.data && ID_IS_LINKED_DATABLOCK(ptr.id.data)) return 0;
return 1;
}
diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c
index cbdc476bee6..9032d286933 100644
--- a/source/blender/editors/space_nla/nla_buttons.c
+++ b/source/blender/editors/space_nla/nla_buttons.c
@@ -567,7 +567,7 @@ void NLA_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "NLA_OT_properties";
- ot->description = "Toggle display properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = nla_properties_toggle_exec;
ot->poll = ED_operator_nla_active;
diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c
index b0adabe4d1d..5b3c062e16d 100644
--- a/source/blender/editors/space_nla/nla_draw.c
+++ b/source/blender/editors/space_nla/nla_draw.c
@@ -62,7 +62,7 @@
#include "UI_resources.h"
#include "UI_view2d.h"
-
+#include "nla_private.h"
#include "nla_intern.h" /* own include */
@@ -145,6 +145,62 @@ static void nla_action_draw_keyframes(AnimData *adt, bAction *act, View2D *v2d,
BLI_dlrbTree_free(&keys);
}
+/* Strip Markers ------------------------ */
+
+/* Markers inside an action strip */
+static void nla_actionclip_draw_markers(NlaStrip *strip, float yminc, float ymaxc)
+{
+ bAction *act = strip->act;
+ TimeMarker *marker;
+
+ if (ELEM(NULL, strip->act, strip->act->markers.first))
+ return;
+
+ for (marker = act->markers.first; marker; marker = marker->next) {
+ if ((marker->frame > strip->actstart) && (marker->frame < strip->actend)) {
+ float frame = nlastrip_get_frame(strip, marker->frame, NLATIME_CONVERT_MAP);
+
+ /* just a simple line for now */
+ // XXX: draw a triangle instead...
+ fdrawline(frame, yminc + 1, frame, ymaxc - 1);
+ }
+ }
+}
+
+/* Markers inside a NLA-Strip */
+static void nla_strip_draw_markers(NlaStrip *strip, float yminc, float ymaxc)
+{
+ glLineWidth(2.0);
+
+ if (strip->type == NLASTRIP_TYPE_CLIP) {
+ /* try not to be too conspicuous, while being visible enough when transforming */
+ if (strip->flag & NLASTRIP_FLAG_SELECT)
+ UI_ThemeColorShade(TH_STRIP_SELECT, -60);
+ else
+ UI_ThemeColorShade(TH_STRIP_SELECT, -40);
+
+ setlinestyle(3);
+
+ /* just draw the markers in this clip */
+ nla_actionclip_draw_markers(strip, yminc, ymaxc);
+
+ setlinestyle(0);
+ }
+ else if (strip->flag & NLASTRIP_FLAG_TEMP_META) {
+ /* just a solid color, so that it is very easy to spot */
+ UI_ThemeColorShade(TH_STRIP_SELECT, 20);
+
+ /* draw the markers in the first level of strips only (if they are actions) */
+ for (NlaStrip *nls = strip->strips.first; nls; nls = nls->next) {
+ if (nls->type == NLASTRIP_TYPE_CLIP) {
+ nla_actionclip_draw_markers(nls, yminc, ymaxc);
+ }
+ }
+ }
+
+ glLineWidth(1.0);
+}
+
/* Strips (Proper) ---------------------- */
/* get colors for drawing NLA-Strips */
@@ -361,6 +417,10 @@ static void nla_draw_strip(SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStri
nla_draw_strip_curves(strip, yminc, ymaxc);
+ /* draw markings indicating locations of local markers (useful for lining up different actions) */
+ if ((snla->flag & SNLA_NOLOCALMARKERS) == 0)
+ nla_strip_draw_markers(strip, yminc, ymaxc);
+
/* draw strip outline
* - color used here is to indicate active vs non-active
*/
diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c
index a2159696394..3e7e8ccc3f4 100644
--- a/source/blender/editors/space_nla/nla_edit.c
+++ b/source/blender/editors/space_nla/nla_edit.c
@@ -1856,6 +1856,7 @@ void NLA_OT_action_sync_length(wmOperatorType *ot)
static int nlaedit_make_single_user_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Main *bmain = CTX_data_main(C);
bAnimContext ac;
ListBase anim_data = {NULL, NULL};
@@ -1887,7 +1888,7 @@ static int nlaedit_make_single_user_exec(bContext *C, wmOperator *UNUSED(op))
/* multi-user? */
if (ID_REAL_USERS(strip->act) > 1) {
/* make a new copy of the action for us to use (it will have 1 user already) */
- bAction *new_action = BKE_action_copy(strip->act);
+ bAction *new_action = BKE_action_copy(bmain, strip->act);
/* decrement user count of our existing action */
id_us_min(&strip->act->id);
@@ -1945,6 +1946,7 @@ static short bezt_apply_nlamapping(KeyframeEditData *ked, BezTriple *bezt)
static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op))
{
+ Main *bmain = CTX_data_main(C);
bAnimContext ac;
ListBase anim_data = {NULL, NULL};
@@ -1974,7 +1976,7 @@ static int nlaedit_apply_scale_exec(bContext *C, wmOperator *UNUSED(op))
continue;
if (strip->act->id.us > 1) {
/* make a copy of the Action to work on */
- bAction *act = BKE_action_copy(strip->act);
+ bAction *act = BKE_action_copy(bmain, strip->act);
/* set this as the new referenced action, decrementing the users of the old one */
id_us_min(&strip->act->id);
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index ddbd07616bc..7b08b8368ba 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -1338,7 +1338,7 @@ static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, Point
scn_ptr = RNA_pointer_get(ptr, "scene");
RNA_string_get(&scn_ptr, "name", scene_name);
-
+
WM_operator_properties_create_ptr(&op_ptr, ot);
RNA_string_set(&op_ptr, "layer", layer_name);
RNA_string_set(&op_ptr, "scene", scene_name);
diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c
index 52b0292b9a3..f0567924edd 100644
--- a/source/blender/editors/space_node/node_buttons.c
+++ b/source/blender/editors/space_node/node_buttons.c
@@ -222,7 +222,7 @@ static int node_properties_poll(bContext *C)
void NODE_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
- ot->description = "Toggles the properties panel display";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "NODE_OT_properties";
ot->exec = node_properties_toggle_exec;
diff --git a/source/blender/editors/space_node/node_group.c b/source/blender/editors/space_node/node_group.c
index 5c58e9b720c..26eeaa91dd0 100644
--- a/source/blender/editors/space_node/node_group.c
+++ b/source/blender/editors/space_node/node_group.c
@@ -255,7 +255,7 @@ static int node_group_ungroup(bNodeTree *ntree, bNode *gnode)
bAction *waction;
/* firstly, wgroup needs to temporary dummy action that can be destroyed, as it shares copies */
- waction = wgroup->adt->action = BKE_action_copy(wgroup->adt->action);
+ waction = wgroup->adt->action = BKE_action_copy(G.main, wgroup->adt->action);
/* now perform the moving */
BKE_animdata_separate_by_basepath(&wgroup->id, &ntree->id, &anim_basepaths);
@@ -794,22 +794,37 @@ static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree,
link->tosock = node_group_find_input_socket(gnode, iosock->identifier);
}
else if (fromselect) {
- bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock);
- bNodeSocket *output_sock;
-
- /* update the group node and interface node sockets,
- * so the new interface socket can be linked.
- */
- node_group_verify(ntree, gnode, (ID *)ngroup);
- node_group_output_verify(ngroup, output_node, (ID *)ngroup);
+ /* First check whether the source of this link is already connected to an output.
+ * If yes, reuse that output instead of duplicating it. */
+ bool connected = false;
+ bNodeLink *olink;
+ for (olink = ngroup->links.first; olink; olink = olink->next) {
+ if (olink->fromsock == link->fromsock && olink->tonode == output_node) {
+ bNodeSocket *output_sock = node_group_find_output_socket(gnode, olink->tosock->identifier);
+ link->fromnode = gnode;
+ link->fromsock = output_sock;
+ connected = true;
+ }
+ }
- /* create new internal link */
- output_sock = node_group_output_find_socket(output_node, iosock->identifier);
- nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock);
-
- /* redirect external link */
- link->fromnode = gnode;
- link->fromsock = node_group_find_output_socket(gnode, iosock->identifier);
+ if (!connected) {
+ bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock);
+ bNodeSocket *output_sock;
+
+ /* update the group node and interface node sockets,
+ * so the new interface socket can be linked.
+ */
+ node_group_verify(ntree, gnode, (ID *)ngroup);
+ node_group_output_verify(ngroup, output_node, (ID *)ngroup);
+
+ /* create new internal link */
+ output_sock = node_group_output_find_socket(output_node, iosock->identifier);
+ nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock);
+
+ /* redirect external link */
+ link->fromnode = gnode;
+ link->fromsock = node_group_find_output_socket(gnode, iosock->identifier);
+ }
}
}
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index d7249897723..ea3869ef387 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -1726,7 +1726,7 @@ static int node_insert_offset_modal(bContext *C, wmOperator *UNUSED(op), const w
float duration;
bool redraw = false;
- if (!snode || event->type != TIMER || iofsd->anim_timer != event->customdata)
+ if (!snode || event->type != TIMER || iofsd == NULL || iofsd->anim_timer != event->customdata)
return OPERATOR_PASS_THROUGH;
duration = (float)iofsd->anim_timer->duration;
diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c
index 43e9c262172..b57462df53b 100644
--- a/source/blender/editors/space_outliner/outliner_draw.c
+++ b/source/blender/editors/space_outliner/outliner_draw.c
@@ -402,7 +402,7 @@ void restrictbutton_gr_restrict_flag(void *poin, void *poin2, int flag)
if (group_restrict_flag(gr, flag)) {
for (gob = gr->gobject.first; gob; gob = gob->next) {
- if (gob->ob->id.lib)
+ if (ID_IS_LINKED_DATABLOCK(gob->ob))
continue;
gob->ob->restrictflag &= ~flag;
@@ -414,7 +414,7 @@ void restrictbutton_gr_restrict_flag(void *poin, void *poin2, int flag)
}
else {
for (gob = gr->gobject.first; gob; gob = gob->next) {
- if (gob->ob->id.lib)
+ if (ID_IS_LINKED_DATABLOCK(gob->ob))
continue;
/* not in editmode */
@@ -655,7 +655,7 @@ static void outliner_draw_restrictbuts(uiBlock *block, Scene *scene, ARegion *ar
int but_flag = UI_BUT_DRAG_LOCK;
gr = (Group *)tselem->id;
- if (gr->id.lib)
+ if (ID_IS_LINKED_DATABLOCK(gr))
but_flag |= UI_BUT_DISABLED;
UI_block_emboss_set(block, UI_EMBOSS_NONE);
@@ -828,7 +828,7 @@ static void outliner_draw_userbuts(uiBlock *block, ARegion *ar, SpaceOops *soops
char buf[16] = "";
int but_flag = UI_BUT_DRAG_LOCK;
- if (id->lib)
+ if (ID_IS_LINKED_DATABLOCK(id))
but_flag |= UI_BUT_DISABLED;
UI_block_emboss_set(block, UI_EMBOSS_NONE);
@@ -993,7 +993,8 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon)
}
else {
uiBut *but = uiDefIconBut(arg->block, UI_BTYPE_LABEL, 0, icon, arg->xb, arg->yb, UI_UNIT_X, UI_UNIT_Y, NULL,
- 0.0, 0.0, 1.0, arg->alpha, (arg->id && arg->id->lib) ? arg->id->lib->name : "");
+ 0.0, 0.0, 1.0, arg->alpha,
+ (arg->id && ID_IS_LINKED_DATABLOCK(arg->id)) ? arg->id->lib->name : "");
if (arg->id)
UI_but_drag_set_id(but, arg->id);
@@ -1001,7 +1002,7 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon)
}
-static void tselem_draw_gp_icon_uibut(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl)
+static void UNUSED_FUNCTION(tselem_draw_gp_icon_uibut)(struct DrawIconArg *arg, ID *id, bGPDlayer *gpl)
{
/* restrict column clip - skip it for now... */
if (arg->x >= arg->xmax) {
@@ -1232,113 +1233,116 @@ static void tselem_draw_icon(uiBlock *block, int xmax, float x, float y, TreeSto
else
UI_icon_draw(x, y, RNA_struct_ui_icon(te->rnaptr.type));
break;
+ /* Removed the icons from outliner. Need a better structure with Layers, Palettes and Colors */
+#if 0
case TSE_GP_LAYER:
tselem_draw_gp_icon_uibut(&arg, tselem->id, te->directdata);
break;
+#endif
default:
UI_icon_draw(x, y, ICON_DOT); break;
}
}
- else if (GS(tselem->id->name) == ID_OB) {
- Object *ob = (Object *)tselem->id;
- switch (ob->type) {
- case OB_LAMP:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LAMP); break;
- case OB_MESH:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_MESH); break;
- case OB_CAMERA:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CAMERA); break;
- case OB_CURVE:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break;
- case OB_MBALL:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_META); break;
- case OB_LATTICE:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LATTICE); break;
- case OB_ARMATURE:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_ARMATURE); break;
- case OB_FONT:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_FONT); break;
- case OB_SURF:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SURFACE); break;
- case OB_SPEAKER:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SPEAKER); break;
- case OB_EMPTY:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_EMPTY); break;
-
+ else if (tselem->id) {
+ if (GS(tselem->id->name) == ID_OB) {
+ Object *ob = (Object *)tselem->id;
+ switch (ob->type) {
+ case OB_LAMP:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LAMP); break;
+ case OB_MESH:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_MESH); break;
+ case OB_CAMERA:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CAMERA); break;
+ case OB_CURVE:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_CURVE); break;
+ case OB_MBALL:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_META); break;
+ case OB_LATTICE:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_LATTICE); break;
+ case OB_ARMATURE:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_ARMATURE); break;
+ case OB_FONT:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_FONT); break;
+ case OB_SURF:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SURFACE); break;
+ case OB_SPEAKER:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_SPEAKER); break;
+ case OB_EMPTY:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_OB_EMPTY); break;
+ }
}
- }
- else {
- switch (GS(tselem->id->name)) {
- case ID_SCE:
- tselem_draw_icon_uibut(&arg, ICON_SCENE_DATA); break;
- case ID_ME:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_MESH); break;
- case ID_CU:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CURVE); break;
- case ID_MB:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_META); break;
- case ID_LT:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LATTICE); break;
- case ID_LA:
- {
- Lamp *la = (Lamp *)tselem->id;
-
- switch (la->type) {
- case LA_LOCAL:
- tselem_draw_icon_uibut(&arg, ICON_LAMP_POINT); break;
- case LA_SUN:
- tselem_draw_icon_uibut(&arg, ICON_LAMP_SUN); break;
- case LA_SPOT:
- tselem_draw_icon_uibut(&arg, ICON_LAMP_SPOT); break;
- case LA_HEMI:
- tselem_draw_icon_uibut(&arg, ICON_LAMP_HEMI); break;
- case LA_AREA:
- tselem_draw_icon_uibut(&arg, ICON_LAMP_AREA); break;
- default:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LAMP); break;
+ else {
+ switch (GS(tselem->id->name)) {
+ case ID_SCE:
+ tselem_draw_icon_uibut(&arg, ICON_SCENE_DATA); break;
+ case ID_ME:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_MESH); break;
+ case ID_CU:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CURVE); break;
+ case ID_MB:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_META); break;
+ case ID_LT:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LATTICE); break;
+ case ID_LA:
+ {
+ Lamp *la = (Lamp *)tselem->id;
+ switch (la->type) {
+ case LA_LOCAL:
+ tselem_draw_icon_uibut(&arg, ICON_LAMP_POINT); break;
+ case LA_SUN:
+ tselem_draw_icon_uibut(&arg, ICON_LAMP_SUN); break;
+ case LA_SPOT:
+ tselem_draw_icon_uibut(&arg, ICON_LAMP_SPOT); break;
+ case LA_HEMI:
+ tselem_draw_icon_uibut(&arg, ICON_LAMP_HEMI); break;
+ case LA_AREA:
+ tselem_draw_icon_uibut(&arg, ICON_LAMP_AREA); break;
+ default:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_LAMP); break;
+ }
+ break;
}
- break;
+ case ID_MA:
+ tselem_draw_icon_uibut(&arg, ICON_MATERIAL_DATA); break;
+ case ID_TE:
+ tselem_draw_icon_uibut(&arg, ICON_TEXTURE_DATA); break;
+ case ID_IM:
+ tselem_draw_icon_uibut(&arg, ICON_IMAGE_DATA); break;
+ case ID_SPK:
+ case ID_SO:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_SPEAKER); break;
+ case ID_AR:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_ARMATURE); break;
+ case ID_CA:
+ tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CAMERA); break;
+ case ID_KE:
+ tselem_draw_icon_uibut(&arg, ICON_SHAPEKEY_DATA); break;
+ case ID_WO:
+ tselem_draw_icon_uibut(&arg, ICON_WORLD_DATA); break;
+ case ID_AC:
+ tselem_draw_icon_uibut(&arg, ICON_ACTION); break;
+ case ID_NLA:
+ tselem_draw_icon_uibut(&arg, ICON_NLA); break;
+ case ID_TXT:
+ tselem_draw_icon_uibut(&arg, ICON_SCRIPT); break;
+ case ID_GR:
+ tselem_draw_icon_uibut(&arg, ICON_GROUP); break;
+ case ID_LI:
+ if (tselem->id->tag & LIB_TAG_MISSING) {
+ tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_BROKEN);
+ }
+ else if (((Library *)tselem->id)->parent) {
+ tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_INDIRECT);
+ }
+ else {
+ tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_DIRECT);
+ }
+ break;
+ case ID_LS:
+ tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break;
+ case ID_GD:
+ tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break;
}
- case ID_MA:
- tselem_draw_icon_uibut(&arg, ICON_MATERIAL_DATA); break;
- case ID_TE:
- tselem_draw_icon_uibut(&arg, ICON_TEXTURE_DATA); break;
- case ID_IM:
- tselem_draw_icon_uibut(&arg, ICON_IMAGE_DATA); break;
- case ID_SPK:
- case ID_SO:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_SPEAKER); break;
- case ID_AR:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_ARMATURE); break;
- case ID_CA:
- tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_CAMERA); break;
- case ID_KE:
- tselem_draw_icon_uibut(&arg, ICON_SHAPEKEY_DATA); break;
- case ID_WO:
- tselem_draw_icon_uibut(&arg, ICON_WORLD_DATA); break;
- case ID_AC:
- tselem_draw_icon_uibut(&arg, ICON_ACTION); break;
- case ID_NLA:
- tselem_draw_icon_uibut(&arg, ICON_NLA); break;
- case ID_TXT:
- tselem_draw_icon_uibut(&arg, ICON_SCRIPT); break;
- case ID_GR:
- tselem_draw_icon_uibut(&arg, ICON_GROUP); break;
- case ID_LI:
- if (tselem->id->tag & LIB_TAG_MISSING) {
- tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_BROKEN);
- }
- else if (((Library *)tselem->id)->parent) {
- tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_INDIRECT);
- }
- else {
- tselem_draw_icon_uibut(&arg, ICON_LIBRARY_DATA_DIRECT);
- }
- break;
- case ID_LS:
- tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break;
- case ID_GD:
- tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break;
}
}
}
@@ -1567,7 +1571,7 @@ static void outliner_draw_tree_element(
else
offsx += 2 * ufac;
- if (tselem->type == 0 && tselem->id->lib) {
+ if (tselem->type == 0 && ID_IS_LINKED_DATABLOCK(tselem->id)) {
glPixelTransferf(GL_ALPHA_SCALE, 0.5f);
if (tselem->id->tag & LIB_TAG_MISSING) {
UI_icon_draw((float)startx + offsx, (float)*starty + 2 * ufac, ICON_LIBRARY_DATA_BROKEN);
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 07608b59d2e..3c47f542dae 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -232,7 +232,7 @@ static void do_item_rename(ARegion *ar, TreeElement *te, TreeStoreElem *tselem,
else if (ELEM(tselem->type, TSE_SEQUENCE, TSE_SEQ_STRIP, TSE_SEQUENCE_DUP)) {
BKE_report(reports, RPT_WARNING, "Cannot edit sequence name");
}
- else if (tselem->id->lib) {
+ else if (ID_IS_LINKED_DATABLOCK(tselem->id)) {
BKE_report(reports, RPT_WARNING, "Cannot edit external libdata");
}
else if (te->idcode == ID_LI && ((Library *)tselem->id)->parent) {
@@ -417,13 +417,13 @@ static int outliner_id_remap_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
- if (!(old_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) {
+ if (!(old_id && new_id && (old_id != new_id) && (GS(old_id->name) == GS(new_id->name)))) {
BKE_reportf(op->reports, RPT_ERROR_INVALID_INPUT, "Invalid old/new ID pair ('%s' / '%s')",
- old_id->name, new_id->name);
+ old_id ? old_id->name : "Invalid ID", new_id ? new_id->name : "Invalid ID");
return OPERATOR_CANCELLED;
}
- if (old_id->lib) {
+ if (ID_IS_LINKED_DATABLOCK(old_id)) {
BKE_reportf(op->reports, RPT_WARNING,
"Old ID '%s' is linked from a library, indirect usages of this datablock will not be remapped",
old_id->name);
@@ -1982,7 +1982,7 @@ static int parent_drop_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "child", childname);
ob = (Object *)BKE_libblock_find_name(ID_OB, childname);
- if (ob->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(ob)) {
BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
return OPERATOR_CANCELLED;
}
@@ -2030,7 +2030,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
if (ob == par) {
return OPERATOR_CANCELLED;
}
- if (ob->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(ob)) {
BKE_report(op->reports, RPT_INFO, "Can't edit library linked object");
return OPERATOR_CANCELLED;
}
@@ -2239,7 +2239,7 @@ static int scene_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
RNA_string_get(op->ptr, "object", obname);
ob = (Object *)BKE_libblock_find_name(ID_OB, obname);
- if (ELEM(NULL, ob, scene) || scene->id.lib != NULL) {
+ if (ELEM(NULL, ob, scene) || ID_IS_LINKED_DATABLOCK(scene)) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c
index bfec62997e1..e38886abeac 100644
--- a/source/blender/editors/space_outliner/outliner_tools.c
+++ b/source/blender/editors/space_outliner/outliner_tools.c
@@ -435,11 +435,11 @@ static void id_local_cb(
bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), TreeElement *UNUSED(te),
TreeStoreElem *UNUSED(tsep), TreeStoreElem *tselem, void *UNUSED(user_data))
{
- if (tselem->id->lib && (tselem->id->tag & LIB_TAG_EXTERN)) {
+ if (ID_IS_LINKED_DATABLOCK(tselem->id) && (tselem->id->tag & LIB_TAG_EXTERN)) {
+ Main *bmain = CTX_data_main(C);
/* if the ID type has no special local function,
* just clear the lib */
- if (id_make_local(tselem->id, false) == false) {
- Main *bmain = CTX_data_main(C);
+ if (id_make_local(bmain, tselem->id, false, false) == false) {
id_clear_lib_data(bmain, tselem->id);
}
}
@@ -516,23 +516,16 @@ static void group_linkobs2scene_cb(
Group *group = (Group *)tselem->id;
GroupObject *gob;
Base *base;
-
+
for (gob = group->gobject.first; gob; gob = gob->next) {
base = BKE_scene_base_find(scene, gob->ob);
- if (base) {
- base->object->flag |= SELECT;
- base->flag |= SELECT;
- }
- else {
+ if (!base) {
/* link to scene */
- base = MEM_callocN(sizeof(Base), "add_base");
- BLI_addhead(&scene->base, base);
- base->lay = gob->ob->lay;
- gob->ob->flag |= SELECT;
- base->flag = gob->ob->flag;
- base->object = gob->ob;
+ base = BKE_scene_base_add(scene, gob->ob);
id_lib_extern((ID *)gob->ob); /* in case these are from a linked group */
}
+ base->object->flag |= SELECT;
+ base->flag |= SELECT;
}
}
@@ -1352,7 +1345,7 @@ static int outliner_lib_operation_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
SpaceOops *soops = CTX_wm_space_outliner(C);
int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0;
- eOutlinerIdOpTypes event;
+ eOutlinerLibOpTypes event;
/* check for invalid states */
if (soops == NULL)
diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c
index a687f61d69f..b22e6595caf 100644
--- a/source/blender/editors/space_outliner/outliner_tree.c
+++ b/source/blender/editors/space_outliner/outliner_tree.c
@@ -490,7 +490,7 @@ static void outliner_add_object_contents(SpaceOops *soops, TreeElement *te, Tree
outliner_add_element(soops, &te->subtree, ob->poselib, te, 0, 0); // XXX FIXME.. add a special type for this
- if (ob->proxy && ob->id.lib == NULL)
+ if (ob->proxy && !ID_IS_LINKED_DATABLOCK(ob))
outliner_add_element(soops, &te->subtree, ob->proxy, te, TSE_PROXY, 0);
outliner_add_element(soops, &te->subtree, ob->gpd, te, 0, 0);
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index c11a4d2e5a3..ede6b7ce469 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -1071,7 +1071,7 @@ static int sequencer_add_effect_strip_invoke(bContext *C, wmOperator *op, const
{
bool is_type_set = RNA_struct_property_is_set(op->ptr, "type");
int type = -1;
- int prop_flag = SEQPROP_ENDFRAME;
+ int prop_flag = SEQPROP_ENDFRAME | SEQPROP_NOPATHS;
if (is_type_set) {
type = RNA_enum_get(op->ptr, "type");
@@ -1106,9 +1106,6 @@ void SEQUENCER_OT_effect_strip_add(struct wmOperatorType *ot)
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- WM_operator_properties_filesel(
- ot, 0, FILE_SPECIAL, FILE_OPENFILE,
- WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
sequencer_generic_props__internal(ot, SEQPROP_STARTFRAME | SEQPROP_ENDFRAME);
RNA_def_enum(ot->srna, "type", sequencer_prop_effect_types, SEQ_TYPE_CROSS, "Type", "Sequencer effect type");
RNA_def_float_vector(ot->srna, "color", 3, NULL, 0.0f, 1.0f, "Color",
diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c
index 86d3fcbe1ac..b33a26db728 100644
--- a/source/blender/editors/space_sequencer/sequencer_buttons.c
+++ b/source/blender/editors/space_sequencer/sequencer_buttons.c
@@ -94,7 +94,7 @@ void SEQUENCER_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
ot->idname = "SEQUENCER_OT_properties";
- ot->description = "Open sequencer properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->exec = sequencer_properties_toggle_exec;
ot->poll = ED_operator_sequencer_active;
diff --git a/source/blender/editors/space_text/text_header.c b/source/blender/editors/space_text/text_header.c
index 91665f1a598..e06a5b5474e 100644
--- a/source/blender/editors/space_text/text_header.c
+++ b/source/blender/editors/space_text/text_header.c
@@ -92,7 +92,7 @@ void TEXT_OT_properties(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Properties";
- ot->description = "Toggle text properties panel";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "TEXT_OT_properties";
/* api callbacks */
diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c
index 94ed280f792..e7428fd79d9 100644
--- a/source/blender/editors/space_text/text_ops.c
+++ b/source/blender/editors/space_text/text_ops.c
@@ -92,7 +92,7 @@ static int text_edit_poll(bContext *C)
if (!text)
return 0;
- if (text->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(text)) {
// BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata");
return 0;
}
@@ -108,7 +108,7 @@ int text_space_edit_poll(bContext *C)
if (!st || !text)
return 0;
- if (text->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(text)) {
// BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata");
return 0;
}
@@ -128,7 +128,7 @@ static int text_region_edit_poll(bContext *C)
if (!ar || ar->regiontype != RGN_TYPE_WINDOW)
return 0;
- if (text->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(text)) {
// BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata");
return 0;
}
diff --git a/source/blender/editors/space_time/space_time.c b/source/blender/editors/space_time/space_time.c
index fde955c24a7..525d42a1965 100644
--- a/source/blender/editors/space_time/space_time.c
+++ b/source/blender/editors/space_time/space_time.c
@@ -585,6 +585,10 @@ static void time_main_region_listener(bScreen *UNUSED(sc), ScrArea *UNUSED(sa),
break;
}
break;
+ case NC_GPENCIL:
+ if (wmn->data == ND_DATA)
+ ED_region_tag_redraw(ar);
+ break;
}
}
diff --git a/source/blender/editors/space_view3d/drawmesh.c b/source/blender/editors/space_view3d/drawmesh.c
index 755d4985518..614289997c1 100644
--- a/source/blender/editors/space_view3d/drawmesh.c
+++ b/source/blender/editors/space_view3d/drawmesh.c
@@ -237,7 +237,9 @@ static struct TextureDrawState {
bool texpaint_material; /* use material slots for texture painting */
} Gtexdraw = {NULL, NULL, NULL, false, 0, 0, 0, false, false, {0, 0, 0, 0}, false, false};
-static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material *ma, struct TextureDrawState gtexdraw)
+static bool set_draw_settings_cached(
+ int clearcache, MTexPoly *texface, Material *ma,
+ const struct TextureDrawState *gtexdraw)
{
static Material *c_ma;
static int c_textured;
@@ -253,7 +255,7 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material
int lit = 0;
int has_texface = texface != NULL;
bool need_set_tpage = false;
- bool texpaint = ((gtexdraw.ob->mode & OB_MODE_TEXTURE_PAINT) != 0);
+ bool texpaint = ((gtexdraw->ob->mode & OB_MODE_TEXTURE_PAINT) != 0);
Image *ima = NULL;
@@ -271,16 +273,18 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material
c_ma = NULL;
}
else {
- textured = gtexdraw.is_tex;
+ textured = gtexdraw->is_tex;
}
/* convert number of lights into boolean */
- if (gtexdraw.is_lit) lit = 1;
+ if (gtexdraw->is_lit) {
+ lit = 1;
+ }
- backculled = gtexdraw.use_backface_culling;
+ backculled = gtexdraw->use_backface_culling;
if (ma) {
if (ma->mode & MA_SHLESS) lit = 0;
- if (gtexdraw.use_game_mat) {
+ if (gtexdraw->use_game_mat) {
backculled = backculled || (ma->game.flag & GEMAT_BACKCULL);
alphablend = ma->game.alpha_blend;
}
@@ -294,10 +298,10 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material
alphablend = GPU_BLEND_ALPHA;
}
else if (texpaint) {
- if (gtexdraw.texpaint_material)
+ if (gtexdraw->texpaint_material)
ima = ma && ma->texpaintslot ? ma->texpaintslot[ma->paint_active_slot].ima : NULL;
else
- ima = gtexdraw.canvas;
+ ima = gtexdraw->canvas;
}
else
textured = 0;
@@ -375,7 +379,7 @@ static bool set_draw_settings_cached(int clearcache, MTexPoly *texface, Material
if (c_textured && !c_badtex) {
options |= GPU_SHADER_TEXTURE_2D;
}
- if (gtexdraw.two_sided_lighting) {
+ if (gtexdraw->two_sided_lighting) {
options |= GPU_SHADER_TWO_SIDED;
}
@@ -431,12 +435,16 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O
else {
/* draw with lights in the scene otherwise */
solidtex = false;
- if (v3d->flag2 & V3D_SHADELESS_TEX)
+ if (v3d->flag2 & V3D_SHADELESS_TEX) {
Gtexdraw.is_lit = 0;
- else
- Gtexdraw.is_lit = GPU_scene_object_lights(scene, ob, v3d->lay, rv3d->viewmat, !rv3d->is_persp);
+ }
+ else {
+ Gtexdraw.is_lit = GPU_scene_object_lights(
+ scene, ob, v3d->localvd ? v3d->localvd->lay : v3d->lay,
+ rv3d->viewmat, !rv3d->is_persp);
+ }
}
-
+
rgba_float_to_uchar(obcol, ob->col);
if (solidtex || v3d->drawtype == OB_TEXTURE) is_tex = true;
@@ -495,7 +503,7 @@ static void draw_textured_begin(Scene *scene, View3D *v3d, RegionView3D *rv3d, O
Gtexdraw.two_sided_lighting = (me->flag & ME_TWOSIDED);
memcpy(Gtexdraw.obcol, obcol, sizeof(obcol));
- set_draw_settings_cached(1, NULL, NULL, Gtexdraw);
+ set_draw_settings_cached(1, NULL, NULL, &Gtexdraw);
glCullFace(GL_BACK);
}
@@ -553,7 +561,7 @@ static DMDrawOption draw_tface__set_draw_legacy(MTexPoly *mtexpoly, const bool h
if (ma && (ma->game.flag & GEMAT_INVISIBLE))
return DM_DRAW_OPTION_SKIP;
- invalidtexture = set_draw_settings_cached(0, mtexpoly, ma, Gtexdraw);
+ invalidtexture = set_draw_settings_cached(0, mtexpoly, ma, &Gtexdraw);
if (mtexpoly && invalidtexture) {
glColor3ub(0xFF, 0x00, 0xFF);
@@ -594,7 +602,7 @@ static DMDrawOption draw_tface__set_draw(MTexPoly *mtexpoly, const bool UNUSED(h
if (ma && (ma->game.flag & GEMAT_INVISIBLE)) return DM_DRAW_OPTION_SKIP;
if (mtexpoly || Gtexdraw.is_texpaint)
- set_draw_settings_cached(0, mtexpoly, ma, Gtexdraw);
+ set_draw_settings_cached(0, mtexpoly, ma, &Gtexdraw);
/* always use color from mcol, as set in update_tface_color_layer */
return DM_DRAW_OPTION_NORMAL;
@@ -664,7 +672,7 @@ static void update_tface_color_layer(DerivedMesh *dm, bool use_mcol)
copy_mode = COPY_PREV;
}
}
- else if (mtexpoly && set_draw_settings_cached(0, mtexpoly, ma, Gtexdraw)) {
+ else if (mtexpoly && set_draw_settings_cached(0, mtexpoly, ma, &Gtexdraw)) {
int loop_index = mp->loopstart;
for (j = 0; j < mp->totloop; j++, loop_index++) {
finalCol[loop_index].r = 255;
@@ -830,7 +838,7 @@ static void draw_mesh_text(Scene *scene, Object *ob, int glsl)
}
}
else {
- badtex = set_draw_settings_cached(0, mtpoly, mat, Gtexdraw);
+ badtex = set_draw_settings_cached(0, mtpoly, mat, &Gtexdraw);
if (badtex) {
continue;
}
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index acc20997b27..7d281a344f3 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -4671,7 +4671,7 @@ static bool drawDispList_nobackface(Scene *scene, View3D *v3d, RegionView3D *rv3
index3_nors_incr = true;
}
else {
- if (!render_only || (render_only && BKE_displist_has_faces(lb))) {
+ if (!render_only || BKE_displist_has_faces(lb)) {
return drawDispListwire(lb, ob->type);
}
}
@@ -7342,7 +7342,7 @@ static void draw_object_wire_color(Scene *scene, Base *base, unsigned char r_ob_
}
else {
/* Sets the 'colindex' */
- if (ob->id.lib) {
+ if (ID_IS_LINKED_DATABLOCK(ob)) {
colindex = (base->flag & (SELECT + BA_WAS_SEL)) ? 2 : 1;
}
/* Sets the 'theme_id' or fallback to wire */
@@ -8098,7 +8098,7 @@ void draw_object(Scene *scene, ARegion *ar, View3D *v3d, Base *base, const short
!(G.f & G_RENDER_OGL))
{
/* check > 0 otherwise grease pencil can draw into the circle select which is annoying. */
- drawcentercircle(v3d, rv3d, ob->obmat[3], do_draw_center, ob->id.lib || ob->id.us > 1);
+ drawcentercircle(v3d, rv3d, ob->obmat[3], do_draw_center, ID_IS_LINKED_DATABLOCK(ob) || ob->id.us > 1);
}
}
}
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index 0805b81147f..8139a407a43 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -1486,7 +1486,9 @@ static void view3d_id_remap(ScrArea *sa, SpaceLink *slink, ID *old_id, ID *new_i
if ((ID *)v3d->camera == old_id) {
v3d->camera = (Object *)new_id;
if (!new_id) {
- for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ /* 3D view might be inactive, in that case needs to use slink->regionbase */
+ ListBase *regionbase = (slink == sa->spacedata.first) ? &sa->regionbase : &slink->regionbase;
+ for (ar = regionbase->first; ar; ar = ar->next) {
if (ar->regiontype == RGN_TYPE_WINDOW) {
RegionView3D *rv3d = is_local ? ((RegionView3D *)ar->regiondata)->localvd : ar->regiondata;
if (rv3d && (rv3d->persp == RV3D_CAMOB)) {
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index c52d1238163..b9c8c98b62f 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1206,7 +1206,7 @@ static int view3d_properties_toggle_exec(bContext *C, wmOperator *UNUSED(op))
void VIEW3D_OT_properties(wmOperatorType *ot)
{
ot->name = "Properties";
- ot->description = "Toggles the properties panel display";
+ ot->description = "Toggle the properties region visibility";
ot->idname = "VIEW3D_OT_properties";
ot->exec = view3d_properties_toggle_exec;
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index 62059766bd6..6463c137c2b 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -2607,6 +2607,7 @@ static void gpu_update_lamps_shadows_world(Scene *scene, View3D *v3d)
GPU_mist_update_values(world->mistype, world->miststa, world->mistdist, world->misi, &world->horr);
GPU_horizon_update_color(&world->horr);
GPU_ambient_update_color(&world->ambr);
+ GPU_zenith_update_color(&world->zenr);
}
}
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 1bd0ec23d65..215d90a8878 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -110,7 +110,7 @@ static bool view3d_operator_offset_lock_check(bContext *C, wmOperator *op)
bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d)
{
return ((v3d->camera) &&
- (v3d->camera->id.lib == NULL) &&
+ (!ID_IS_LINKED_DATABLOCK(v3d->camera)) &&
(v3d->flag2 & V3D_LOCK_CAMERA) &&
(rv3d->persp == RV3D_CAMOB));
}
@@ -3079,7 +3079,9 @@ static int viewselected_exec(bContext *C, wmOperator *op)
else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
ok = PE_minmax(scene, min, max);
}
- else if (ob && (ob->mode & (OB_MODE_SCULPT | OB_MODE_TEXTURE_PAINT))) {
+ else if (ob &&
+ (ob->mode & (OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)))
+ {
BKE_paint_stroke_get_average(scene, ob, min);
copy_v3_v3(max, min);
ok = true;
@@ -3434,12 +3436,8 @@ static int render_border_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL);
}
- /* drawing a border surrounding the entire camera view switches off border rendering
- * or the border covers no pixels */
- if ((border.xmin <= 0.0f && border.xmax >= 1.0f &&
- border.ymin <= 0.0f && border.ymax >= 1.0f) ||
- (border.xmin == border.xmax || border.ymin == border.ymax))
- {
+ /* drawing a border outside the camera view switches off border rendering */
+ if ((border.xmin == border.xmax || border.ymin == border.ymax)) {
if (rv3d->persp == RV3D_CAMOB)
scene->r.mode &= ~R_BORDER;
else
@@ -4520,7 +4518,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
/* note: having key shortcut here is bad practice,
* but for now keep because this displays when dragging an image over the 3D viewport */
ot->name = "Add Background Image (Ctrl for Empty Object)";
- ot->description = "Add a new background image";
+ ot->description = "Add a new background image (Ctrl for Empty Object)";
ot->idname = "VIEW3D_OT_background_image_add";
/* api callbacks */
@@ -4529,7 +4527,7 @@ void VIEW3D_OT_background_image_add(wmOperatorType *ot)
ot->poll = ED_operator_view3d_active;
/* flags */
- ot->flag = 0;
+ ot->flag = OPTYPE_UNDO;
/* properties */
RNA_def_string(ot->srna, "name", "Image", MAX_ID_NAME - 2, "Name", "Image name to assign");
diff --git a/source/blender/editors/space_view3d/view3d_fly.c b/source/blender/editors/space_view3d/view3d_fly.c
index 8418cf0980c..31377d0fce8 100644
--- a/source/blender/editors/space_view3d/view3d_fly.c
+++ b/source/blender/editors/space_view3d/view3d_fly.c
@@ -346,7 +346,7 @@ static bool initFlyInfo(bContext *C, FlyInfo *fly, wmOperator *op, const wmEvent
fly->rv3d->persp = RV3D_PERSP;
}
- if (fly->rv3d->persp == RV3D_CAMOB && fly->v3d->camera->id.lib) {
+ if (fly->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED_DATABLOCK(fly->v3d->camera)) {
BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
return false;
}
diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h
index 6d831c667a8..bdd2702a6ce 100644
--- a/source/blender/editors/space_view3d/view3d_intern.h
+++ b/source/blender/editors/space_view3d/view3d_intern.h
@@ -274,6 +274,7 @@ bool ED_view3d_minmax_verts(struct Object *obedit, float min[3], float max[3]);
void VIEW3D_OT_snap_selected_to_grid(struct wmOperatorType *ot);
void VIEW3D_OT_snap_selected_to_cursor(struct wmOperatorType *ot);
+void VIEW3D_OT_snap_selected_to_active(struct wmOperatorType *ot);
void VIEW3D_OT_snap_cursor_to_grid(struct wmOperatorType *ot);
void VIEW3D_OT_snap_cursor_to_center(struct wmOperatorType *ot);
void VIEW3D_OT_snap_cursor_to_selected(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c
index 1c84ce3c985..b273f46fca3 100644
--- a/source/blender/editors/space_view3d/view3d_ops.c
+++ b/source/blender/editors/space_view3d/view3d_ops.c
@@ -214,6 +214,7 @@ void view3d_operatortypes(void)
WM_operatortype_append(VIEW3D_OT_snap_selected_to_grid);
WM_operatortype_append(VIEW3D_OT_snap_selected_to_cursor);
+ WM_operatortype_append(VIEW3D_OT_snap_selected_to_active);
WM_operatortype_append(VIEW3D_OT_snap_cursor_to_grid);
WM_operatortype_append(VIEW3D_OT_snap_cursor_to_center);
WM_operatortype_append(VIEW3D_OT_snap_cursor_to_selected);
diff --git a/source/blender/editors/space_view3d/view3d_project.c b/source/blender/editors/space_view3d/view3d_project.c
index ac05853e6d0..7448d4c658e 100644
--- a/source/blender/editors/space_view3d/view3d_project.c
+++ b/source/blender/editors/space_view3d/view3d_project.c
@@ -312,25 +312,9 @@ static void view3d_win_to_ray_segment(
if (!r_ray_co) r_ray_co = _ray_co;
if (!r_ray_dir) r_ray_dir = _ray_dir;
+ ED_view3d_win_to_origin(ar, mval, r_ray_co);
ED_view3d_win_to_vector(ar, mval, r_ray_dir);
- if (rv3d->is_persp) {
- copy_v3_v3(r_ray_co, rv3d->viewinv[3]);
- }
- else {
- r_ray_co[0] = 2.0f * mval[0] / ar->winx - 1.0f;
- r_ray_co[1] = 2.0f * mval[1] / ar->winy - 1.0f;
-
- if (rv3d->persp == RV3D_CAMOB) {
- r_ray_co[2] = -1.0f;
- }
- else {
- r_ray_co[2] = 0.0f;
- }
-
- mul_project_m4_v3(rv3d->persinv, r_ray_co);
- }
-
if ((rv3d->is_persp == false) && (rv3d->persp != RV3D_CAMOB)) {
end_offset = v3d->far / 2.0f;
start_offset = -end_offset;
@@ -347,7 +331,7 @@ static void view3d_win_to_ray_segment(
}
}
-BLI_INLINE bool view3d_clip_segment(const RegionView3D *rv3d, float ray_start[3], float ray_end[3])
+bool ED_view3d_clip_segment(const RegionView3D *rv3d, float ray_start[3], float ray_end[3])
{
if ((rv3d->rflag & RV3D_CLIPPING) &&
(clip_segment_v3_plane_n(ray_start, ray_end, rv3d->clip, 6,
@@ -384,7 +368,7 @@ bool ED_view3d_win_to_ray_ex(
/* bounds clipping */
if (do_clip) {
- return view3d_clip_segment(ar->regiondata, r_ray_start, ray_end);
+ return ED_view3d_clip_segment(ar->regiondata, r_ray_start, ray_end);
}
return true;
@@ -549,6 +533,37 @@ void ED_view3d_win_to_delta(const ARegion *ar, const float mval[2], float out[3]
}
/**
+ * Calculate a 3d origin from 2d window coordinates.
+ * \note Orthographic views have a less obvious origin,
+ * Since far clip can be a very large value resulting in numeric precision issues,
+ * the origin in this case is close to zero coordinate.
+ *
+ * \param ar The region (used for the window width and height).
+ * \param mval The area relative 2d location (such as event->mval converted to floats).
+ * \param out The resulting normalized world-space direction vector.
+ */
+void ED_view3d_win_to_origin(const ARegion *ar, const float mval[2], float out[3])
+{
+ RegionView3D *rv3d = ar->regiondata;
+ if (rv3d->is_persp) {
+ copy_v3_v3(out, rv3d->viewinv[3]);
+ }
+ else {
+ out[0] = 2.0f * mval[0] / ar->winx - 1.0f;
+ out[1] = 2.0f * mval[1] / ar->winy - 1.0f;
+
+ if (rv3d->persp == RV3D_CAMOB) {
+ out[2] = -1.0f;
+ }
+ else {
+ out[2] = 0.0f;
+ }
+
+ mul_project_m4_v3(rv3d->persinv, out);
+ }
+}
+
+/**
* Calculate a 3d direction vector from 2d window coordinates.
* This direction vector starts and the view in the direction of the 2d window coordinates.
* In orthographic view all window coordinates yield the same vector.
@@ -599,7 +614,7 @@ bool ED_view3d_win_to_segment(const ARegion *ar, View3D *v3d, const float mval[2
/* bounds clipping */
if (do_clip) {
- return view3d_clip_segment((RegionView3D *)ar->regiondata, r_ray_start, r_ray_end);
+ return ED_view3d_clip_segment((RegionView3D *)ar->regiondata, r_ray_start, r_ray_end);
}
return true;
diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c
index f46608b7d5e..a55849e5633 100644
--- a/source/blender/editors/space_view3d/view3d_ruler.c
+++ b/source/blender/editors/space_view3d/view3d_ruler.c
@@ -327,6 +327,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
for (j = 0; j < 3; j++) {
copy_v3_v3(&pt->x, ruler_item->co[j]);
pt->pressure = 1.0f;
+ pt->strength = 1.0f;
pt++;
}
}
@@ -336,6 +337,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info)
for (j = 0; j < 3; j += 2) {
copy_v3_v3(&pt->x, ruler_item->co[j]);
pt->pressure = 1.0f;
+ pt->strength = 1.0f;
pt++;
}
}
diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c
index e8e7d3c62fb..5dd69cc66eb 100644
--- a/source/blender/editors/space_view3d/view3d_snap.c
+++ b/source/blender/editors/space_view3d/view3d_snap.c
@@ -44,6 +44,7 @@
#include "BKE_main.h"
#include "BKE_mball.h"
#include "BKE_object.h"
+#include "BKE_report.h"
#include "BKE_tracking.h"
#include "WM_api.h"
@@ -204,7 +205,7 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot)
/* *************************************************** */
-static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
+static int snap_selected_to_location(bContext *C, const float snap_target_global[3], const bool use_offset)
{
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
@@ -213,15 +214,10 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
TransVertStore tvs = {NULL};
TransVert *tv;
float imat[3][3], bmat[3][3];
- const float *cursor_global;
float center_global[3];
float offset_global[3];
int a;
- const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
-
- cursor_global = ED_view3d_cursor3d_get(scene, v3d);
-
if (use_offset) {
if ((v3d && v3d->around == V3D_AROUND_ACTIVE) &&
snap_calc_active_center(C, true, center_global))
@@ -231,11 +227,11 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
else {
snap_curs_to_sel_ex(C, center_global);
}
- sub_v3_v3v3(offset_global, cursor_global, center_global);
+ sub_v3_v3v3(offset_global, snap_target_global, center_global);
}
if (obedit) {
- float cursor_local[3];
+ float snap_target_local[3];
if (ED_transverts_check_obedit(obedit))
ED_transverts_create_from_obedit(&tvs, obedit, 0);
@@ -246,8 +242,8 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
invert_m3_m3(imat, bmat);
/* get the cursor in object space */
- sub_v3_v3v3(cursor_local, cursor_global, obedit->obmat[3]);
- mul_m3_v3(imat, cursor_local);
+ sub_v3_v3v3(snap_target_local, snap_target_global, obedit->obmat[3]);
+ mul_m3_v3(imat, snap_target_local);
if (use_offset) {
float offset_local[3];
@@ -262,7 +258,7 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
else {
tv = tvs.transverts;
for (a = 0; a < tvs.transverts_tot; a++, tv++) {
- copy_v3_v3(tv->loc, cursor_local);
+ copy_v3_v3(tv->loc, snap_target_local);
}
}
@@ -274,10 +270,10 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
bPoseChannel *pchan;
bArmature *arm = obact->data;
- float cursor_local[3];
+ float snap_target_local[3];
invert_m4_m4(obact->imat, obact->obmat);
- mul_v3_m4v3(cursor_local, obact->imat, cursor_global);
+ mul_v3_m4v3(snap_target_local, obact->imat, snap_target_global);
for (pchan = obact->pose->chanbase.first; pchan; pchan = pchan->next) {
if ((pchan->bone->flag & BONE_SELECTED) &&
@@ -311,7 +307,7 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
BKE_armature_loc_pose_to_bone(pchan, cursor_pose, cursor_pose);
}
else {
- BKE_armature_loc_pose_to_bone(pchan, cursor_local, cursor_pose);
+ BKE_armature_loc_pose_to_bone(pchan, snap_target_local, cursor_pose);
}
/* copy new position */
@@ -367,7 +363,7 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
add_v3_v3v3(cursor_parent, ob->obmat[3], offset_global);
}
else {
- copy_v3_v3(cursor_parent, cursor_global);
+ copy_v3_v3(cursor_parent, snap_target_global);
}
sub_v3_v3(cursor_parent, ob->obmat[3]);
@@ -401,6 +397,18 @@ static int snap_sel_to_curs_exec(bContext *C, wmOperator *op)
return OPERATOR_FINISHED;
}
+static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op)
+{
+ const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
+
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+
+ const float *snap_target_global = ED_view3d_cursor3d_get(scene, v3d);
+
+ return snap_selected_to_location(C, snap_target_global, use_offset);
+}
+
void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot)
{
/* identifiers */
@@ -409,7 +417,7 @@ void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot)
ot->idname = "VIEW3D_OT_snap_selected_to_cursor";
/* api callbacks */
- ot->exec = snap_sel_to_curs_exec;
+ ot->exec = snap_selected_to_cursor_exec;
ot->poll = ED_operator_view3d_active;
/* flags */
@@ -419,6 +427,34 @@ void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "use_offset", 1, "Offset", "");
}
+static int snap_selected_to_active_exec(bContext *C, wmOperator *op)
+{
+ float snap_target_global[3];
+
+ if (snap_calc_active_center(C, false, snap_target_global) == false) {
+ BKE_report(op->reports, RPT_ERROR, "No active element found!");
+ return OPERATOR_CANCELLED;
+ }
+
+ return snap_selected_to_location(C, snap_target_global, false);
+}
+
+void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Snap Selection to Active";
+ ot->description = "Snap selected item(s) to the active item";
+ ot->idname = "VIEW3D_OT_snap_selected_to_active";
+
+ /* api callbacks */
+ ot->exec = snap_selected_to_active_exec;
+ ot->poll = ED_operator_view3d_active;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+
/* *************************************************** */
static int snap_curs_to_grid_exec(bContext *C, wmOperator *UNUSED(op))
diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c
index c35646b9e92..bc7a9989c72 100644
--- a/source/blender/editors/space_view3d/view3d_view.c
+++ b/source/blender/editors/space_view3d/view3d_view.c
@@ -221,7 +221,7 @@ void ED_view3d_smooth_view_ex(
copy_v3_v3(sms.dyn_ofs, sview->dyn_ofs);
sms.use_dyn_ofs = true;
- /* calcualte the final destination offset */
+ /* calculate the final destination offset */
view3d_orbit_apply_dyn_ofs(sms.dst.ofs, sms.src.ofs, sms.src.quat, sms.dst.quat, sms.dyn_ofs);
}
@@ -507,7 +507,7 @@ static int view3d_camera_to_view_poll(bContext *C)
if (ED_view3d_context_user_region(C, &v3d, &ar)) {
RegionView3D *rv3d = ar->regiondata;
- if (v3d && v3d->camera && v3d->camera->id.lib == NULL) {
+ if (v3d && v3d->camera && !ID_IS_LINKED_DATABLOCK(v3d->camera)) {
if (rv3d && (rv3d->viewlock & RV3D_LOCKED) == 0) {
if (rv3d->persp != RV3D_CAMOB) {
return 1;
diff --git a/source/blender/editors/space_view3d/view3d_walk.c b/source/blender/editors/space_view3d/view3d_walk.c
index 384da277612..c9e4bb301b8 100644
--- a/source/blender/editors/space_view3d/view3d_walk.c
+++ b/source/blender/editors/space_view3d/view3d_walk.c
@@ -505,7 +505,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op)
walk->rv3d->persp = RV3D_PERSP;
}
- if (walk->rv3d->persp == RV3D_CAMOB && walk->v3d->camera->id.lib) {
+ if (walk->rv3d->persp == RV3D_CAMOB && ID_IS_LINKED_DATABLOCK(walk->v3d->camera)) {
BKE_report(op->reports, RPT_ERROR, "Cannot navigate a camera from an external library");
return false;
}
@@ -924,8 +924,7 @@ static void walkEvent(bContext *C, wmOperator *op, WalkInfo *walk, const wmEvent
copy_v3_v3(teleport->origin, walk->rv3d->viewinv[3]);
/* stop the camera from a distance (camera height) */
- normalize_v3(nor);
- mul_v3_fl(nor, walk->view_height);
+ normalize_v3_length(nor, walk->view_height);
add_v3_v3(loc, nor);
sub_v3_v3v3(teleport->direction, loc, teleport->origin);
diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c
index 95864474fc0..ef6cff19181 100644
--- a/source/blender/editors/transform/transform.c
+++ b/source/blender/editors/transform/transform.c
@@ -51,6 +51,7 @@
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_ghash.h"
+#include "BLI_stackdefines.h"
#include "BLI_memarena.h"
#include "BKE_nla.h"
@@ -100,7 +101,6 @@ static void doVertSlide(TransInfo *t, float perc);
static void drawEdgeSlide(TransInfo *t);
static void drawVertSlide(TransInfo *t);
-static void len_v3_ensure(float v[3], const float length);
static void postInputRotation(TransInfo *t, float values[3]);
static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around);
@@ -3720,19 +3720,10 @@ static void initRotation(TransInfo *t)
copy_v3_v3(t->axis_orig, t->axis);
}
-static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short around)
+static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], const float *center)
{
float vec[3], totmat[3][3], smat[3][3];
float eul[3], fmat[3][3], quat[4];
- const float *center;
-
- /* local constraint shouldn't alter center */
- if (transdata_check_local_center(t, around)) {
- center = td->center;
- }
- else {
- center = t->center;
- }
if (t->flag & T_POINTS) {
mul_m3_m3m3(totmat, mat, td->mtx);
@@ -3942,6 +3933,21 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], short
}
}
+static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around)
+{
+ const float *center;
+
+ /* local constraint shouldn't alter center */
+ if (transdata_check_local_center(t, around)) {
+ center = td->center;
+ }
+ else {
+ center = t->center;
+ }
+
+ ElementRotation_ex(t, td, mat, center);
+}
+
static void applyRotationValue(TransInfo *t, float angle, float axis[3])
{
TransData *td = t->data;
@@ -3991,10 +3997,8 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2]))
applySnapping(t, &final);
- if (applyNumInput(&t->num, &final)) {
- /* Clamp between -PI and PI */
- final = angle_wrap_rad(final);
- }
+ /* Used to clamp final result in [-PI, PI[ range, no idea why, inheritance from 2.4x area, see T48998. */
+ applyNumInput(&t->num, &final);
t->values[0] = final;
@@ -4342,12 +4346,16 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
if (td->flag & TD_SKIP)
continue;
+ float rotate_offset[3] = {0};
+ bool use_rotate_offset = false;
+
/* handle snapping rotation before doing the translation */
if (usingSnappingNormal(t)) {
+ float mat[3][3];
+
if (validSnappingNormal(t)) {
const float *original_normal;
- float mat[3][3];
-
+
/* In pose mode, we want to align normals with Y axis of bones... */
if (t->flag & T_POSE)
original_normal = td->axismtx[1];
@@ -4355,18 +4363,19 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
original_normal = td->axismtx[2];
rotation_between_vecs_to_mat3(mat, original_normal, t->tsnap.snapNormal);
-
- ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS);
}
else {
- float mat[3][3];
-
unit_m3(mat);
-
- ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS);
+ }
+
+ ElementRotation_ex(t, td, mat, t->tsnap.snapTarget);
+
+ if (td->loc) {
+ use_rotate_offset = true;
+ sub_v3_v3v3(rotate_offset, td->loc, td->iloc);
}
}
-
+
if (t->con.applyVec) {
float pvec[3];
t->con.applyVec(t, td, vec, tvec, pvec);
@@ -4374,6 +4383,10 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
else {
copy_v3_v3(tvec, vec);
}
+
+ if (use_rotate_offset) {
+ add_v3_v3(tvec, rotate_offset);
+ }
mul_m3_v3(td->smtx, tvec);
mul_v3_fl(tvec, td->factor);
@@ -4382,7 +4395,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3])
if (td->loc)
add_v3_v3v3(td->loc, td->iloc, tvec);
-
+
constraintTransLim(t, td);
}
}
@@ -4957,9 +4970,7 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2]))
project_v3_v3v3(vec, vec, axis);
}
}
- normalize_v3(vec);
- mul_v3_fl(vec, distance);
- mul_v3_fl(vec, td->factor);
+ normalize_v3_length(vec, distance * td->factor);
add_v3_v3v3(td->loc, td->iloc, vec);
}
@@ -5753,13 +5764,6 @@ static void interp_line_v3_v3v3v3(float p[3], const float v1[3], const float v2[
}
}
-
-static void len_v3_ensure(float v[3], const float length)
-{
- normalize_v3(v);
- mul_v3_fl(v, length);
-}
-
/**
* Find the closest point on the ngon on the opposite side.
* used to set the edge slide distance for ngons.
@@ -5823,7 +5827,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
if (l->e == e_next) {
if (i) {
- len_v3_ensure(vec_accum, vec_accum_len / (float)i);
+ normalize_v3_length(vec_accum, vec_accum_len / (float)i);
}
else {
/* When there is no edge to slide along,
@@ -5843,7 +5847,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
cross_v3_v3v3(vec_accum, l_tmp->f->no, tdir);
#if 0
/* rough guess, we can do better! */
- len_v3_ensure(vec_accum, (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f);
+ normalize_v3_length(vec_accum, (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f);
#else
/* be clever, check the opposite ngon edge to slide into.
* this gives best results */
@@ -5858,7 +5862,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
dist = (BM_edge_calc_length(e_prev) + BM_edge_calc_length(e_next)) / 2.0f;
}
- len_v3_ensure(vec_accum, dist);
+ normalize_v3_length(vec_accum, dist);
}
#endif
}
@@ -5879,7 +5883,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
if (BM_loop_other_edge_loop(l, v)->e == e_next) {
if (i) {
- len_v3_ensure(vec_accum, vec_accum_len / (float)i);
+ normalize_v3_length(vec_accum, vec_accum_len / (float)i);
}
copy_v3_v3(r_slide_vec, vec_accum);
@@ -5890,7 +5894,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
((l = l->radial_next) != l_first));
if (i) {
- len_v3_ensure(vec_accum, vec_accum_len / (float)i);
+ normalize_v3_length(vec_accum, vec_accum_len / (float)i);
}
copy_v3_v3(r_slide_vec, vec_accum);
@@ -5903,7 +5907,7 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l,
*/
static void calcEdgeSlide_mval_range(
TransInfo *t, EdgeSlideData *sld, const int *sv_table, const int loop_nr,
- const float mval[2], const bool use_btree_disp, const bool use_calc_direction)
+ const float mval[2], const bool use_occlude_geometry, const bool use_calc_direction)
{
TransDataEdgeSlideVert *sv_array = sld->sv;
BMEditMesh *em = BKE_editmesh_from_object(t->obedit);
@@ -5912,7 +5916,7 @@ static void calcEdgeSlide_mval_range(
View3D *v3d = NULL;
RegionView3D *rv3d = NULL;
float projectMat[4][4];
- BMBVHTree *btree;
+ BMBVHTree *bmbvh;
/* only for use_calc_direction */
float (*loop_dir)[3] = NULL, *loop_maxdist = NULL;
@@ -5936,11 +5940,11 @@ static void calcEdgeSlide_mval_range(
ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat);
}
- if (use_btree_disp) {
- btree = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false);
+ if (use_occlude_geometry) {
+ bmbvh = BKE_bmbvh_new_from_editmesh(em, BMBVH_RESPECT_HIDDEN, NULL, false);
}
else {
- btree = NULL;
+ bmbvh = NULL;
}
/* find mouse vectors, the global one, and one per loop in case we have
@@ -5975,7 +5979,7 @@ static void calcEdgeSlide_mval_range(
continue;
/* This test is only relevant if object is not wire-drawn! See [#32068]. */
- if (use_btree_disp && !BMBVH_EdgeVisible(btree, e_other, ar, v3d, t->obedit)) {
+ if (use_occlude_geometry && !BMBVH_EdgeVisible(bmbvh, e_other, ar, v3d, t->obedit)) {
continue;
}
@@ -6056,8 +6060,8 @@ static void calcEdgeSlide_mval_range(
sld->mval_end[0] = t->mval[0] + mval_end[0];
sld->mval_end[1] = t->mval[1] + mval_end[1];
- if (btree) {
- BKE_bmbvh_free(btree);
+ if (bmbvh) {
+ BKE_bmbvh_free(bmbvh);
}
}
@@ -6119,8 +6123,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
int *sv_table; /* BMVert -> sv_array index */
EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
- int numsel, i, j, loop_nr;
- bool use_btree_disp = false;
+ int numsel, i, loop_nr;
+ bool use_occlude_geometry = false;
View3D *v3d = NULL;
RegionView3D *rv3d = NULL;
@@ -6167,31 +6171,39 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
sv_table = MEM_mallocN(sizeof(*sv_table) * bm->totvert, __func__);
- j = 0;
- BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
- if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
- BM_elem_flag_enable(v, BM_ELEM_TAG);
- sv_table[i] = j;
- j += 1;
- }
- else {
- BM_elem_flag_disable(v, BM_ELEM_TAG);
- sv_table[i] = -1;
+#define INDEX_UNSET -1
+#define INDEX_INVALID -2
+
+ {
+ int j = 0;
+ BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
+ if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+ BM_elem_flag_enable(v, BM_ELEM_TAG);
+ sv_table[i] = INDEX_UNSET;
+ j += 1;
+ }
+ else {
+ BM_elem_flag_disable(v, BM_ELEM_TAG);
+ sv_table[i] = INDEX_INVALID;
+ }
+ BM_elem_index_set(v, i); /* set_inline */
}
- BM_elem_index_set(v, i); /* set_inline */
- }
- bm->elem_index_dirty &= ~BM_VERT;
+ bm->elem_index_dirty &= ~BM_VERT;
- if (!j) {
- MEM_freeN(sld);
- MEM_freeN(sv_table);
- return false;
+ if (!j) {
+ MEM_freeN(sld);
+ MEM_freeN(sv_table);
+ return false;
+ }
+ sv_tot = j;
}
- sv_tot = j;
sv_array = MEM_callocN(sizeof(TransDataEdgeSlideVert) * sv_tot, "sv_array");
loop_nr = 0;
+ STACK_DECLARE(sv_array);
+ STACK_INIT(sv_array, sv_tot);
+
while (1) {
float vec_a[3], vec_b[3];
BMLoop *l_a, *l_b;
@@ -6284,6 +6296,11 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
l_a_prev = NULL;
l_b_prev = NULL;
+#define SV_FROM_VERT(v) ( \
+ (sv_table[BM_elem_index_get(v)] == INDEX_UNSET) ? \
+ ((void)(sv_table[BM_elem_index_get(v)] = STACK_SIZE(sv_array)), STACK_PUSH_RET_PTR(sv_array)) : \
+ (&sv_array[sv_table[BM_elem_index_get(v)]]))
+
/*iterate over the loop*/
v_first = v;
do {
@@ -6295,8 +6312,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
/* XXX, 'sv' will initialize multiple times, this is suspicious. see [#34024] */
BLI_assert(v != NULL);
- BLI_assert(sv_table[BM_elem_index_get(v)] != -1);
- sv = &sv_array[sv_table[BM_elem_index_get(v)]];
+ BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
+ sv = SV_FROM_VERT(v);
sv->v = v;
copy_v3_v3(sv->v_co_orig, v->co);
sv->loop_nr = loop_nr;
@@ -6321,8 +6338,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
if (!e) {
BLI_assert(v != NULL);
- BLI_assert(sv_table[BM_elem_index_get(v)] != -1);
- sv = &sv_array[sv_table[BM_elem_index_get(v)]];
+
+ BLI_assert(sv_table[BM_elem_index_get(v)] != INDEX_INVALID);
+ sv = SV_FROM_VERT(v);
+
sv->v = v;
copy_v3_v3(sv->v_co_orig, v->co);
sv->loop_nr = loop_nr;
@@ -6411,6 +6430,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
BM_elem_flag_disable(v_prev, BM_ELEM_TAG);
} while ((e != v_first->e) && (l_a || l_b));
+#undef SV_FROM_VERT
+#undef INDEX_UNSET
+#undef INDEX_INVALID
+
loop_nr++;
#undef EDGESLIDE_VERT_IS_INNER
@@ -6418,6 +6441,8 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
/* EDBM_flag_disable_all(em, BM_ELEM_SELECT); */
+ BLI_assert(STACK_SIZE(sv_array) == sv_tot);
+
sld->sv = sv_array;
sld->totsv = sv_tot;
@@ -6425,10 +6450,10 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f
if (t->spacetype == SPACE_VIEW3D) {
v3d = t->sa ? t->sa->spacedata.first : NULL;
rv3d = t->ar ? t->ar->regiondata : NULL;
- use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
+ use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
}
- calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, true);
+ calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, true);
/* create copies of faces for customdata projection */
bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
@@ -6466,7 +6491,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f
EdgeSlideData *sld = MEM_callocN(sizeof(*sld), "sld");
float mval[2] = {(float)t->mval[0], (float)t->mval[1]};
int loop_nr;
- bool use_btree_disp = false;
+ bool use_occlude_geometry = false;
View3D *v3d = NULL;
RegionView3D *rv3d = NULL;
@@ -6513,6 +6538,7 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f
bm->elem_index_dirty &= ~BM_VERT;
if (!j) {
+ MEM_freeN(sld);
return false;
}
@@ -6627,10 +6653,10 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f
if (t->spacetype == SPACE_VIEW3D) {
v3d = t->sa ? t->sa->spacedata.first : NULL;
rv3d = t->ar ? t->ar->regiondata : NULL;
- use_btree_disp = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
+ use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE);
}
- calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_btree_disp, false);
+ calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, false);
/* create copies of faces for customdata projection */
bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES);
diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h
index 50168e78dda..a59f9dc43dd 100644
--- a/source/blender/editors/transform/transform.h
+++ b/source/blender/editors/transform/transform.h
@@ -87,7 +87,6 @@ typedef struct TransSnap {
float snapPoint[3]; /* snapping from this point */
float snapTarget[3]; /* to this point */
float snapNormal[3];
- float snapTangent[3];
char snapNodeBorder;
ListBase points;
TransSnapPoint *selectedPoint;
diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c
index 13cc0c22778..d7b670b6476 100644
--- a/source/blender/editors/transform/transform_constraints.c
+++ b/source/blender/editors/transform/transform_constraints.c
@@ -187,9 +187,7 @@ static void viewAxisCorrectCenter(TransInfo *t, float t_con_center[3])
if (l < min_dist) {
float diff[3];
- normalize_v3_v3(diff, t->viewinv[2]);
- mul_v3_fl(diff, min_dist - l);
-
+ normalize_v3_v3_length(diff, t->viewinv[2], min_dist - l);
sub_v3_v3(t_con_center, diff);
}
}
@@ -225,9 +223,8 @@ static void axisProjection(TransInfo *t, const float axis[3], const float in[3],
if (factor < 0.0f) factor *= -factor;
else factor *= factor;
- copy_v3_v3(out, axis);
- normalize_v3(out);
- mul_v3_fl(out, -factor); /* -factor makes move down going backwards */
+ /* -factor makes move down going backwards */
+ normalize_v3_v3_length(out, axis, -factor);
}
else {
float v[3], i1[3], i2[3];
diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c
index 707c60f8701..ad2b40bfef8 100644
--- a/source/blender/editors/transform/transform_conversions.c
+++ b/source/blender/editors/transform/transform_conversions.c
@@ -701,7 +701,6 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
bPoseChannel *pchan;
Bone *bone;
int mode = *out_mode;
- int hastranslation = 0;
int total = 0;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
@@ -729,7 +728,7 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
}
}
/* now count, and check if we have autoIK or have to switch from translate to rotate */
- hastranslation = 0;
+ bool has_translation = false, has_rotation = false;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
bone = pchan->bone;
@@ -740,20 +739,29 @@ int count_set_pose_transflags(int *out_mode, short around, Object *ob)
if (has_targetless_ik(pchan) == NULL) {
if (pchan->parent && (pchan->bone->flag & BONE_CONNECTED)) {
if (pchan->bone->flag & BONE_HINGE_CHILD_TRANSFORM)
- hastranslation = 1;
+ has_translation = true;
}
- else if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC)
- hastranslation = 1;
+ else {
+ if ((pchan->protectflag & OB_LOCK_LOC) != OB_LOCK_LOC)
+ has_translation = true;
+ }
+ if ((pchan->protectflag & OB_LOCK_ROT) != OB_LOCK_ROT)
+ has_rotation = true;
}
else
- hastranslation = 1;
+ has_translation = true;
}
}
}
/* if there are no translatable bones, do rotation */
- if (mode == TFM_TRANSLATION && !hastranslation) {
- *out_mode = TFM_ROTATION;
+ if (mode == TFM_TRANSLATION && !has_translation) {
+ if (has_rotation) {
+ *out_mode = TFM_ROTATION;
+ }
+ else {
+ *out_mode = TFM_RESIZE;
+ }
}
return total;
@@ -5273,13 +5281,9 @@ static void ObjectToTransData(TransInfo *t, TransData *td, Object *ob)
skip_invert = true;
if (skip_invert == false && constinv == false) {
- if (constinv == false)
- ob->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc_time checks this */
-
+ ob->transflag |= OB_NO_CONSTRAINTS; /* BKE_object_where_is_calc_time checks this */
BKE_object_where_is_calc(t->scene, ob);
-
- if (constinv == false)
- ob->transflag &= ~OB_NO_CONSTRAINTS;
+ ob->transflag &= ~OB_NO_CONSTRAINTS;
}
else
BKE_object_where_is_calc(t->scene, ob);
@@ -6429,7 +6433,7 @@ static void createTransObject(bContext *C, TransInfo *t)
}
/* select linked objects, but skip them later */
- if (ob->id.lib != NULL) {
+ if (ID_IS_LINKED_DATABLOCK(ob)) {
td->flag |= TD_SKIP;
}
@@ -7683,7 +7687,11 @@ static void createTransGPencil(bContext *C, TransInfo *t)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
-
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
+
if (is_prop_edit) {
/* Proportional Editing... */
if (is_prop_edit_connected) {
@@ -7731,14 +7739,27 @@ static void createTransGPencil(bContext *C, TransInfo *t)
if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
bGPDframe *gpf = gpl->actframe;
bGPDstroke *gps;
+ float diff_mat[4][4];
+ float inverse_diff_mat[4][4];
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ /* undo matrix */
+ invert_m4_m4(inverse_diff_mat, diff_mat);
+ }
- /* Make a new frame to work on if the layer's frame and the current scene frame don't match up
+ /* Make a new frame to work on if the layer's frame and the current scene frame don't match up
* - This is useful when animating as it saves that "uh-oh" moment when you realize you've
* spent too much time editing the wrong frame...
*/
// XXX: should this be allowed when framelock is enabled?
if (gpf->framenum != cfra) {
gpf = gpencil_frame_addcopy(gpl, cfra);
+ /* in some weird situations (framelock enabled) return NULL */
+ if (gpf == NULL) {
+ continue;
+ }
}
/* Loop over strokes, adding TransData for points as needed... */
@@ -7751,7 +7772,10 @@ static void createTransGPencil(bContext *C, TransInfo *t)
if (ED_gpencil_stroke_can_use(C, gps) == false) {
continue;
}
-
+ /* check if the color is editable */
+ if (ED_gpencil_stroke_color_use(gpl, gps) == false) {
+ continue;
+ }
/* What we need to include depends on proportional editing settings... */
if (is_prop_edit) {
if (is_prop_edit_connected) {
@@ -7820,9 +7844,18 @@ static void createTransGPencil(bContext *C, TransInfo *t)
/* screenspace */
td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
- copy_m3_m4(td->smtx, t->persmat);
- copy_m3_m4(td->mtx, t->persinv);
- unit_m3(td->axismtx);
+ /* apply parent transformations */
+ if (gpl->parent == NULL) {
+ copy_m3_m4(td->smtx, t->persmat);
+ copy_m3_m4(td->mtx, t->persinv);
+ unit_m3(td->axismtx);
+ }
+ else {
+ /* apply matrix transformation relative to parent */
+ copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */
+ copy_m3_m4(td->mtx, diff_mat); /* display position */
+ copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */
+ }
}
else {
/* configure 2D dataspace points so that they don't play up... */
@@ -7831,9 +7864,18 @@ static void createTransGPencil(bContext *C, TransInfo *t)
// XXX: matrices may need to be different?
}
- copy_m3_m3(td->smtx, smtx);
- copy_m3_m3(td->mtx, mtx);
- unit_m3(td->axismtx); // XXX?
+ /* apply parent transformations */
+ if (gpl->parent == NULL) {
+ copy_m3_m3(td->smtx, smtx);
+ copy_m3_m3(td->mtx, mtx);
+ unit_m3(td->axismtx); // XXX?
+ }
+ else {
+ /* apply matrix transformation relative to parent */
+ copy_m3_m4(td->smtx, inverse_diff_mat); /* final position */
+ copy_m3_m4(td->mtx, diff_mat); /* display position */
+ copy_m3_m4(td->axismtx, diff_mat); /* axis orientation */
+ }
}
/* Triangulation must be calculated again, so save the stroke for recalc function */
td->extra = gps;
diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c
index 67740644afe..f78a23be7b8 100644
--- a/source/blender/editors/transform/transform_generics.c
+++ b/source/blender/editors/transform/transform_generics.c
@@ -563,7 +563,7 @@ static void recalcData_nla(TransInfo *t)
}
/* Use RNA to write the values to ensure that constraints on these are obeyed
- * (e.g. for transition strips, the values are taken from the neighbours)
+ * (e.g. for transition strips, the values are taken from the neighbors)
*
* NOTE: we write these twice to avoid truncation errors which can arise when
* moving the strips a large distance using numeric input [#33852]
@@ -975,7 +975,9 @@ static void recalcData_gpencil_strokes(TransInfo *t)
TransData *td = t->data;
for (int i = 0; i < t->total; i++, td++) {
bGPDstroke *gps = td->extra;
- gps->flag |= GP_STROKE_RECALC_CACHES;
+ if (gps != NULL) {
+ gps->flag |= GP_STROKE_RECALC_CACHES;
+ }
}
}
diff --git a/source/blender/editors/transform/transform_manipulator.c b/source/blender/editors/transform/transform_manipulator.c
index 992fde0cce3..075f311db72 100644
--- a/source/blender/editors/transform/transform_manipulator.c
+++ b/source/blender/editors/transform/transform_manipulator.c
@@ -58,6 +58,7 @@
#include "BKE_pointcache.h"
#include "BKE_editmesh.h"
#include "BKE_lattice.h"
+#include "BKE_gpencil.h"
#include "BIF_gl.h"
@@ -68,6 +69,7 @@
#include "ED_curve.h"
#include "ED_particle.h"
#include "ED_view3d.h"
+#include "ED_gpencil.h"
#include "UI_resources.h"
@@ -288,24 +290,49 @@ static int calc_manipulator_stats(const bContext *C)
zero_v3(scene->twcent);
if (is_gp_edit) {
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- /* we're only interested in selected points here... */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
-
- /* Change selection status of all points, then make the stroke match */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- calc_tw_center(scene, &pt->x);
- totsel++;
+ float diff_mat[4][4];
+ float fpt[3];
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+
+ /* calculate difference matrix if parent object */
+ if (gpl->parent != NULL) {
+ ED_gpencil_parent_location(gpl, diff_mat);
+ }
+
+ for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) {
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false) {
+ continue;
+ }
+
+ /* we're only interested in selected points here... */
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+
+ /* Change selection status of all points, then make the stroke match */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (gpl->parent == NULL) {
+ calc_tw_center(scene, &pt->x);
+ totsel++;
+ }
+ else {
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+ calc_tw_center(scene, fpt);
+ totsel++;
+ }
+ }
+ }
}
}
}
}
- CTX_DATA_END;
-
+
+
/* selection center */
if (totsel) {
mul_v3_fl(scene->twcent, 1.0f / (float)totsel); /* centroid! */
@@ -995,8 +1022,7 @@ static void draw_manipulator_rotate(
vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]);
vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]);
vec[2] = 0.0f;
- normalize_v3(vec);
- mul_v3_fl(vec, 1.2f * size);
+ normalize_v3_length(vec, 1.2f * size);
glBegin(GL_LINES);
glVertex3f(0.0f, 0.0f, 0.0f);
glVertex3fv(vec);
@@ -1516,8 +1542,7 @@ static void draw_manipulator_rotate_cyl(
vec[0] = 0; // XXX (float)(t->mouse.imval[0] - t->center2d[0]);
vec[1] = 0; // XXX (float)(t->mouse.imval[1] - t->center2d[1]);
vec[2] = 0.0f;
- normalize_v3(vec);
- mul_v3_fl(vec, 1.2f * size);
+ normalize_v3_length(vec, 1.2f * size);
glBegin(GL_LINES);
glVertex3f(0.0, 0.0, 0.0);
glVertex3fv(vec);
diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c
index 0bb64315845..f8bb124e943 100644
--- a/source/blender/editors/transform/transform_snap.c
+++ b/source/blender/editors/transform/transform_snap.c
@@ -984,15 +984,6 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec))
}
if (found == true) {
- float tangent[3];
-
- sub_v2_v2v2(tangent, loc, t->tsnap.snapPoint);
- tangent[2] = 0.0f;
-
- if (!is_zero_v3(tangent)) {
- copy_v3_v3(t->tsnap.snapTangent, tangent);
- }
-
copy_v3_v3(t->tsnap.snapPoint, loc);
copy_v3_v3(t->tsnap.snapNormal, no);
diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c
index e85b686b5b3..1d4872cca7a 100644
--- a/source/blender/editors/transform/transform_snap_object.c
+++ b/source/blender/editors/transform/transform_snap_object.c
@@ -110,6 +110,12 @@ struct SnapObjectContext {
};
+enum eViewProj {
+ VIEW_PROJ_NONE = -1,
+ VIEW_PROJ_ORTHO = 0,
+ VIEW_PROJ_PERSP = -1,
+};
+
static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt);
@@ -137,10 +143,6 @@ struct RayCastAll_Data {
Object *ob;
unsigned int ob_uuid;
- /* DerivedMesh only */
- DerivedMesh *dm;
- const struct MLoopTri *dm_looptri;
-
/* output data */
ListBase *hit_list;
bool retval;
@@ -218,197 +220,557 @@ static void raycast_all_cb(void *userdata, int index, const BVHTreeRay *ray, BVH
/* -------------------------------------------------------------------- */
-/** \name Internal Object Snapping API
+/** \Common utilities
* \{ */
-static bool snapEdge(
- const ARegion *ar, const float v1co[3], const short v1no[3], const float v2co[3], const short v2no[3],
- float obmat[4][4], float timat[3][3], const float mval_fl[2],
- const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3],
- /* read/write args */
- float *ray_depth, float *dist_px,
- /* return args */
- float r_loc[3], float r_no[3])
-{
- float intersect[3] = {0, 0, 0}, ray_end[3], dvec[3];
- int result;
- bool retval = false;
- copy_v3_v3(ray_end, ray_normal_local);
- mul_v3_fl(ray_end, 2000);
- add_v3_v3v3(ray_end, ray_start_local, ray_end);
+/**
+ * Struct that kepts basic information about a BVHTree build from a editmesh.
+ */
+typedef struct BVHTreeFromMeshType {
+ void *userdata;
+ char type;
+} BVHTreeFromMeshType;
+
+typedef struct PreDefProject {
+ float pmat[4][4]; /* perspective matrix multiplied by object matrix */
+ float win_half[2];
+ float dist_px_sq;
+} PreDefProject;
+
+static void precalc_project(
+ PreDefProject *projectdefs, const ARegion *ar,
+ const float dist_px, float obmat[4][4])
+{
+ float (*pmat)[4] = ((RegionView3D *)ar->regiondata)->persmat;
+ if (obmat) {
+ mul_m4_m4m4(projectdefs->pmat, pmat, obmat);
+ }
+ else {
+ copy_m4_m4(projectdefs->pmat, pmat);
+ }
+ projectdefs->win_half[0] = ar->winx / 2;
+ projectdefs->win_half[1] = ar->winy / 2;
+ projectdefs->dist_px_sq = SQUARE(dist_px);
+}
- /* dvec used but we don't care about result */
- result = isect_line_line_v3(v1co, v2co, ray_start_local, ray_end, intersect, dvec);
+/**
+ * From a threshold (maximum distance to snap in pixels) returns:
+ *
+ * - The *real* distance (3D) if you are in orthographic-view.
+ * - The *tangent* (view cone radius at distance 1.0) if you are in perspective-view.
+ */
+static float dist_px_to_dist3d_or_tangent(const ARegion *ar, const float dist_px)
+{
+ const RegionView3D *rv3d = ar->regiondata;
+ if (ar->winx >= ar->winy)
+ return 2 * (dist_px / ar->winx) / rv3d->winmat[0][0];
+ else
+ return 2 * (dist_px / ar->winy) / rv3d->winmat[1][1];
+}
- if (result) {
- float edge_loc[3], vec[3];
- float mul;
+static const float *get_vert_co(const BVHTreeFromMeshType *meshdata, const int index)
+{
+ switch (meshdata->type) {
+ case SNAP_MESH:
+ {
+ BVHTreeFromMesh *data = meshdata->userdata;
+ const MVert *vert = data->vert;
+ return vert[index].co;
+ }
+ case SNAP_EDIT_MESH:
+ {
+ BVHTreeFromEditMesh *data = meshdata->userdata;
+ BMVert *eve = BM_vert_at_index(data->em->bm, index);
+ return eve->co;
+ }
+ }
+ return NULL;
+}
- /* check for behind ray_start */
- sub_v3_v3v3(dvec, intersect, ray_start_local);
+static void copy_vert_no(const BVHTreeFromMeshType *meshdata, const int index, float r_no[3])
+{
+ switch (meshdata->type) {
+ case SNAP_MESH:
+ {
+ BVHTreeFromMesh *data = meshdata->userdata;
+ const MVert *vert = data->vert;
+ normal_short_to_float_v3(r_no, vert->no);
+ break;
+ }
+ case SNAP_EDIT_MESH:
+ {
+ BVHTreeFromEditMesh *data = meshdata->userdata;
+ BMVert *eve = BM_vert_at_index(data->em->bm, index);
+ copy_v3_v3(r_no, eve->no);
+ break;
+ }
+ }
+}
- sub_v3_v3v3(edge_loc, v1co, v2co);
- sub_v3_v3v3(vec, intersect, v2co);
+static void get_edge_verts(
+ const BVHTreeFromMeshType *meshdata, const int index,
+ const float *v_pair[2])
+{
+ switch (meshdata->type) {
+ case SNAP_MESH:
+ {
+ BVHTreeFromMesh *data = meshdata->userdata;
- mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc);
+ const MVert *vert = data->vert;
+ const MEdge *edge = data->edge + index;
- if (mul > 1) {
- mul = 1;
- copy_v3_v3(intersect, v1co);
+ v_pair[0] = vert[edge->v1].co;
+ v_pair[1] = vert[edge->v2].co;
+ break;
}
- else if (mul < 0) {
- mul = 0;
- copy_v3_v3(intersect, v2co);
+ case SNAP_EDIT_MESH:
+ {
+ BVHTreeFromEditMesh *data = meshdata->userdata;
+ BMEdge *eed = BM_edge_at_index(data->em->bm, index);
+
+ v_pair[0] = eed->v1->co;
+ v_pair[1] = eed->v2->co;
+ break;
}
+ }
+}
- if (dot_v3v3(ray_normal_local, dvec) > 0) {
- float location[3];
- float new_depth;
- float screen_loc[2];
- float new_dist;
+#define V3_MUL_ELEM(a, b) \
+ (a)[0] * (b)[0], \
+ (a)[1] * (b)[1], \
+ (a)[2] * (b)[2]
- copy_v3_v3(location, intersect);
+static bool test_vert_dist(
+ const float vco[3], const float ray_co[3], const float ray_dir[3],
+ const float ray_depth_range[2], const float scale[3],
+ /* read/write args */
+ float *ray_depth, float *dist_to_ray_sq,
+ /* return args */
+ float r_co[3])
+{
+ const float vco_sc[3] = {V3_MUL_ELEM(vco, scale)};
+ const float origin_sc[3] = {V3_MUL_ELEM(ray_co, scale)};
+ const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)};
- mul_m4_v3(obmat, location);
+ float depth, dist_sq;
+ dist_sq = dist_squared_to_ray_v3(origin_sc, dir_sc, vco_sc, &depth);
- new_depth = len_v3v3(location, ray_start);
+ if (depth < ray_depth_range[0]) {
+ return false;
+ }
- if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
- new_dist = len_manhattan_v2v2(mval_fl, screen_loc);
- }
- else {
- new_dist = TRANSFORM_DIST_MAX_PX;
- }
+ if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) {
+ *dist_to_ray_sq = dist_sq;
- /* 10% threshold if edge is closer but a bit further
- * this takes care of series of connected edges a bit slanted w.r.t the viewport
- * otherwise, it would stick to the verts of the closest edge and not slide along merrily
- * */
- if (new_dist <= *dist_px && new_depth < *ray_depth * 1.001f) {
- float n1[3], n2[3];
+ copy_v3_v3(r_co, vco);
- *ray_depth = new_depth;
- retval = true;
+ *ray_depth = depth;
+ return true;
+ }
+ return false;
+}
- sub_v3_v3v3(edge_loc, v1co, v2co);
- sub_v3_v3v3(vec, intersect, v2co);
+static bool test_edge_dist(
+ const float v1[3], const float v2[3], const float ray_co[3], const float ray_dir[3],
+ const float ray_depth_range[2], const float scale[3],
+ /* read/write args */
+ float *ray_depth, float *dist_to_ray_sq,
+ /* return args */
+ float r_co[3])
+{
+ const float v1_sc[3] = {V3_MUL_ELEM(v1, scale)};
+ const float v2_sc[3] = {V3_MUL_ELEM(v2, scale)};
+ const float co_sc[3] = {V3_MUL_ELEM(ray_co, scale)};
+ const float dir_sc[3] = {V3_MUL_ELEM(ray_dir, scale)};
- mul = dot_v3v3(vec, edge_loc) / dot_v3v3(edge_loc, edge_loc);
+ float tmp_co[3], depth, dist_sq;
+ dist_sq = dist_squared_ray_to_seg_v3(co_sc, dir_sc, v1_sc, v2_sc, tmp_co, &depth);
- if (r_no) {
- normal_short_to_float_v3(n1, v1no);
- normal_short_to_float_v3(n2, v2no);
- interp_v3_v3v3(r_no, n2, n1, mul);
- mul_m3_v3(timat, r_no);
- normalize_v3(r_no);
- }
+ if (depth < ray_depth_range[0]) {
+ return false;
+ }
- copy_v3_v3(r_loc, location);
+ if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) {
+ *dist_to_ray_sq = dist_sq;
- *dist_px = new_dist;
- }
+ tmp_co[0] /= scale[0];
+ tmp_co[1] /= scale[1];
+ tmp_co[2] /= scale[2];
+
+ copy_v3_v3(r_co, tmp_co);
+
+ *ray_depth = depth;
+ return true;
+ }
+ return false;
+}
+
+#undef V3_MUL_ELEM
+
+static bool test_projected_vert_dist(
+ PreDefProject *projectdefs,
+ const float co[3], const enum eViewProj view_proj,
+ const float mval[2], const float depth_range[2],
+ float r_co[3])
+{
+ float depth;
+ float(*pmat)[4] = projectdefs->pmat;
+ if (view_proj == VIEW_PROJ_PERSP) {
+ depth = mul_project_m4_v3_zfac(pmat, co);
+ if (depth < depth_range[0] || depth > depth_range[1]) {
+ return false;
}
}
- return retval;
+ float co2d[2] = {
+ (dot_m4_v3_row_x(pmat, co) + pmat[3][0]),
+ (dot_m4_v3_row_y(pmat, co) + pmat[3][1]),
+ };
+
+ if (view_proj == VIEW_PROJ_PERSP) {
+ mul_v2_fl(co2d, 1 / depth);
+ }
+
+ co2d[0] += 1.0f;
+ co2d[1] += 1.0f;
+ co2d[0] *= projectdefs->win_half[0];
+ co2d[1] *= projectdefs->win_half[1];
+
+ const float dist_sq = len_squared_v2v2(mval, co2d);
+ if (dist_sq < projectdefs->dist_px_sq) {
+ copy_v3_v3(r_co, co);
+ projectdefs->dist_px_sq = dist_sq;
+ return true;
+ }
+ return false;
}
-static bool snapVertex(
- const ARegion *ar, const float vco[3], const float vno[3],
- float obmat[4][4], float timat[3][3], const float mval_fl[2],
- const float ray_start[3], const float ray_start_local[3], const float ray_normal_local[3],
- /* read/write args */
- float *ray_depth, float *dist_px,
- /* return args */
- float r_loc[3], float r_no[3])
+static bool test_projected_edge_dist(
+ PreDefProject *projectdefs,
+ const float va[3], const float vb[3], const float ray_start[3], const float ray_normal[3],
+ const enum eViewProj view_proj, const float mval[2], const float depth_range[2],
+ float r_co[3])
{
- bool retval = false;
- float dvec[3];
- sub_v3_v3v3(dvec, vco, ray_start_local);
+ float tmp_co[3], depth;
+ dist_squared_ray_to_seg_v3(ray_start, ray_normal, va, vb, tmp_co, &depth);
+ return test_projected_vert_dist(projectdefs, tmp_co, view_proj, mval, depth_range, r_co);
+}
- if (dot_v3v3(ray_normal_local, dvec) > 0) {
- float location[3];
- float new_depth;
- float screen_loc[2];
- float new_dist;
+/** \} */
- copy_v3_v3(location, vco);
- mul_m4_v3(obmat, location);
+/* -------------------------------------------------------------------- */
- new_depth = len_v3v3(location, ray_start);
+/** \Walk DFS
+ * \{ */
+typedef struct Object_Nearest2dPrecalc {
+ float ray_origin_local[3];
+ float ray_direction_local[3];
+ float ray_inv_dir[3];
+
+ PreDefProject projectdefs;
+ float mval[2];
+ bool sign[3];
+ bool r_axis_closest[3];
+ float depth_range[2];
+
+ void *userdata;
+ int index;
+ float co[3];
+ float no[3];
+} Object_Nearest2dPrecalc;
+
+
+static void nearest2d_precalc(
+ Object_Nearest2dPrecalc *neasrest_precalc, const ARegion *ar,
+ const float dist_px, float obmat[4][4],
+ const float ray_origin_local[3], const float ray_direction_local[3],
+ const float mval[2], const float depth_range[2])
+{
+ precalc_project(&neasrest_precalc->projectdefs, ar, dist_px, obmat);
+ copy_v3_v3(neasrest_precalc->ray_origin_local, ray_origin_local);
+ copy_v3_v3(neasrest_precalc->ray_direction_local, ray_direction_local);
+ copy_v2_v2(neasrest_precalc->mval, mval);
+ copy_v2_v2(neasrest_precalc->depth_range, depth_range);
- if (ED_view3d_project_float_global(ar, location, screen_loc, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) {
- new_dist = len_manhattan_v2v2(mval_fl, screen_loc);
- }
- else {
- new_dist = TRANSFORM_DIST_MAX_PX;
- }
+ for (int i = 0; i < 3; i++) {
+ neasrest_precalc->ray_inv_dir[i] =
+ (neasrest_precalc->ray_direction_local[i] != 0.0f) ?
+ (1.0f / neasrest_precalc->ray_direction_local[i]) : FLT_MAX;
+ neasrest_precalc->sign[i] = (neasrest_precalc->ray_inv_dir[i] < 0.0f);
+ neasrest_precalc->r_axis_closest[i] = true;
+ }
+}
+static bool cb_walk_parent_snap_project(const BVHTreeAxisRange *bounds, void *user_data)
+{
+ Object_Nearest2dPrecalc *data = user_data;
+ float local_bvmin[3], local_bvmax[3];
+ if (data->sign[0]) {
+ local_bvmin[0] = bounds[0].max;
+ local_bvmax[0] = bounds[0].min;
+ }
+ else {
+ local_bvmin[0] = bounds[0].min;
+ local_bvmax[0] = bounds[0].max;
+ }
+ if (data->sign[1]) {
+ local_bvmin[1] = bounds[1].max;
+ local_bvmax[1] = bounds[1].min;
+ }
+ else {
+ local_bvmin[1] = bounds[1].min;
+ local_bvmax[1] = bounds[1].max;
+ }
+ if (data->sign[2]) {
+ local_bvmin[2] = bounds[2].max;
+ local_bvmax[2] = bounds[2].min;
+ }
+ else {
+ local_bvmin[2] = bounds[2].min;
+ local_bvmax[2] = bounds[2].max;
+ }
- if (new_dist <= *dist_px && new_depth < *ray_depth) {
- *ray_depth = new_depth;
- retval = true;
+ const float tmin[3] = {
+ (local_bvmin[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0],
+ (local_bvmin[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1],
+ (local_bvmin[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2],
+ };
+ const float tmax[3] = {
+ (local_bvmax[0] - data->ray_origin_local[0]) * data->ray_inv_dir[0],
+ (local_bvmax[1] - data->ray_origin_local[1]) * data->ray_inv_dir[1],
+ (local_bvmax[2] - data->ray_origin_local[2]) * data->ray_inv_dir[2],
+ };
+ float va[3], vb[3];
+ float rtmin, rtmax;
+ int main_axis;
+
+ if ((tmax[0] <= tmax[1]) && (tmax[0] <= tmax[2])) {
+ rtmax = tmax[0];
+ va[0] = vb[0] = local_bvmax[0];
+ main_axis = 3;
+ data->r_axis_closest[0] = data->sign[0];
+ }
+ else if ((tmax[1] <= tmax[0]) && (tmax[1] <= tmax[2])) {
+ rtmax = tmax[1];
+ va[1] = vb[1] = local_bvmax[1];
+ main_axis = 2;
+ data->r_axis_closest[1] = data->sign[1];
+ }
+ else {
+ rtmax = tmax[2];
+ va[2] = vb[2] = local_bvmax[2];
+ main_axis = 1;
+ data->r_axis_closest[2] = data->sign[2];
+ }
- copy_v3_v3(r_loc, location);
+ if ((tmin[0] >= tmin[1]) && (tmin[0] >= tmin[2])) {
+ rtmin = tmin[0];
+ va[0] = vb[0] = local_bvmin[0];
+ main_axis -= 3;
+ data->r_axis_closest[0] = !data->sign[0];
+ }
+ else if ((tmin[1] >= tmin[0]) && (tmin[1] >= tmin[2])) {
+ rtmin = tmin[1];
+ va[1] = vb[1] = local_bvmin[1];
+ main_axis -= 1;
+ data->r_axis_closest[1] = !data->sign[1];
+ }
+ else {
+ rtmin = tmin[2];
+ va[2] = vb[2] = local_bvmin[2];
+ main_axis -= 2;
+ data->r_axis_closest[2] = !data->sign[2];
+ }
+ if (main_axis < 0) {
+ main_axis += 3;
+ }
- if (r_no) {
- copy_v3_v3(r_no, vno);
- mul_m3_v3(timat, r_no);
- normalize_v3(r_no);
- }
+ /* if rtmin < rtmax, ray intersect `AABB` */
+ if (rtmin <= rtmax) {
+#ifdef IGNORE_BEHIND_RAY
+ /* `if rtmax < depth_min`, the whole `AABB` is behind us */
+ if (rtmax < min_depth) {
+ return fallback;
+ }
+#endif
+ const float proj = rtmin * data->ray_direction_local[main_axis];
+ data->r_axis_closest[main_axis] = (proj - va[main_axis]) < (vb[main_axis] - proj);
+ return true;
+ }
+#ifdef IGNORE_BEHIND_RAY
+ /* `if rtmin < depth_min`, the whole `AABB` is behing us */
+ else if (rtmin < min_depth) {
+ return fallback;
+ }
+#endif
+ if (data->sign[main_axis]) {
+ va[main_axis] = local_bvmax[main_axis];
+ vb[main_axis] = local_bvmin[main_axis];
+ }
+ else {
+ va[main_axis] = local_bvmin[main_axis];
+ vb[main_axis] = local_bvmax[main_axis];
+ }
+ float scale = fabsf(local_bvmax[main_axis] - local_bvmin[main_axis]);
+
+ float (*pmat)[4] = data->projectdefs.pmat;
+ float depth_a = mul_project_m4_v3_zfac(pmat, va);
+ float depth_b = depth_a + pmat[main_axis][3] * scale;
+
+ float va2d[2] = {
+ (dot_m4_v3_row_x(pmat, va) + pmat[3][0]),
+ (dot_m4_v3_row_y(pmat, va) + pmat[3][1]),
+ };
+ float vb2d[2] = {
+ (va2d[0] + pmat[main_axis][0] * scale) / depth_b,
+ (va2d[1] + pmat[main_axis][1] * scale) / depth_b,
+ };
- *dist_px = new_dist;
+ va2d[0] /= depth_a;
+ va2d[1] /= depth_a;
+
+ va2d[0] += 1.0f;
+ va2d[1] += 1.0f;
+ vb2d[0] += 1.0f;
+ vb2d[1] += 1.0f;
+
+ va2d[0] *= data->projectdefs.win_half[0];
+ va2d[1] *= data->projectdefs.win_half[1];
+ vb2d[0] *= data->projectdefs.win_half[0];
+ vb2d[1] *= data->projectdefs.win_half[1];
+
+ //float dvec[2], edge[2], rdist;
+ //sub_v2_v2v2(dvec, data->mval, va2d);
+ //sub_v2_v2v2(edge, vb2d, va2d);
+ float rdist;
+ short dvec[2] = {data->mval[0] - va2d[0], data->mval[1] - va2d[1]};
+ short edge[2] = {vb2d[0] - va2d[0], vb2d[1] - va2d[1]};
+ float lambda = dvec[0] * edge[0] + dvec[1] * edge[1];
+ if (lambda != 0.0f) {
+ lambda /= edge[0] * edge[0] + edge[1] * edge[1];
+ if (lambda <= 0.0f) {
+ rdist = len_squared_v2v2(data->mval, va2d);
+ data->r_axis_closest[main_axis] = true;
}
+ else if (lambda >= 1.0f) {
+ rdist = len_squared_v2v2(data->mval, vb2d);
+ data->r_axis_closest[main_axis] = false;
+ }
+ else {
+ va2d[0] += edge[0] * lambda;
+ va2d[1] += edge[1] * lambda;
+ rdist = len_squared_v2v2(data->mval, va2d);
+ data->r_axis_closest[main_axis] = lambda < 0.5f;
+ }
+ }
+ else {
+ rdist = len_squared_v2v2(data->mval, va2d);
}
+ return rdist < data->projectdefs.dist_px_sq;
+}
- return retval;
+static bool cb_walk_leaf_snap_vert(const BVHTreeAxisRange *bounds, int index, void *userdata)
+{
+ struct Object_Nearest2dPrecalc *neasrest_precalc = userdata;
+ const float co[3] = {
+ (bounds[0].min + bounds[0].max) / 2,
+ (bounds[1].min + bounds[1].max) / 2,
+ (bounds[2].min + bounds[2].max) / 2,
+ };
+
+ /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP)
+ * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO),
+ * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/
+ if (test_projected_vert_dist(
+ &neasrest_precalc->projectdefs, co, VIEW_PROJ_PERSP,
+ neasrest_precalc->mval, neasrest_precalc->depth_range,
+ neasrest_precalc->co))
+ {
+ copy_vert_no(neasrest_precalc->userdata, index, neasrest_precalc->no);
+ neasrest_precalc->index = index;
+ }
+ return true;
+}
+
+static bool cb_walk_leaf_snap_edge(const BVHTreeAxisRange *UNUSED(bounds), int index, void *userdata)
+{
+ struct Object_Nearest2dPrecalc *neasrest_precalc = userdata;
+
+ const float *v_pair[2];
+ get_edge_verts(neasrest_precalc->userdata, index, v_pair);
+
+ /* Currently the `BLI_bvhtree_walk_dfs` is being used only in the perspective view mode (VIEW_PROJ_PERSP)
+ * It could be used in orthographic view mode too (VIEW_PROJ_ORTHO),
+ * but in this case the `BLI_bvhtree_find_nearest_to_ray` is more efficient.*/
+ if (test_projected_edge_dist(
+ &neasrest_precalc->projectdefs, v_pair[0], v_pair[1],
+ neasrest_precalc->ray_origin_local, neasrest_precalc->ray_direction_local,
+ VIEW_PROJ_PERSP, neasrest_precalc->mval, neasrest_precalc->depth_range,
+ neasrest_precalc->co))
+ {
+ sub_v3_v3v3(neasrest_precalc->no, v_pair[0], v_pair[1]);
+ neasrest_precalc->index = index;
+ }
+ return true;
}
+static bool cb_nearest_walk_order(const BVHTreeAxisRange *UNUSED(bounds), char axis, void *userdata)
+{
+ const bool *r_axis_closest = ((struct Object_Nearest2dPrecalc *)userdata)->r_axis_closest;
+ return r_axis_closest[axis];
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+
+/** \name Internal Object Snapping API
+ * \{ */
+
static bool snapArmature(
const ARegion *ar, Object *ob, bArmature *arm, float obmat[4][4],
- const float mval[2], const short snap_to,
- const float ray_start[3], const float ray_normal[3],
+ const short snap_to, const float origin[3], const float dir[3],
+ const float mval[2], const enum eViewProj view_proj, const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_px,
+ float *dist_px,
/* return args */
float r_loc[3], float *UNUSED(r_no))
{
- float imat[4][4];
- float ray_start_local[3], ray_normal_local[3];
bool retval = false;
- invert_m4_m4(imat, obmat);
+ float ray_start_local[3], ray_normal_local[3];
+ if (snap_to != SCE_SNAP_MODE_VERTEX) {
+ float imat[4][4];
+ invert_m4_m4(imat, obmat);
- mul_v3_m4v3(ray_start_local, imat, ray_start);
- mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal);
+ copy_v3_v3(ray_start_local, origin);
+ copy_v3_v3(ray_normal_local, dir);
+ mul_m4_v3(imat, ray_start_local);
+ mul_mat3_m4_v3(imat, ray_normal_local);
+ }
- if (arm->edbo) {
- EditBone *eBone;
+ PreDefProject projectdefs;
+ precalc_project(&projectdefs, ar, *dist_px, obmat);
- for (eBone = arm->edbo->first; eBone; eBone = eBone->next) {
+ if (arm->edbo) {
+ for (EditBone *eBone = arm->edbo->first; eBone; eBone = eBone->next) {
if (eBone->layer & arm->layer) {
/* skip hidden or moving (selected) bones */
if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) {
switch (snap_to) {
case SCE_SNAP_MODE_VERTEX:
- retval |= snapVertex(
- ar, eBone->head, NULL, obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local, ray_depth,
- r_loc, NULL);
- retval |= snapVertex(
- ar, eBone->tail, NULL, obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local, ray_depth,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, eBone->head, view_proj, mval, depth_range, r_loc);
+ retval |= test_projected_vert_dist(
+ &projectdefs, eBone->tail, view_proj, mval, depth_range, r_loc);
break;
case SCE_SNAP_MODE_EDGE:
- retval |= snapEdge(
- ar, eBone->head, NULL, eBone->tail, NULL,
- obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, r_loc, NULL);
+ retval |= test_projected_edge_dist(
+ &projectdefs, eBone->head, eBone->tail, ray_start_local, ray_normal_local,
+ view_proj, mval, depth_range, r_loc);
break;
}
}
@@ -416,11 +778,8 @@ static bool snapArmature(
}
}
else if (ob->pose && ob->pose->chanbase.first) {
- bPoseChannel *pchan;
- Bone *bone;
-
- for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- bone = pchan->bone;
+ for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ Bone *bone = pchan->bone;
/* skip hidden bones */
if (bone && !(bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) {
const float *head_vec = pchan->pose_head;
@@ -428,61 +787,49 @@ static bool snapArmature(
switch (snap_to) {
case SCE_SNAP_MODE_VERTEX:
- retval |= snapVertex(
- ar, head_vec, NULL, obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, r_loc, NULL);
- retval |= snapVertex(
- ar, tail_vec, NULL, obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local, ray_depth,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, head_vec, view_proj, mval, depth_range, r_loc);
+ retval |= test_projected_vert_dist(
+ &projectdefs, tail_vec, view_proj, mval, depth_range, r_loc);
break;
case SCE_SNAP_MODE_EDGE:
- retval |= snapEdge(
- ar, head_vec, NULL, tail_vec, NULL,
- obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, r_loc, NULL);
+ retval |= test_projected_edge_dist(
+ &projectdefs, head_vec, tail_vec, ray_start_local, ray_normal_local,
+ view_proj, mval, depth_range, r_loc);
break;
}
}
}
}
-
- return retval;
+ if (retval) {
+ *dist_px = sqrtf(projectdefs.dist_px_sq);
+ mul_m4_v3(obmat, r_loc);
+ return true;
+ }
+ return false;
}
static bool snapCurve(
const ARegion *ar, Object *ob, Curve *cu, float obmat[4][4],
- const float mval[2], const short snap_to,
- const float ray_start[3], const float ray_normal[3],
+ const short snap_to, const float mval[2], const enum eViewProj view_proj,
+ const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_px,
+ float *dist_px,
/* return args */
float r_loc[3], float *UNUSED(r_no))
{
- float imat[4][4];
- float ray_start_local[3], ray_normal_local[3];
bool retval = false;
- int u;
-
- Nurb *nu;
/* only vertex snapping mode (eg control points and handles) supported for now) */
if (snap_to != SCE_SNAP_MODE_VERTEX) {
return retval;
}
- invert_m4_m4(imat, obmat);
-
- copy_v3_v3(ray_start_local, ray_start);
- copy_v3_v3(ray_normal_local, ray_normal);
-
- mul_m4_v3(imat, ray_start_local);
- mul_mat3_m4_v3(imat, ray_normal_local);
+ PreDefProject projectdefs;
+ precalc_project(&projectdefs, ar, *dist_px, obmat);
- for (nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) {
- for (u = 0; u < nu->pntsu; u++) {
+ for (Nurb *nu = (ob->mode == OB_MODE_EDIT ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) {
+ for (int u = 0; u < nu->pntsu; u++) {
switch (snap_to) {
case SCE_SNAP_MODE_VERTEX:
{
@@ -492,29 +839,20 @@ static bool snapCurve(
if (nu->bezt[u].f2 & SELECT || nu->bezt[u].hide != 0) {
break;
}
- retval |= snapVertex(
- ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, dist_px,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, r_loc);
/* don't snap if handle is selected (moving), or if it is aligning to a moving handle */
if (!(nu->bezt[u].f1 & SELECT) &&
!(nu->bezt[u].h1 & HD_ALIGN && nu->bezt[u].f3 & SELECT))
{
- retval |= snapVertex(
- ar, nu->bezt[u].vec[0], NULL, obmat, NULL, mval,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, dist_px,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, nu->bezt[u].vec[0], view_proj, mval, depth_range, r_loc);
}
if (!(nu->bezt[u].f3 & SELECT) &&
!(nu->bezt[u].h2 & HD_ALIGN && nu->bezt[u].f1 & SELECT))
{
- retval |= snapVertex(
- ar, nu->bezt[u].vec[2], NULL, obmat, NULL, mval,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, dist_px,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, nu->bezt[u].vec[2], view_proj, mval, depth_range, r_loc);
}
}
else {
@@ -522,29 +860,20 @@ static bool snapCurve(
if (nu->bp[u].f1 & SELECT || nu->bp[u].hide != 0) {
break;
}
- retval |= snapVertex(
- ar, nu->bp[u].vec, NULL, obmat, NULL, mval,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, dist_px,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc);
}
}
else {
/* curve is not visible outside editmode if nurb length less than two */
if (nu->pntsu > 1) {
if (nu->bezt) {
- retval |= snapVertex(
- ar, nu->bezt[u].vec[1], NULL, obmat, NULL, mval,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, dist_px,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, nu->bezt[u].vec[1], view_proj, mval, depth_range, r_loc);
}
else {
- retval |= snapVertex(
- ar, nu->bp[u].vec, NULL, obmat, NULL, mval,
- ray_start, ray_start_local, ray_normal_local,
- ray_depth, dist_px,
- r_loc, NULL);
+ retval |= test_projected_vert_dist(
+ &projectdefs, nu->bp[u].vec, view_proj, mval, depth_range, r_loc);
}
}
}
@@ -555,44 +884,42 @@ static bool snapCurve(
}
}
}
- return retval;
+ if (retval) {
+ *dist_px = sqrtf(projectdefs.dist_px_sq);
+ mul_m4_v3(obmat, r_loc);
+ return true;
+ }
+ return false;
}
/* may extend later (for now just snaps to empty center) */
static bool snapEmpty(
const ARegion *ar, Object *ob, float obmat[4][4],
- const float mval[2], const short snap_to,
- const float ray_start[3], const float ray_normal[3],
+ const short snap_to, const float mval[2], const enum eViewProj view_proj,
+ const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_px,
+ float *dist_px,
/* return args */
float r_loc[3], float *UNUSED(r_no))
{
- float imat[4][4];
- float ray_start_local[3], ray_normal_local[3];
bool retval = false;
if (ob->transflag & OB_DUPLI) {
return retval;
}
- /* for now only vertex supported */
- if (snap_to != SCE_SNAP_MODE_VERTEX) {
- return retval;
- }
-
- invert_m4_m4(imat, obmat);
-
- mul_v3_m4v3(ray_start_local, imat, ray_start);
- mul_v3_mat3_m4v3(ray_normal_local, imat, ray_normal);
+ /* for now only vertex supported */
switch (snap_to) {
case SCE_SNAP_MODE_VERTEX:
{
- const float zero_co[3] = {0.0f};
- retval |= snapVertex(
- ar, zero_co, NULL, obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local, ray_depth,
- r_loc, NULL);
+ PreDefProject projectdefs;
+ precalc_project(&projectdefs, ar, *dist_px, NULL);
+ float tmp_co[3];
+ copy_v3_v3(tmp_co, obmat[3]);
+ if (test_projected_vert_dist(&projectdefs, tmp_co, view_proj, mval, depth_range, r_loc)) {
+ *dist_px = sqrtf(projectdefs.dist_px_sq);
+ retval = true;
+ }
break;
}
default:
@@ -603,19 +930,23 @@ static bool snapEmpty(
}
static bool snapCamera(
- const ARegion *ar, Scene *scene, Object *object, float obmat[4][4],
- const float mval[2], const short snap_to,
- const float ray_start[3], const float ray_normal[3],
+ const SnapObjectContext *sctx, Object *object, float obmat[4][4],
+ const short snap_to, const float mval[2], const enum eViewProj view_proj,
+ const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_px,
+ float *dist_px,
/* return args */
float r_loc[3], float *UNUSED(r_no))
{
+ Scene *scene = sctx->scene;
+
+ PreDefProject projectdefs;
+ precalc_project(&projectdefs, sctx->v3d_data.ar, *dist_px, NULL);
+
float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4];
bool retval = false;
MovieClip *clip = BKE_object_movieclip_get(scene, object, false);
MovieTracking *tracking;
- float ray_start_local[3], ray_normal_local[3];
if (clip == NULL) {
return retval;
@@ -646,9 +977,6 @@ static bool snapCamera(
reconstructed_camera_imat[4][4];
float (*vertex_obmat)[4];
- copy_v3_v3(ray_start_local, ray_start);
- copy_v3_v3(ray_normal_local, ray_normal);
-
if ((tracking_object->flag & TRACKING_OBJECT_CAMERA) == 0) {
BKE_tracking_camera_get_reconstructed_interpolate(tracking, tracking_object,
CFRA, reconstructed_camera_mat);
@@ -665,21 +993,16 @@ static bool snapCamera(
copy_v3_v3(bundle_pos, track->bundle_pos);
if (tracking_object->flag & TRACKING_OBJECT_CAMERA) {
- mul_m4_v3(orig_camera_imat, ray_start_local);
- mul_mat3_m4_v3(orig_camera_imat, ray_normal_local);
vertex_obmat = orig_camera_mat;
}
else {
mul_m4_v3(reconstructed_camera_imat, bundle_pos);
- mul_m4_v3(imat, ray_start_local);
- mul_mat3_m4_v3(imat, ray_normal_local);
vertex_obmat = obmat;
}
- retval |= snapVertex(
- ar, bundle_pos, NULL, vertex_obmat, NULL, mval, dist_px,
- ray_start, ray_start_local, ray_normal_local, ray_depth,
- r_loc, NULL);
+ mul_m4_v3(vertex_obmat, bundle_pos);
+ retval |= test_projected_vert_dist(
+ &projectdefs, bundle_pos, view_proj, mval, depth_range, r_loc);
}
}
@@ -689,7 +1012,11 @@ static bool snapCamera(
break;
}
- return retval;
+ if (retval) {
+ *dist_px = sqrtf(projectdefs.dist_px_sq);
+ return true;
+ }
+ return false;
}
static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt)
@@ -700,149 +1027,45 @@ static int dm_looptri_to_poly_index(DerivedMesh *dm, const MLoopTri *lt)
struct NearestDM_Data {
void *bvhdata;
- bool is_persp;
- const float *ray_depth_range;
-
+ const float *depth_range;
float *ray_depth;
};
-static bool test_vert(
- const float vco[3], const float vno[3], const float ray_co[3], const float ray_dir[3],
- const float ray_depth_range[2], const float scale[3], const bool is_persp,
- /* read/write args */
- float *ray_depth, float *dist_to_ray_sq,
- /* return args */
- float r_co[3], float r_no[3])
-{
- const float vco_sc[3] = {
- vco[0] * scale[0],
- vco[1] * scale[1],
- vco[2] * scale[2],
- };
- const float co_sc[3] = {
- ray_co[0] * scale[0],
- ray_co[1] * scale[1],
- ray_co[2] * scale[2],
- };
- const float dir_sc[3] = {
- ray_dir[0] * scale[0],
- ray_dir[1] * scale[1],
- ray_dir[2] * scale[2],
- };
-
- float depth;
- float dist_sq = dist_squared_to_ray_v3(co_sc, dir_sc, vco_sc, &depth);
-
- if (depth < ray_depth_range[0])
- return false;
-
- if (is_persp)
- dist_sq /= SQUARE(depth);
-
- if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) {
- *dist_to_ray_sq = dist_sq;
-
- copy_v3_v3(r_co, vco);
-
- if (vno) {
- copy_v3_v3(r_no, vno);
- }
-
- *ray_depth = depth;
- return true;
- }
- return false;
-}
-
-static bool test_edge(
- const float v1[3], const float v2[3], const float ray_co[3], const float ray_dir[3],
- const float ray_depth_range[2], const float scale[3], const bool is_persp,
- /* read/write args */
- float *ray_depth, float *dist_to_ray_sq,
- /* return args */
- float r_co[3], float r_no[3])
-{
- const float v1_sc[3] = {
- v1[0] * scale[0],
- v1[1] * scale[1],
- v1[2] * scale[2],
- };
- const float v2_sc[3] = {
- v2[0] * scale[0],
- v2[1] * scale[1],
- v2[2] * scale[2],
- };
- const float co_sc[3] = {
- ray_co[0] * scale[0],
- ray_co[1] * scale[1],
- ray_co[2] * scale[2],
- };
- const float dir_sc[3] = {
- ray_dir[0] * scale[0],
- ray_dir[1] * scale[1],
- ray_dir[2] * scale[2],
- };
-
- float tmp_co[3], depth;
- float dist_sq = dist_squared_ray_to_seg_v3(co_sc, dir_sc, v1_sc, v2_sc, tmp_co, &depth);
-
- if (depth < ray_depth_range[0])
- return false;
-
- if (is_persp)
- dist_sq /= SQUARE(depth);
-
- if ((dist_sq < *dist_to_ray_sq) && (depth < *ray_depth)) {
- *dist_to_ray_sq = dist_sq;
-
- tmp_co[0] /= scale[0];
- tmp_co[1] /= scale[1];
- tmp_co[2] /= scale[2];
-
- copy_v3_v3(r_co, tmp_co);
-
- if (r_no) {
- sub_v3_v3v3(r_no, v1, v2);
- }
-
- *ray_depth = depth;
- return true;
- }
- return false;
-}
-
-static void test_vert_depth_cb(
+static void test_vert_ray_dist_cb(
void *userdata, const float origin[3], const float dir[3],
const float scale[3], int index, BVHTreeNearest *nearest)
{
struct NearestDM_Data *ndata = userdata;
- const BVHTreeFromMesh *data = ndata->bvhdata;
- const MVert *vert = data->vert + index;
+ const struct BVHTreeFromMeshType *data = ndata->bvhdata;
+
+ const float *co = get_vert_co(data, index);
- if (test_vert(
- vert->co, NULL, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp,
- ndata->ray_depth, &nearest->dist_sq,
- nearest->co, NULL))
+ if (test_vert_dist(
+ co, origin, dir, ndata->depth_range,
+ scale, ndata->ray_depth, &nearest->dist_sq,
+ nearest->co))
{
- normal_short_to_float_v3(nearest->no, vert->no);
+ copy_vert_no(data, index, nearest->no);
nearest->index = index;
}
}
-static void test_edge_depth_cb(
+static void test_edge_ray_dist_cb(
void *userdata, const float origin[3], const float dir[3],
const float scale[3], int index, BVHTreeNearest *nearest)
{
struct NearestDM_Data *ndata = userdata;
- const BVHTreeFromMesh *data = ndata->bvhdata;
- const MVert *vert = data->vert;
- const MEdge *edge = data->edge + index;
-
- if (test_edge(
- vert[edge->v1].co, vert[edge->v2].co, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp,
- ndata->ray_depth, &nearest->dist_sq,
- nearest->co, nearest->no))
+ BVHTreeFromMeshType *data = ndata->bvhdata;
+
+ const float *v_pair[2];
+ get_edge_verts(data, index, v_pair);
+
+ if (test_edge_dist(
+ v_pair[0], v_pair[1], origin, dir, ndata->depth_range,
+ scale, ndata->ray_depth, &nearest->dist_sq,
+ nearest->co))
{
+ sub_v3_v3v3(nearest->no, v_pair[0], v_pair[1]);
nearest->index = index;
}
}
@@ -850,10 +1073,11 @@ static void test_edge_depth_cb(
static bool snapDerivedMesh(
SnapObjectContext *sctx,
Object *ob, DerivedMesh *dm, float obmat[4][4], const unsigned int ob_index,
- const short snap_to, bool do_bb,
- const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2],
+ const short snap_to, const float mval[2], const enum eViewProj view_proj, bool do_bb,
+ const float ray_origin[3], const float ray_start[3], const float ray_normal[3],
+ const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_to_ray_sq, float *dist_px,
+ float *ray_depth, float *dist_px,
/* return args */
float r_loc[3], float r_no[3], int *r_index,
ListBase *r_hit_list)
@@ -865,7 +1089,7 @@ static bool snapDerivedMesh(
return retval;
}
}
- if (snap_to == SCE_SNAP_MODE_EDGE) {
+ else if (snap_to == SCE_SNAP_MODE_EDGE) {
if (dm->getNumEdges(dm) == 0) {
return retval;
}
@@ -877,8 +1101,7 @@ static bool snapDerivedMesh(
}
{
- const bool is_persp = sctx->use_v3d && ((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp;
- bool need_ray_start_correction_init = (snap_to == SCE_SNAP_MODE_FACE) && sctx->use_v3d && !is_persp;
+ bool need_ray_start_correction_init = (snap_to == SCE_SNAP_MODE_FACE) && (view_proj == VIEW_PROJ_ORTHO);
float imat[4][4];
float timat[3][3]; /* transpose inverse matrix for normals */
@@ -985,175 +1208,161 @@ static bool snapDerivedMesh(
break;
}
}
- switch (snap_to) {
- case SCE_SNAP_MODE_FACE:
- {
- /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already
- * been *inside* boundbox, leading to snap failures (see T38409).
- * Note also ar might be null (see T38435), in this case we assume ray_start is ok!
- */
- if (sctx->use_v3d && !is_persp) { /* do_ray_start_correction */
- if (need_ray_start_correction_init) {
- /* We *need* a reasonably valid len_diff in this case.
- * Use BHVTree to find the closest face from ray_start_local.
- */
- if (treedata && treedata->tree != NULL) {
- BVHTreeNearest nearest;
- nearest.index = -1;
- nearest.dist_sq = FLT_MAX;
- /* Compute and store result. */
- BLI_bvhtree_find_nearest(
- treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata);
- if (nearest.index != -1) {
- float dvec[3];
- sub_v3_v3v3(dvec, nearest.co, ray_start_local);
- len_diff = dot_v3v3(dvec, ray_normal_local);
- }
- }
- }
- float ray_org_local[3];
- copy_v3_v3(ray_org_local, ray_origin);
- mul_m4_v3(imat, ray_org_local);
+ if (!treedata || !treedata->tree) {
+ return retval;
+ }
- /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far
- * away ray_start values (as returned in case of ortho view3d), see T38358.
+ if (snap_to == SCE_SNAP_MODE_FACE) {
+ /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already
+ * been *inside* boundbox, leading to snap failures (see T38409).
+ * Note also ar might be null (see T38435), in this case we assume ray_start is ok!
+ */
+ if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */
+ if (need_ray_start_correction_init) {
+ /* We *need* a reasonably valid len_diff in this case.
+ * Use BHVTree to find the closest face from ray_start_local.
*/
- len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */
- madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local,
- len_diff + ray_depth_range[0]);
- local_depth -= len_diff;
- }
- else {
- len_diff = 0.0f;
- }
- if (r_hit_list) {
- struct RayCastAll_Data data;
-
- data.bvhdata = treedata;
- data.raycast_callback = treedata->raycast_callback;
- data.obmat = obmat;
- data.timat = timat;
- data.len_diff = len_diff;
- data.local_scale = local_scale;
- data.ob = ob;
- data.ob_uuid = ob_index;
- data.dm = dm;
- data.hit_list = r_hit_list;
- data.retval = retval;
-
- BLI_bvhtree_ray_cast_all(
- treedata->tree, ray_start_local, ray_normal_local, 0.0f,
- *ray_depth, raycast_all_cb, &data);
-
- retval = data.retval;
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ /* Compute and store result. */
+ BLI_bvhtree_find_nearest(
+ treedata->tree, ray_start_local, &nearest, treedata->nearest_callback, treedata);
+ if (nearest.index != -1) {
+ float dvec[3];
+ sub_v3_v3v3(dvec, nearest.co, ray_start_local);
+ len_diff = dot_v3v3(dvec, ray_normal_local);
+ }
}
- else {
- BVHTreeRayHit hit;
-
- hit.index = -1;
- hit.dist = local_depth;
-
- if (treedata->tree &&
- BLI_bvhtree_ray_cast(
- treedata->tree, ray_start_local, ray_normal_local, 0.0f,
- &hit, treedata->raycast_callback, treedata) != -1)
- {
- hit.dist += len_diff;
- hit.dist /= local_scale;
- if (hit.dist <= *ray_depth) {
- *ray_depth = hit.dist;
- copy_v3_v3(r_loc, hit.co);
- copy_v3_v3(r_no, hit.no);
+ float ray_org_local[3];
+
+ copy_v3_v3(ray_org_local, ray_origin);
+ mul_m4_v3(imat, ray_org_local);
+
+ /* We pass a temp ray_start, set from object's boundbox, to avoid precision issues with very far
+ * away ray_start values (as returned in case of ortho view3d), see T38358.
+ */
+ len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */
+ madd_v3_v3v3fl(
+ ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale);
+ local_depth -= len_diff;
+ }
+ else {
+ len_diff = 0.0f;
+ }
+ if (r_hit_list) {
+ struct RayCastAll_Data data;
+
+ data.bvhdata = treedata;
+ data.raycast_callback = treedata->raycast_callback;
+ data.obmat = obmat;
+ data.timat = timat;
+ data.len_diff = len_diff;
+ data.local_scale = local_scale;
+ data.ob = ob;
+ data.ob_uuid = ob_index;
+ data.hit_list = r_hit_list;
+ data.retval = retval;
+
+ BLI_bvhtree_ray_cast_all(
+ treedata->tree, ray_start_local, ray_normal_local, 0.0f,
+ *ray_depth, raycast_all_cb, &data);
+
+ retval = data.retval;
+ }
+ else {
+ BVHTreeRayHit hit = {.index = -1, .dist = local_depth};
+
+ if (BLI_bvhtree_ray_cast(
+ treedata->tree, ray_start_local, ray_normal_local, 0.0f,
+ &hit, treedata->raycast_callback, treedata) != -1)
+ {
+ hit.dist += len_diff;
+ hit.dist /= local_scale;
+ if (hit.dist <= *ray_depth) {
+ *ray_depth = hit.dist;
+ copy_v3_v3(r_loc, hit.co);
+
+ /* back to worldspace */
+ mul_m4_v3(obmat, r_loc);
- /* back to worldspace */
- mul_m4_v3(obmat, r_loc);
+ if (r_no) {
+ copy_v3_v3(r_no, hit.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
+ }
- retval = true;
+ retval = true;
- if (r_index) {
- *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]);
- }
+ if (r_index) {
+ *r_index = dm_looptri_to_poly_index(dm, &treedata->looptri[hit.index]);
}
}
}
- break;
}
- case SCE_SNAP_MODE_VERTEX:
- {
- float ray_org_local[3];
+ }
+ else {
+ const ARegion *ar = sctx->v3d_data.ar;
- copy_v3_v3(ray_org_local, ray_origin);
- mul_m4_v3(imat, ray_org_local);
+ float ray_org_local[3];
+ copy_v3_v3(ray_org_local, ray_origin);
+ mul_m4_v3(imat, ray_org_local);
- BVHTreeNearest nearest;
+ BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_MESH};
- nearest.index = -1;
- nearest.dist_sq = *dist_to_ray_sq;
+ if (view_proj == VIEW_PROJ_PERSP) {
+ Object_Nearest2dPrecalc neasrest_precalc;
+ neasrest_precalc.userdata = &treedata_type;
+ neasrest_precalc.index = -1;
- struct NearestDM_Data userdata;
- userdata.bvhdata = treedata;
- userdata.is_persp = is_persp;
- userdata.ray_depth_range = ray_depth_range;
- userdata.ray_depth = ray_depth;
+ nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat,
+ ray_org_local, ray_normal_local, mval, depth_range);
- float ob_scale[3];
- mat4_to_size(ob_scale, obmat);
+ BVHTree_WalkLeafCallback cb_walk_leaf =
+ (snap_to == SCE_SNAP_MODE_VERTEX) ?
+ cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge;
- if (treedata->tree && (
- is_persp ?
- BLI_bvhtree_find_nearest_to_ray_angle(
- treedata->tree, ray_org_local, ray_normal_local,
- true, ob_scale, &nearest, test_vert_depth_cb, &userdata) :
- BLI_bvhtree_find_nearest_to_ray(
- treedata->tree, ray_org_local, ray_normal_local,
- true, ob_scale, &nearest, test_vert_depth_cb, &userdata)) != -1)
- {
- copy_v3_v3(r_loc, nearest.co);
+ BLI_bvhtree_walk_dfs(
+ treedata->tree,
+ cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc);
+
+ if (neasrest_precalc.index != -1) {
+ copy_v3_v3(r_loc, neasrest_precalc.co);
mul_m4_v3(obmat, r_loc);
if (r_no) {
- copy_v3_v3(r_no, nearest.no);
+ copy_v3_v3(r_no, neasrest_precalc.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
- *dist_px *= nearest.dist_sq / (*dist_to_ray_sq);
- *dist_to_ray_sq = nearest.dist_sq;
+ *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq);
retval = true;
}
- break;
}
- case SCE_SNAP_MODE_EDGE:
- {
- float ray_org_local[3];
-
- copy_v3_v3(ray_org_local, ray_origin);
- mul_m4_v3(imat, ray_org_local);
-
+ else {
BVHTreeNearest nearest;
nearest.index = -1;
- nearest.dist_sq = *dist_to_ray_sq;
+ float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px);
+ nearest.dist_sq = SQUARE(dist_3d);
- struct NearestDM_Data userdata;
- userdata.bvhdata = treedata;
- userdata.is_persp = is_persp;
- userdata.ray_depth_range = ray_depth_range;
- userdata.ray_depth = ray_depth;
float ob_scale[3];
mat4_to_size(ob_scale, obmat);
- if (treedata->tree && (
- is_persp ?
- BLI_bvhtree_find_nearest_to_ray_angle(
- treedata->tree, ray_org_local, ray_normal_local,
- true, ob_scale, &nearest, test_edge_depth_cb, &userdata) :
- BLI_bvhtree_find_nearest_to_ray(
- treedata->tree, ray_org_local, ray_normal_local,
- true, ob_scale, &nearest, test_edge_depth_cb, &userdata)) != -1)
+ struct NearestDM_Data userdata;
+ userdata.bvhdata = &treedata_type;
+ userdata.depth_range = depth_range;
+ userdata.ray_depth = ray_depth;
+
+ BVHTree_NearestToRayCallback cb_test_ray_dist =
+ (snap_to == SCE_SNAP_MODE_VERTEX) ?
+ test_vert_ray_dist_cb : test_edge_ray_dist_cb;
+
+ if (BLI_bvhtree_find_nearest_to_ray(
+ treedata->tree, ray_org_local, ray_normal_local,
+ true, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1)
{
copy_v3_v3(r_loc, nearest.co);
mul_m4_v3(obmat, r_loc);
@@ -1162,12 +1371,10 @@ static bool snapDerivedMesh(
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
- *dist_px *= nearest.dist_sq / (*dist_to_ray_sq);
- *dist_to_ray_sq = nearest.dist_sq;
+ *dist_px *= sqrtf(nearest.dist_sq) / dist_3d;
retval = true;
}
- break;
}
}
@@ -1181,47 +1388,14 @@ static bool snapDerivedMesh(
return retval;
}
-static void test_bmvert_depth_cb(
- void *userdata, const float origin[3], const float dir[3],
- const float scale[3], int index, BVHTreeNearest *nearest)
-{
- struct NearestDM_Data *ndata = userdata;
- const BMEditMesh *em = ndata->bvhdata;
- BMVert *eve = BM_vert_at_index(em->bm, index);
-
- if (test_vert(
- eve->co, eve->no, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp,
- ndata->ray_depth, &nearest->dist_sq,
- nearest->co, nearest->no))
- {
- nearest->index = index;
- }
-}
-
-static void test_bmedge_depth_cb(
- void *userdata, const float origin[3], const float dir[3],
- const float scale[3], int index, BVHTreeNearest *nearest)
-{
- struct NearestDM_Data *ndata = userdata;
- const BMEditMesh *em = ndata->bvhdata;
- BMEdge *eed = BM_edge_at_index(em->bm, index);
-
- if (test_edge(
- eed->v1->co, eed->v2->co, origin, dir, ndata->ray_depth_range, scale, ndata->is_persp,
- ndata->ray_depth, &nearest->dist_sq,
- nearest->co, nearest->no))
- {
- nearest->index = index;
- }
-}
-
static bool snapEditMesh(
SnapObjectContext *sctx,
Object *ob, BMEditMesh *em, float obmat[4][4], const unsigned int ob_index,
- float *dist_px, const short snap_to,
- const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2],
+ const short snap_to, const float mval[2], const enum eViewProj view_proj,
+ const float ray_origin[3], const float ray_start[3], const float ray_normal[3],
+ const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_to_ray_sq,
+ float *ray_depth, float *dist_px,
/* return args */
float r_loc[3], float r_no[3], int *r_index,
ListBase *r_hit_list)
@@ -1245,8 +1419,6 @@ static bool snapEditMesh(
}
{
- const bool is_persp = (sctx->use_v3d && ((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp);
-
float imat[4][4];
float timat[3][3]; /* transpose inverse matrix for normals */
float ray_normal_local[3];
@@ -1351,183 +1523,168 @@ static bool snapEditMesh(
}
}
- switch (snap_to) {
- case SCE_SNAP_MODE_FACE:
- {
- float ray_start_local[3];
- copy_v3_v3(ray_start_local, ray_start);
- mul_m4_v3(imat, ray_start_local);
-
- /* local scale in normal direction */
- float local_scale = normalize_v3(ray_normal_local);
- float local_depth = *ray_depth;
- if (local_depth != BVH_RAYCAST_DIST_MAX) {
- local_depth *= local_scale;
- }
+ if (!treedata || !treedata->tree) {
+ return retval;
+ }
+
+ if (snap_to == SCE_SNAP_MODE_FACE) {
+ float ray_start_local[3];
+ copy_v3_v3(ray_start_local, ray_start);
+ mul_m4_v3(imat, ray_start_local);
+
+ /* local scale in normal direction */
+ float local_scale = normalize_v3(ray_normal_local);
+ float local_depth = *ray_depth;
+ if (local_depth != BVH_RAYCAST_DIST_MAX) {
+ local_depth *= local_scale;
+ }
- /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already
- * been *inside* boundbox, leading to snap failures (see T38409).
- * Note also ar might be null (see T38435), in this case we assume ray_start is ok!
+ /* Only use closer ray_start in case of ortho view! In perspective one, ray_start may already
+ * been *inside* boundbox, leading to snap failures (see T38409).
+ * Note also ar might be null (see T38435), in this case we assume ray_start is ok!
+ */
+ float len_diff = 0.0f;
+ if (view_proj == VIEW_PROJ_ORTHO) { /* do_ray_start_correction */
+ /* We *need* a reasonably valid len_diff in this case.
+ * Use BHVTree to find the closest face from ray_start_local.
*/
- float len_diff = 0.0f;
- if (sctx->use_v3d && !is_persp) { /* do_ray_start_correction */
- /* We *need* a reasonably valid len_diff in this case.
- * Use BHVTree to find the closest face from ray_start_local.
+ BVHTreeNearest nearest;
+ nearest.index = -1;
+ nearest.dist_sq = FLT_MAX;
+ /* Compute and store result. */
+ if (BLI_bvhtree_find_nearest(
+ treedata->tree, ray_start_local, &nearest, NULL, NULL) != -1)
+ {
+ float dvec[3];
+ sub_v3_v3v3(dvec, nearest.co, ray_start_local);
+ len_diff = dot_v3v3(dvec, ray_normal_local);
+ float ray_org_local[3];
+
+ copy_v3_v3(ray_org_local, ray_origin);
+ mul_m4_v3(imat, ray_org_local);
+
+ /* We pass a temp ray_start, set from object's boundbox,
+ * to avoid precision issues with very far away ray_start values
+ * (as returned in case of ortho view3d), see T38358.
*/
- if (treedata && treedata->tree != NULL) {
- BVHTreeNearest nearest;
- nearest.index = -1;
- nearest.dist_sq = FLT_MAX;
- /* Compute and store result. */
- if (BLI_bvhtree_find_nearest(
- treedata->tree, ray_start_local, &nearest, NULL, NULL) != -1)
- {
- float dvec[3];
- sub_v3_v3v3(dvec, nearest.co, ray_start_local);
- len_diff = dot_v3v3(dvec, ray_normal_local);
- float ray_org_local[3];
-
- copy_v3_v3(ray_org_local, ray_origin);
- mul_m4_v3(imat, ray_org_local);
-
- /* We pass a temp ray_start, set from object's boundbox,
- * to avoid precision issues with very far away ray_start values
- * (as returned in case of ortho view3d), see T38358.
- */
- len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */
- madd_v3_v3v3fl(ray_start_local, ray_org_local, ray_normal_local,
- len_diff + ray_depth_range[0]);
- local_depth -= len_diff;
- }
- }
- }
- if (r_hit_list) {
- struct RayCastAll_Data data;
-
- data.bvhdata = treedata;
- data.raycast_callback = treedata->raycast_callback;
- data.obmat = obmat;
- data.timat = timat;
- data.len_diff = len_diff;
- data.local_scale = local_scale;
- data.ob = ob;
- data.ob_uuid = ob_index;
- data.dm = NULL;
- data.hit_list = r_hit_list;
- data.retval = retval;
-
- BLI_bvhtree_ray_cast_all(
- treedata->tree, ray_start_local, ray_normal_local, 0.0f,
- *ray_depth, raycast_all_cb, &data);
-
- retval = data.retval;
+ len_diff -= local_scale; /* make temp start point a bit away from bbox hit point. */
+ madd_v3_v3v3fl(
+ ray_start_local, ray_org_local, ray_normal_local, len_diff + depth_range[0] * local_scale);
+ local_depth -= len_diff;
}
- else {
- BVHTreeRayHit hit;
-
- hit.index = -1;
- hit.dist = local_depth;
-
- if (treedata->tree &&
- BLI_bvhtree_ray_cast(
- treedata->tree, ray_start_local, ray_normal_local, 0.0f,
- &hit, treedata->raycast_callback, treedata) != -1)
- {
- hit.dist += len_diff;
- hit.dist /= local_scale;
- if (hit.dist <= *ray_depth) {
- *ray_depth = hit.dist;
- copy_v3_v3(r_loc, hit.co);
- copy_v3_v3(r_no, hit.no);
+ }
+ if (r_hit_list) {
+ struct RayCastAll_Data data;
+
+ data.bvhdata = treedata;
+ data.raycast_callback = treedata->raycast_callback;
+ data.obmat = obmat;
+ data.timat = timat;
+ data.len_diff = len_diff;
+ data.local_scale = local_scale;
+ data.ob = ob;
+ data.ob_uuid = ob_index;
+ data.hit_list = r_hit_list;
+ data.retval = retval;
+
+ BLI_bvhtree_ray_cast_all(
+ treedata->tree, ray_start_local, ray_normal_local, 0.0f,
+ *ray_depth, raycast_all_cb, &data);
+
+ retval = data.retval;
+ }
+ else {
+ BVHTreeRayHit hit = {.index = -1, .dist = local_depth};
- /* back to worldspace */
- mul_m4_v3(obmat, r_loc);
+ if (BLI_bvhtree_ray_cast(
+ treedata->tree, ray_start_local, ray_normal_local, 0.0f,
+ &hit, treedata->raycast_callback, treedata) != -1)
+ {
+ hit.dist += len_diff;
+ hit.dist /= local_scale;
+ if (hit.dist <= *ray_depth) {
+ *ray_depth = hit.dist;
+ copy_v3_v3(r_loc, hit.co);
+
+ /* back to worldspace */
+ mul_m4_v3(obmat, r_loc);
+
+ if (r_no) {
+ copy_v3_v3(r_no, hit.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
+ }
- retval = true;
+ retval = true;
- if (r_index) {
- *r_index = hit.index;
- }
+ if (r_index) {
+ *r_index = hit.index;
}
}
}
- break;
}
- case SCE_SNAP_MODE_EDGE:
- {
- float ray_org_local[3];
+ }
+ else {
+ const ARegion *ar = sctx->v3d_data.ar;
- copy_v3_v3(ray_org_local, ray_origin);
- mul_m4_v3(imat, ray_org_local);
+ float ray_org_local[3];
+ copy_v3_v3(ray_org_local, ray_origin);
+ mul_m4_v3(imat, ray_org_local);
- BVHTreeNearest nearest;
+ BVHTreeFromMeshType treedata_type = {.userdata = treedata, .type = SNAP_EDIT_MESH};
- nearest.index = -1;
- nearest.dist_sq = *dist_to_ray_sq;
+ if (view_proj == VIEW_PROJ_PERSP) {
+ Object_Nearest2dPrecalc neasrest_precalc;
+ neasrest_precalc.userdata = &treedata_type;
+ neasrest_precalc.index = -1;
- struct NearestDM_Data userdata;
- userdata.bvhdata = em;
- userdata.is_persp = is_persp;
- userdata.ray_depth_range = ray_depth_range;
- userdata.ray_depth = ray_depth;
+ nearest2d_precalc(&neasrest_precalc, ar, *dist_px, obmat,
+ ray_org_local, ray_normal_local, mval, depth_range);
- float ob_scale[3];
- mat4_to_size(ob_scale, obmat);
+ BVHTree_WalkLeafCallback cb_walk_leaf =
+ (snap_to == SCE_SNAP_MODE_VERTEX) ?
+ cb_walk_leaf_snap_vert : cb_walk_leaf_snap_edge;
- if (treedata->tree && (
- is_persp ?
- BLI_bvhtree_find_nearest_to_ray_angle(
- treedata->tree, ray_org_local, ray_normal_local,
- false, ob_scale, &nearest, test_bmedge_depth_cb, &userdata) :
- BLI_bvhtree_find_nearest_to_ray(
- treedata->tree, ray_org_local, ray_normal_local,
- false, ob_scale, &nearest, test_bmedge_depth_cb, &userdata)) != -1)
- {
- copy_v3_v3(r_loc, nearest.co);
+ BLI_bvhtree_walk_dfs(
+ treedata->tree,
+ cb_walk_parent_snap_project, cb_walk_leaf, cb_nearest_walk_order, &neasrest_precalc);
+
+ if (neasrest_precalc.index != -1) {
+ copy_v3_v3(r_loc, neasrest_precalc.co);
mul_m4_v3(obmat, r_loc);
if (r_no) {
- copy_v3_v3(r_no, nearest.no);
+ copy_v3_v3(r_no, neasrest_precalc.no);
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
- *dist_px *= nearest.dist_sq / (*dist_to_ray_sq);
- *dist_to_ray_sq = nearest.dist_sq;
+ *dist_px = sqrtf(neasrest_precalc.projectdefs.dist_px_sq);
retval = true;
}
- break;
}
- case SCE_SNAP_MODE_VERTEX:
- {
- float ray_org_local[3];
-
- copy_v3_v3(ray_org_local, ray_origin);
- mul_m4_v3(imat, ray_org_local);
-
+ else {
BVHTreeNearest nearest;
nearest.index = -1;
- nearest.dist_sq = *dist_to_ray_sq;
+ float dist_3d = dist_px_to_dist3d_or_tangent(ar, *dist_px);
+ nearest.dist_sq = SQUARE(dist_3d);
- struct NearestDM_Data userdata;
- userdata.bvhdata = em;
- userdata.is_persp = is_persp;
- userdata.ray_depth_range = ray_depth_range;
- userdata.ray_depth = ray_depth;
float ob_scale[3];
mat4_to_size(ob_scale, obmat);
- if (treedata->tree && (
- is_persp ?
- BLI_bvhtree_find_nearest_to_ray_angle(
- treedata->tree, ray_org_local, ray_normal_local,
- false, ob_scale, &nearest, test_bmvert_depth_cb, &userdata) :
- BLI_bvhtree_find_nearest_to_ray(
- treedata->tree, ray_org_local, ray_normal_local,
- false, ob_scale, &nearest, test_bmedge_depth_cb, &userdata)) != -1)
+ struct NearestDM_Data userdata;
+ userdata.bvhdata = &treedata_type;
+ userdata.depth_range = depth_range;
+ userdata.ray_depth = ray_depth;
+
+ BVHTree_NearestToRayCallback cb_test_ray_dist =
+ (snap_to == SCE_SNAP_MODE_VERTEX) ?
+ test_vert_ray_dist_cb : test_edge_ray_dist_cb;
+
+ if (BLI_bvhtree_find_nearest_to_ray(
+ treedata->tree, ray_org_local, ray_normal_local,
+ false, ob_scale, &nearest, cb_test_ray_dist, &userdata) != -1)
{
copy_v3_v3(r_loc, nearest.co);
mul_m4_v3(obmat, r_loc);
@@ -1536,12 +1693,10 @@ static bool snapEditMesh(
mul_m3_v3(timat, r_no);
normalize_v3(r_no);
}
- *dist_px *= nearest.dist_sq / (*dist_to_ray_sq);
- *dist_to_ray_sq = nearest.dist_sq;
+ *dist_px *= sqrtf(nearest.dist_sq) / dist_3d;
retval = true;
}
- break;
}
}
@@ -1557,10 +1712,6 @@ static bool snapEditMesh(
/**
* \param use_obedit: Uses the coordinates of BMesh (if any) to do the snapping;
- * \param ray_depth_range:
- * - 0: distance from the ray_origin to the clipping plane min (can be negative).
- * - 1: maximum distance, elements outside this are ignored.
- * \param ray_depth: maximum depth allowed for r_co.
*
* \note Duplicate args here are documented at #snapObjectsRay
*/
@@ -1568,15 +1719,21 @@ static bool snapObject(
SnapObjectContext *sctx,
Object *ob, float obmat[4][4], const unsigned int ob_index,
bool use_obedit, const short snap_to, const float mval[2],
- const float ray_origin[3], const float ray_start[3], const float ray_normal[3], const float ray_depth_range[2],
+ const float ray_origin[3], const float ray_start[3], const float ray_normal[3],
+ const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_to_ray_sq, float *dist_px,
+ float *ray_depth, float *dist_px,
/* return args */
float r_loc[3], float r_no[3], int *r_index,
Object **r_ob, float r_obmat[4][4],
ListBase *r_hit_list)
{
+ const enum eViewProj view_proj =
+ ((sctx->use_v3d == false) || (mval == NULL)) ? VIEW_PROJ_NONE :
+ (((RegionView3D *)sctx->v3d_data.ar->regiondata)->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO);
+
const ARegion *ar = sctx->v3d_data.ar;
+
bool retval = false;
if (ob->type == OB_MESH) {
@@ -1586,9 +1743,9 @@ static bool snapObject(
em = BKE_editmesh_from_object(ob);
retval = snapEditMesh(
sctx, ob, em, obmat, ob_index,
- dist_px, snap_to,
- ray_origin, ray_start, ray_normal, ray_depth_range,
- ray_depth, dist_to_ray_sq,
+ snap_to, mval, view_proj,
+ ray_origin, ray_start, ray_normal, depth_range,
+ ray_depth, dist_px,
r_loc, r_no, r_index,
r_hit_list);
}
@@ -1605,42 +1762,43 @@ static bool snapObject(
}
retval = snapDerivedMesh(
sctx, ob, dm, obmat, ob_index,
- snap_to, true,
- ray_origin, ray_start, ray_normal, ray_depth_range,
- ray_depth, dist_to_ray_sq, dist_px,
+ snap_to, mval, view_proj, true,
+ ray_origin, ray_start, ray_normal, depth_range,
+ ray_depth, dist_px,
r_loc, r_no,
r_index, r_hit_list);
dm->release(dm);
}
}
- else if (ob->type == OB_ARMATURE) {
- retval = snapArmature(
- ar, ob, ob->data, obmat, mval, snap_to,
- ray_start, ray_normal,
- ray_depth, dist_px,
- r_loc, r_no);
- }
- else if (ob->type == OB_CURVE) {
- retval = snapCurve(
- ar, ob, ob->data, obmat, mval, snap_to,
- ray_start, ray_normal,
- ray_depth, dist_px,
- r_loc, r_no);
- }
- else if (ob->type == OB_EMPTY) {
- retval = snapEmpty(
- ar, ob, obmat, mval, snap_to,
- ray_start, ray_normal,
- ray_depth, dist_px,
- r_loc, r_no);
- }
- else if (ob->type == OB_CAMERA) {
- retval = snapCamera(
- ar, sctx->scene, ob, obmat, mval, snap_to,
- ray_start, ray_normal,
- ray_depth, dist_px,
- r_loc, r_no);
+ else if (snap_to != SCE_SNAP_MODE_FACE) {
+ if (ob->type == OB_ARMATURE) {
+ retval = snapArmature(
+ ar, ob, ob->data, obmat, snap_to, ray_origin, ray_normal,
+ mval, view_proj, depth_range, dist_px,
+ r_loc, r_no);
+ }
+ else if (ob->type == OB_CURVE) {
+ retval = snapCurve(
+ ar, ob, ob->data, obmat, snap_to, mval, view_proj,
+ depth_range,
+ dist_px,
+ r_loc, r_no);
+ }
+ else if (ob->type == OB_EMPTY) {
+ retval = snapEmpty(
+ ar, ob, obmat, snap_to, mval, view_proj,
+ depth_range,
+ dist_px,
+ r_loc, r_no);
+ }
+ else if (ob->type == OB_CAMERA) {
+ retval = snapCamera(
+ sctx, ob, obmat, snap_to, mval, view_proj,
+ depth_range,
+ dist_px,
+ r_loc, r_no);
+ }
}
if (retval) {
@@ -1666,20 +1824,18 @@ static bool snapObject(
* \param snap_select: from enum SnapSelect.
*
* \param use_object_edit_cage: Uses the coordinates of BMesh (if any) to do the snapping.
- * \param mval: Optional screen-space 2D location we're snapping to (may phase out).
+ * \param mval: Mouse coords.
+ * When NULL, ray-casting is handled without any projection matrix correction.
* \param ray_origin: ray_start before being moved toward the ray_normal at the distance from vew3d clip_min.
* \param ray_start: ray_origin moved for the start clipping plane (clip_min).
* \param ray_normal: Unit length direction of the ray.
+ * \param depth_range: distances of clipe plane min and clip plane max;
*
* Read/Write Args
* ---------------
*
* \param ray_depth: maximum depth allowed for r_co, elements deeper than this value will be ignored.
- * \param dist_to_ray_sq: Real distance (3D) or Tangent (view cone radius at distance 1.0) squared.
- * resulting of the function #dist_px_to_dist3d_or_tangent.
- *
- * \param dist_px: Pixel distance to element,
- * note that this will eventually be replaced entirely by \a dist_to_ray_sq.
+ * \param dist_px: Maximum threshold distance (in pixels).
*
* Output Args
* -----------
@@ -1698,8 +1854,9 @@ static bool snapObjectsRay(
const unsigned short snap_to, const SnapSelect snap_select,
const bool use_object_edit_cage, const float mval[2],
const float ray_origin[3], const float ray_start[3], const float ray_normal[3],
+ const float depth_range[2],
/* read/write args */
- float *ray_depth, float *dist_to_ray_sq, float *dist_px,
+ float *ray_depth, float *dist_px,
/* return args */
float r_loc[3], float r_no[3], int *r_index,
Object **r_ob, float r_obmat[4][4],
@@ -1707,14 +1864,6 @@ static bool snapObjectsRay(
{
bool retval = false;
- float dvec[3];
- sub_v3_v3v3(dvec, ray_start, ray_origin);
-
- const float ray_depth_range[2] = {
- dot_v3v3(dvec, ray_normal),
- *ray_depth,
- };
-
unsigned int ob_index = 0;
Object *obedit = use_object_edit_cage ? sctx->scene->obedit : NULL;
@@ -1730,8 +1879,8 @@ static bool snapObjectsRay(
retval |= snapObject(
sctx, ob, ob->obmat, ob_index++,
false, snap_to, mval,
- ray_origin, ray_start, ray_normal, ray_depth_range,
- ray_depth, dist_to_ray_sq, dist_px,
+ ray_origin, ray_start, ray_normal, depth_range,
+ ray_depth, dist_px,
r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
}
@@ -1766,8 +1915,8 @@ static bool snapObjectsRay(
retval |= snapObject(
sctx, dupli_snap, dupli_ob->mat, ob_index++,
use_obedit_dupli, snap_to, mval,
- ray_origin, ray_start, ray_normal, ray_depth_range,
- ray_depth, dist_to_ray_sq, dist_px,
+ ray_origin, ray_start, ray_normal, depth_range,
+ ray_depth, dist_px,
r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
}
@@ -1780,8 +1929,8 @@ static bool snapObjectsRay(
retval |= snapObject(
sctx, ob_snap, ob->obmat, ob_index++,
use_obedit, snap_to, mval,
- ray_origin, ray_start, ray_normal, ray_depth_range,
- ray_depth, dist_to_ray_sq, dist_px,
+ ray_origin, ray_start, ray_normal, depth_range,
+ ray_depth, dist_px,
r_loc, r_no, r_index, r_ob, r_obmat, r_hit_list);
}
}
@@ -1888,14 +2037,12 @@ bool ED_transform_snap_object_project_ray_ex(
float r_loc[3], float r_no[3], int *r_index,
Object **r_ob, float r_obmat[4][4])
{
- float dist_to_ray_sq = 0.0f;
-
+ const float depth_range[2] = {0.0f, FLT_MAX};
return snapObjectsRay(
sctx,
- snap_to, params->snap_select, params->use_object_edit_cage,
- NULL,
- ray_start, ray_start, ray_normal,
- ray_depth, &dist_to_ray_sq, NULL,
+ snap_to, params->snap_select, params->use_object_edit_cage, NULL,
+ ray_start, ray_start, ray_normal, depth_range,
+ ray_depth, NULL,
r_loc, r_no, r_index, r_ob, r_obmat, NULL);
}
@@ -1914,8 +2061,7 @@ bool ED_transform_snap_object_project_ray_all(
float ray_depth, bool sort,
ListBase *r_hit_list)
{
- float dist_to_ray_sq = 0.0f;
-
+ const float depth_range[2] = {0.0f, FLT_MAX};
if (ray_depth == -1.0f) {
ray_depth = BVH_RAYCAST_DIST_MAX;
}
@@ -1927,8 +2073,8 @@ bool ED_transform_snap_object_project_ray_all(
bool retval = snapObjectsRay(
sctx,
snap_to, params->snap_select, params->use_object_edit_cage, NULL,
- ray_start, ray_start, ray_normal,
- &ray_depth, &dist_to_ray_sq, NULL,
+ ray_start, ray_start, ray_normal, depth_range,
+ &ray_depth, NULL,
NULL, NULL, NULL, NULL, NULL,
r_hit_list);
@@ -1983,11 +2129,6 @@ bool ED_transform_snap_object_project_ray(
ray_depth = &ray_depth_fallback;
}
- float no_fallback[3];
- if (r_no == NULL) {
- r_no = no_fallback;
- }
-
return transform_snap_context_project_ray_impl(
sctx,
params,
@@ -2006,11 +2147,6 @@ static bool transform_snap_context_project_view3d_mixed_impl(
float ray_depth = BVH_RAYCAST_DIST_MAX;
bool is_hit = false;
- float r_no_dummy[3];
- if (r_no == NULL) {
- r_no = r_no_dummy;
- }
-
const int elem_type[3] = {SCE_SNAP_MODE_VERTEX, SCE_SNAP_MODE_EDGE, SCE_SNAP_MODE_FACE};
BLI_assert(snap_to_flag != 0);
@@ -2037,21 +2173,6 @@ static bool transform_snap_context_project_view3d_mixed_impl(
}
/**
- * From a threshold (maximum distance to snap in pixels) returns:
- *
- * - The *real* distance (3D) if you are in orthographic-view.
- * - The *tangent* (view cone radius at distance 1.0) if you are in perspective-view.
- */
-static float dist_px_to_dist3d_or_tangent(const ARegion *ar, const float dist_px)
-{
- const RegionView3D *rv3d = ar->regiondata;
- if (ar->winx >= ar->winy)
- return 2 * (dist_px / ar->winx) / rv3d->winmat[0][0];
- else
- return 2 * (dist_px / ar->winy) / rv3d->winmat[1][1];
-}
-
-/**
* Convenience function for performing snapping.
*
* Given a 2D region value, snap to vert/edge/face.
@@ -2087,47 +2208,36 @@ bool ED_transform_snap_object_project_view3d_ex(
float *ray_depth,
float r_loc[3], float r_no[3], int *r_index)
{
- float ray_start[3], ray_normal[3], ray_origin[3];
+ float ray_origin[3], ray_start[3], ray_normal[3], depth_range[2], ray_end[3];
- float ray_depth_fallback;
- if (ray_depth == NULL) {
- ray_depth_fallback = BVH_RAYCAST_DIST_MAX;
- ray_depth = &ray_depth_fallback;
- }
+ const ARegion *ar = sctx->v3d_data.ar;
+ const RegionView3D *rv3d = ar->regiondata;
- if (!ED_view3d_win_to_ray_ex(
- sctx->v3d_data.ar, sctx->v3d_data.v3d,
- mval, ray_origin, ray_normal, ray_start, true))
- {
+ ED_view3d_win_to_origin(ar, mval, ray_origin);
+ ED_view3d_win_to_vector(ar, mval, ray_normal);
+
+ ED_view3d_clip_range_get(
+ sctx->v3d_data.v3d, sctx->v3d_data.ar->regiondata,
+ &depth_range[0], &depth_range[1], false);
+
+ madd_v3_v3v3fl(ray_start, ray_origin, ray_normal, depth_range[0]);
+ madd_v3_v3v3fl(ray_end, ray_origin, ray_normal, depth_range[1]);
+
+ if (!ED_view3d_clip_segment(rv3d, ray_start, ray_end)) {
return false;
}
- float dist_to_ray_sq;
- {
- float radius = dist_px_to_dist3d_or_tangent(sctx->v3d_data.ar, *dist_px);
- /**
- * Workaround to use of cone (Instead of project the radius on view plane):
- * In perspective view, the radius of the cone may decrease depending on the ray direction.
- * This is more evident with small values of the `Viewport lens angle`.
- * The threshold becomes distorted that way.
- */
- RegionView3D *rv3d = sctx->v3d_data.ar->regiondata;
- if (rv3d->is_persp) {
- float view_dir[3];
- negate_v3_v3(view_dir, rv3d->viewinv[2]);
- normalize_v3(view_dir);
- radius *= dot_v3v3(ray_normal, view_dir);
- }
-
- dist_to_ray_sq = SQUARE(radius);
+ float ray_depth_fallback;
+ if (ray_depth == NULL) {
+ ray_depth_fallback = BVH_RAYCAST_DIST_MAX;
+ ray_depth = &ray_depth_fallback;
}
return snapObjectsRay(
sctx,
snap_to, params->snap_select, params->use_object_edit_cage,
- mval,
- ray_origin, ray_start, ray_normal,
- ray_depth, &dist_to_ray_sq, dist_px,
+ mval, ray_origin, ray_start, ray_normal, depth_range,
+ ray_depth, dist_px,
r_loc, r_no, r_index, NULL, NULL, NULL);
}
diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c
index e2f60955c81..1f1a778cac7 100644
--- a/source/blender/editors/util/ed_util.c
+++ b/source/blender/editors/util/ed_util.c
@@ -105,7 +105,7 @@ void ED_editors_init(bContext *C)
ob->mode = OB_MODE_OBJECT;
data = ob->data;
- if (ob == obact && !ob->id.lib && !(data && data->lib))
+ if (ob == obact && !ID_IS_LINKED_DATABLOCK(ob) && !(data && ID_IS_LINKED_DATABLOCK(data)))
ED_object_toggle_modes(C, mode);
}
}
diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c
index b3190ef784f..bdfff123aa4 100644
--- a/source/blender/editors/uvedit/uvedit_parametrizer.c
+++ b/source/blender/editors/uvedit/uvedit_parametrizer.c
@@ -743,7 +743,7 @@ static PVert *p_vert_add(PHandle *handle, PHashKey key, const float co[3], PEdge
/* Sanity check, a single nan/inf point causes the entire result to be invalid.
* Note that values within the calculation may _become_ non-finite,
- * so the rest of the code still needs to take this possability into account. */
+ * so the rest of the code still needs to take this possibility into account. */
for (int i = 0; i < 3; i++) {
if (UNLIKELY(!isfinite(v->co[i]))) {
v->co[i] = 0.0f;
@@ -2804,7 +2804,7 @@ static PBool p_chart_abf_solve(PChart *chart)
static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2)
{
- if (pin1 == pin2) {
+ if (!*pin1 || !*pin2 || *pin1 == *pin2) {
/* degenerate case */
PFace *f = chart->faces;
*pin1 = f->edge->vert;
@@ -3051,7 +3051,7 @@ static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf)
p_chart_boundaries(chart, NULL, &outer);
/* outer can be NULL with non-finite coords. */
- if (outer && !p_chart_symmetry_pins(chart, outer, &pin1, &pin2)) {
+ if (!(outer && p_chart_symmetry_pins(chart, outer, &pin1, &pin2))) {
p_chart_extrema_verts(chart, &pin1, &pin2);
}