diff options
author | Bastien Montagne <montagne29@wanadoo.fr> | 2018-08-01 15:16:41 +0300 |
---|---|---|
committer | Bastien Montagne <montagne29@wanadoo.fr> | 2018-08-01 15:16:41 +0300 |
commit | 936d1cef6e6124a8f8f3ce50182afa2e1c947c6f (patch) | |
tree | 429a7503472bd8fe38ceb062086e447cb3a83423 /source/blender/editors | |
parent | 4e6bcd10e559ab541df53a3d7b645faac1a7fe27 (diff) | |
parent | ddd62f1b1063d69df5171c7b3457ffcffcc6b0e4 (diff) |
Merge branch 'blender2.8' into soc-2018-bevel
Conflicts:
release/scripts/addons
release/scripts/startup/bl_ui/space_view3d_toolbar.py
source/blender/editors/space_outliner/outliner_draw.c
Diffstat (limited to 'source/blender/editors')
128 files changed, 17349 insertions, 4013 deletions
diff --git a/source/blender/editors/animation/anim_channels_defines.c b/source/blender/editors/animation/anim_channels_defines.c index 3c10cda6456..79b7c7988fd 100644 --- a/source/blender/editors/animation/anim_channels_defines.c +++ b/source/blender/editors/animation/anim_channels_defines.c @@ -64,6 +64,7 @@ #include "BKE_animsys.h" #include "BKE_curve.h" +#include "BKE_gpencil.h" #include "BKE_key.h" #include "BKE_main.h" #include "BKE_nla.h" @@ -661,6 +662,8 @@ static int acf_object_icon(bAnimListElem *ale) return ICON_OUTLINER_OB_SURFACE; case OB_EMPTY: return ICON_OUTLINER_OB_EMPTY; + case OB_GPENCIL: + return ICON_OUTLINER_OB_GREASEPENCIL; default: return ICON_OBJECT_DATA; } @@ -4048,8 +4051,16 @@ static void achannel_setting_flush_widget_cb(bContext *C, void *ale_npoin, void return; } - if (ale_setting->type == ANIMTYPE_GPLAYER) + if (ale_setting->type == ANIMTYPE_GPLAYER) { + /* draw cache updates for settings that affect the visible strokes */ + if (setting == ACHANNEL_SETTING_VISIBLE) { + bGPdata *gpd = (bGPdata *)ale_setting->id; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + + /* UI updates */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); + } /* tag copy-on-write flushing (so that the settings will have an effect) */ if (ale_setting->id) { diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index d768be49ad4..3f22ac6fa3a 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -1727,8 +1727,7 @@ static int animchannels_delete_exec(bContext *C, wmOperator *UNUSED(op)) bGPDlayer *gpl = (bGPDlayer *)ale->data; /* try to delete the layer's data and the layer itself */ - BKE_gpencil_free_frames(gpl); - BLI_freelinkN(&gpd->layers, gpl); + BKE_gpencil_layer_delete(gpd, gpl); break; } case ANIMTYPE_MASKLAYER: diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index adb5a10c19d..898c8b6464a 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -435,6 +435,16 @@ void ANIM_animdata_update(bAnimContext *ac, ListBase *anim_data) ANIM_list_elem_update(ac->bmain, ac->scene, ale); } } + else if (ale->update) { +#if 0 + if (G.debug & G_DEBUG) { + printf("%s: Unhandled animchannel updates (%d) for type=%d (%p)\n", + __func__, ale->update, ale->type, ale->data); + } +#endif + /* Prevent crashes in cases where it can't be handled */ + ale->update = 0; + } BLI_assert(ale->update == 0); } diff --git a/source/blender/editors/animation/anim_draw.c b/source/blender/editors/animation/anim_draw.c index 780e984f870..05ea3fd6314 100644 --- a/source/blender/editors/animation/anim_draw.c +++ b/source/blender/editors/animation/anim_draw.c @@ -552,11 +552,11 @@ static bool find_prev_next_keyframes(struct bContext *C, int *nextfra, int *prev /* populate tree with keyframe nodes */ scene_to_keylist(&ads, scene, &keys, NULL); - gpencil_to_keylist(&ads, scene->gpd, &keys); + gpencil_to_keylist(&ads, scene->gpd, &keys, false); if (ob) { ob_to_keylist(&ads, ob, &keys, NULL); - gpencil_to_keylist(&ads, ob->gpd, &keys); + gpencil_to_keylist(&ads, ob->data, &keys, false); } if (mask) { diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index c59d24bbdf8..54590c5f66c 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -72,6 +72,7 @@ #include "DNA_speaker_types.h" #include "DNA_world_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_userdef_types.h" #include "DNA_layer_types.h" @@ -1660,7 +1661,7 @@ static size_t animdata_filter_gpencil_data(ListBase *anim_data, bDopeSheet *ads, */ if (filter_mode & ANIMFILTER_ANIMDATA) { /* just add GPD as a channel - this will add everything needed */ - ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, NULL); + ANIMCHANNEL_NEW_CHANNEL(gpd, ANIMTYPE_GPDATABLOCK, gpd); } else { ListBase tmp_data = {NULL, NULL}; @@ -1711,7 +1712,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, ListBase *anim_data, voi /* Objects in the scene */ for (base = view_layer->object_bases.first; base; base = base->next) { /* Only consider this object if it has got some GP data (saving on all the other tests) */ - if (base->object && base->object->gpd) { + if (base->object && (base->object->type == OB_GPENCIL)) { Object *ob = base->object; /* firstly, check if object can be included, by the following factors: @@ -1748,7 +1749,7 @@ static size_t animdata_filter_gpencil(bAnimContext *ac, ListBase *anim_data, voi /* finally, include this object's grease pencil datablock */ /* XXX: Should we store these under expanders per item? */ - items += animdata_filter_gpencil_data(anim_data, ads, ob->gpd, filter_mode); + items += animdata_filter_gpencil_data(anim_data, ads, ob->data, filter_mode); } } } @@ -2613,8 +2614,10 @@ static size_t animdata_filter_dopesheet_ob(bAnimContext *ac, ListBase *anim_data } /* grease pencil */ - if ((ob->gpd) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) { - tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, ob->gpd, filter_mode); + if ((ob->type == OB_GPENCIL) && + (ob->data) && !(ads->filterflag & ADS_FILTER_NOGPENCIL)) + { + tmp_items += animdata_filter_ds_gpencil(ac, &tmp_data, ads, ob->data, filter_mode); } } END_ANIMFILTER_SUBCHANNELS; diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 5f6b299c617..45fe7233f94 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -45,6 +45,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "UI_view2d.h" @@ -98,8 +99,13 @@ static void change_frame_apply(bContext *C, wmOperator *op) float frame = RNA_float_get(op->ptr, "frame"); bool do_snap = RNA_boolean_get(op->ptr, "snap"); - if (do_snap && CTX_wm_space_seq(C)) { - frame = BKE_sequencer_find_next_prev_edit(scene, frame, SEQ_SIDE_BOTH, true, false, false); + if (do_snap) { + if (CTX_wm_space_seq(C)) { + frame = BKE_sequencer_find_next_prev_edit(scene, frame, SEQ_SIDE_BOTH, true, false, false); + } + else { + frame = BKE_scene_frame_snap_by_seconds(scene, 1.0, frame); + } } /* set the new frame number */ diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index 30130ce4dac..a8b63e01ac1 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -48,6 +48,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_mask_types.h" #include "BKE_fcurve.h" @@ -783,7 +784,7 @@ void draw_gpencil_channel(View2D *v2d, bDopeSheet *ads, bGPdata *gpd, float ypos BLI_dlrbTree_init(&keys); - gpencil_to_keylist(ads, gpd, &keys); + gpencil_to_keylist(ads, gpd, &keys, false); BLI_dlrbTree_linkedlist_sync(&keys); @@ -1019,7 +1020,7 @@ void action_to_keylist(AnimData *adt, bAction *act, DLRBT_Tree *keys, DLRBT_Tree } -void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys) +void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys, const bool active) { bGPDlayer *gpl; @@ -1027,7 +1028,9 @@ void gpencil_to_keylist(bDopeSheet *ads, bGPdata *gpd, DLRBT_Tree *keys) /* for now, just aggregate out all the frames, but only for visible layers */ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { if ((gpl->flag & GP_LAYER_HIDE) == 0) { - gpl_to_keylist(ads, gpl, keys); + if ((!active) || ((active) && (gpl->flag & GP_LAYER_SELECT))) { + gpl_to_keylist(ads, gpl, keys); + } } } } diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c index a6ed6643257..b98feac2384 100644 --- a/source/blender/editors/animation/keyframes_general.c +++ b/source/blender/editors/animation/keyframes_general.c @@ -183,7 +183,8 @@ void duplicate_fcurve_keys(FCurve *fcu) /* Various Tools */ /* Basic F-Curve 'cleanup' function that removes 'double points' and unnecessary keyframes on linear-segments only - * optionally clears up curve if one keyframe with default value remains */ + * optionally clears up curve if one keyframe with default value remains + */ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, bool cleardefault) { FCurve *fcu = (FCurve *)ale->key_data; @@ -206,7 +207,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo /* now insert first keyframe, as it should be ok */ bezt = old_bezts; - insert_vert_fcurve(fcu, bezt->vec[1][0], bezt->vec[1][1], BEZKEYTYPE(bezt), 0); + insert_bezt_fcurve(fcu, bezt, 0); if (!(bezt->f2 & SELECT)) { lastb = fcu->bezt; lastb->f1 = lastb->f2 = lastb->f3 = 0; @@ -235,7 +236,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo cur[0] = bezt->vec[1][0]; cur[1] = bezt->vec[1][1]; if (!(bezt->f2 & SELECT)) { - insert_vert_fcurve(fcu, cur[0], cur[1], BEZKEYTYPE(bezt), 0); + insert_bezt_fcurve(fcu, bezt, 0); lastb = (fcu->bezt + (fcu->totvert - 1)); lastb->f1 = lastb->f2 = lastb->f3 = 0; continue; @@ -254,7 +255,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo if (cur[1] > next[1]) { if (IS_EQT(cur[1], prev[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], BEZKEYTYPE(bezt), 0); + insert_bezt_fcurve(fcu, bezt, 0); } } } @@ -262,7 +263,7 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo /* only add if values are a considerable distance apart */ if (IS_EQT(cur[1], prev[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], BEZKEYTYPE(bezt), 0); + insert_bezt_fcurve(fcu, bezt, 0); } } } @@ -271,19 +272,19 @@ void clean_fcurve(struct bAnimContext *ac, bAnimListElem *ale, float thresh, boo if (beztn) { /* does current have same value as previous and next? */ if (IS_EQT(cur[1], prev[1], thresh) == 0) { - /* add new keyframe*/ - insert_vert_fcurve(fcu, cur[0], cur[1], BEZKEYTYPE(bezt), 0); + /* add new keyframe */ + insert_bezt_fcurve(fcu, bezt, 0); } else if (IS_EQT(cur[1], next[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], BEZKEYTYPE(bezt), 0); + insert_bezt_fcurve(fcu, bezt, 0); } } else { /* add if value doesn't equal that of previous */ if (IS_EQT(cur[1], prev[1], thresh) == 0) { /* add new keyframe */ - insert_vert_fcurve(fcu, cur[0], cur[1], BEZKEYTYPE(bezt), 0); + insert_bezt_fcurve(fcu, bezt, 0); } } } diff --git a/source/blender/editors/armature/armature_utils.c b/source/blender/editors/armature/armature_utils.c index c93bfb5d8c3..2b28b76bcf6 100644 --- a/source/blender/editors/armature/armature_utils.c +++ b/source/blender/editors/armature/armature_utils.c @@ -681,7 +681,7 @@ void ED_armature_from_edit(Main *bmain, bArmature *arm) /* so all users of this armature should get rebuilt */ for (obt = bmain->object.first; obt; obt = obt->id.next) { if (obt->data == arm) { - BKE_pose_rebuild(bmain, obt, arm); + BKE_pose_rebuild(bmain, obt, arm, true); } } diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index a9ba8c405ba..1dfb9ec984a 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -287,7 +287,8 @@ void POSE_OT_paths_calculate(wmOperatorType *ot) RNA_def_int(ot->srna, "end_frame", 250, MINAFRAME, MAXFRAME, "End", "Last frame to calculate bone paths on", MINFRAME, MAXFRAME / 2.0); - RNA_def_enum(ot->srna, "bake_location", rna_enum_motionpath_bake_location_items, 0, + RNA_def_enum(ot->srna, "bake_location", rna_enum_motionpath_bake_location_items, + MOTIONPATH_BAKE_HEADS, "Bake Location", "Which point on the bones is used when calculating paths"); } @@ -364,6 +365,9 @@ static void ED_pose_clear_paths(Object *ob, bool only_selected) /* if nothing was skipped, there should be no paths left! */ if (skipped == false) ob->pose->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS; + + /* tag armature object for copy on write - so removed paths don't still show */ + DEG_id_tag_update(&ob->id, DEG_TAG_COPY_ON_WRITE); } /* operator callback - wrapper for the backend function */ diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 1d7a9ac46c7..f7d54e3bc4f 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -6260,12 +6260,12 @@ static int match_texture_space_exec(bContext *C, wmOperator *UNUSED(op)) float min[3], max[3], size[3], loc[3]; int a; - if (object->curve_cache == NULL) { + if (object->runtime.curve_cache == NULL) { BKE_displist_make_curveTypes(depsgraph, scene, object, false); } INIT_MINMAX(min, max); - BKE_displist_minmax(&object->curve_cache->disp, min, max); + BKE_displist_minmax(&object->runtime.curve_cache->disp, min, max); mid_v3_v3v3(loc, min, max); diff --git a/source/blender/editors/datafiles/CMakeLists.txt b/source/blender/editors/datafiles/CMakeLists.txt index 0e09ef6f583..0698282c4e5 100644 --- a/source/blender/editors/datafiles/CMakeLists.txt +++ b/source/blender/editors/datafiles/CMakeLists.txt @@ -536,6 +536,16 @@ set(ICON_NAMES # This section is maintained by the updating script, keep BEGIN/END comments. set_property(GLOBAL PROPERTY ICON_GEOM_NAMES # BEGIN ICON_GEOM_NAMES + brush.gpencil.draw.eraser_hard + brush.gpencil.draw.eraser_soft + brush.gpencil.draw.eraser_stroke + brush.gpencil.draw_block + brush.gpencil.draw_fill + brush.gpencil.draw_ink + brush.gpencil.draw_marker + brush.gpencil.draw_noise + brush.gpencil.draw_pen + brush.gpencil.draw_pencil brush.paint_texture.airbrush brush.paint_texture.clone brush.paint_texture.draw @@ -583,6 +593,24 @@ set_property(GLOBAL PROPERTY ICON_GEOM_NAMES ops.generic.select_border ops.generic.select_circle ops.generic.select_lasso + ops.gpencil.draw + ops.gpencil.draw.eraser + ops.gpencil.draw.line + ops.gpencil.draw.poly + ops.gpencil.edit_bend + ops.gpencil.edit_mirror + ops.gpencil.edit_shear + ops.gpencil.edit_to_sphere + ops.gpencil.sculpt_clone + ops.gpencil.sculpt_grab + ops.gpencil.sculpt_pinch + ops.gpencil.sculpt_push + ops.gpencil.sculpt_randomize + ops.gpencil.sculpt_smooth + ops.gpencil.sculpt_strength + ops.gpencil.sculpt_thickness + ops.gpencil.sculpt_twist + ops.gpencil.sculpt_weight ops.mesh.bevel ops.mesh.bisect ops.mesh.dupli_extrude_cursor @@ -634,6 +662,7 @@ if(WITH_BLENDER) # blends data_to_c_simple(../../../../release/datafiles/preview.blend SRC) data_to_c_simple(../../../../release/datafiles/preview_cycles.blend SRC) + data_to_c_simple(../../../../release/datafiles/preview_grease_pencil.blend SRC) # images data_to_c_simple(../../../../release/datafiles/splash.png SRC) @@ -689,6 +718,29 @@ if(WITH_BLENDER) data_to_c_simple(../../../../release/datafiles/brushicons/twist.png SRC) data_to_c_simple(../../../../release/datafiles/brushicons/vertexdraw.png SRC) + # grease pencil sculpt + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_smooth.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_thickness.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_strength.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_grab.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_push.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_twist.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_pinch.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_randomize.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_clone.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_weight.png SRC) + + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_pencil.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_pen.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_ink.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_inknoise.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_block.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_marker.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_fill.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_soft.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_hard.png SRC) + data_to_c_simple(../../../../release/datafiles/brushicons/gp_brush_erase_stroke.png SRC) + endif() data_to_c_simple(../../../../release/datafiles/startup.blend SRC) diff --git a/source/blender/editors/gpencil/CMakeLists.txt b/source/blender/editors/gpencil/CMakeLists.txt index 587c25031ab..f9f196f6634 100644 --- a/source/blender/editors/gpencil/CMakeLists.txt +++ b/source/blender/editors/gpencil/CMakeLists.txt @@ -39,18 +39,24 @@ set(INC_SYS ) set(SRC + annotate_draw.c + annotate_paint.c drawgpencil.c editaction_gpencil.c + gpencil_add_monkey.c gpencil_brush.c gpencil_convert.c gpencil_data.c gpencil_edit.c gpencil_interpolate.c + gpencil_primitive.c gpencil_ops.c gpencil_paint.c + gpencil_fill.c gpencil_select.c gpencil_undo.c gpencil_utils.c + gpencil_old.c gpencil_intern.h ) diff --git a/source/blender/editors/gpencil/annotate_draw.c b/source/blender/editors/gpencil/annotate_draw.c new file mode 100644 index 00000000000..4f6e2004197 --- /dev/null +++ b/source/blender/editors/gpencil/annotate_draw.c @@ -0,0 +1,1066 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung, Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/annotate_draw.c + * \ingroup edgpencil + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> +#include <float.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_sys_types.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_polyfill_2d.h" + +#include "BLF_api.h" +#include "BLT_translation.h" + +#include "DNA_gpencil_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#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" +#include "BKE_gpencil.h" + +#include "WM_api.h" + +#include "BIF_glutil.h" + +#include "GPU_immediate.h" +#include "GPU_draw.h" +#include "GPU_state.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_space_api.h" + +#include "UI_interface_icons.h" +#include "UI_resources.h" + +/* ************************************************** */ +/* GREASE PENCIL DRAWING */ + +/* ----- General Defines ------ */ +/* flags for sflag */ +typedef enum eDrawStrokeFlags { + GP_DRAWDATA_NOSTATUS = (1 << 0), /* don't draw status info */ + GP_DRAWDATA_ONLY3D = (1 << 1), /* only draw 3d-strokes */ + GP_DRAWDATA_ONLYV2D = (1 << 2), /* only draw 'canvas' strokes */ + GP_DRAWDATA_ONLYI2D = (1 << 3), /* only draw 'image' strokes */ + GP_DRAWDATA_IEDITHACK = (1 << 4), /* special hack for drawing strokes in Image Editor (weird coordinates) */ + GP_DRAWDATA_NO_XRAY = (1 << 5), /* don't draw xray in 3D view (which is default) */ + GP_DRAWDATA_NO_ONIONS = (1 << 6), /* no onionskins should be drawn (for animation playback) */ +} eDrawStrokeFlags; + + +/* ----- Tool Buffer Drawing ------ */ + +/* draw stroke defined in buffer (simple ogl lines/points for now, as dotted lines) */ +static void gp_draw_stroke_buffer(const tGPspoint *points, int totpoints, short thickness, + short dflag, short sflag, float ink[4]) +{ + int draw_points = 0; + + /* error checking */ + if ((points == NULL) || (totpoints <= 0)) + return; + + /* check if buffer can be drawn */ + if (dflag & (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_ONLYV2D)) + return; + + if (sflag & GP_STROKE_ERASER) { + /* don't draw stroke at all! */ + return; + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + + const tGPspoint *pt = points; + + if (totpoints == 1) { + /* if drawing a single point, draw it larger */ + GPU_point_size((float)(thickness + 2) * points->pressure); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + immUniformColor3fvAlpha(ink, ink[3]); + immBegin(GPU_PRIM_POINTS, 1); + immVertex2iv(pos, &pt->x); + } + else { + float oldpressure = points[0].pressure; + + /* draw stroke curve */ + GPU_line_width(max_ff(oldpressure * thickness, 1.0)); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + + /* TODO: implement this with a geometry shader to draw one continuous tapered stroke */ + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints); + + for (int i = 0; i < totpoints; i++, pt++) { + /* 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) + */ + if (fabsf(pt->pressure - oldpressure) > 0.2f) { + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + immVertex2iv(pos, &(pt - 1)->x); + } + + immEnd(); + draw_points = 0; + + GPU_line_width(max_ff(pt->pressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1); + + /* need to roll-back one point to ensure that there are no gaps in the stroke */ + if (i != 0) { + immVertex2iv(pos, &(pt - 1)->x); + draw_points++; + } + + oldpressure = pt->pressure; /* reset our threshold */ + } + + /* now the point we want */ + immVertex2iv(pos, &pt->x); + draw_points++; + } + /* need to have 2 points to avoid immEnd assert error */ + if (draw_points < 2) { + immVertex2iv(pos, &(pt - 1)->x); + } + } + + immEnd(); + immUnbindProgram(); +} + +/* --------- 2D Stroke Drawing Helpers --------- */ +/* change in parameter list */ +static void gp_calc_2d_stroke_fxy(const 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[0]; + r_co[1] = pt[1]; + } + else if (sflag & GP_STROKE_2DIMAGE) { + 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[0] / 100 * winx) + offsx; + const float y = (float)(pt[1] / 100 * winy) + offsy; + + r_co[0] = x; + r_co[1] = y; + } +} + +/* ----- Existing Strokes Drawing (3D and Point) ------ */ + +/* draw a given stroke - just a single dot (only one point) */ +static void gp_draw_stroke_point( + const bGPDspoint *points, short thickness, short UNUSED(dflag), short sflag, + int offsx, int offsy, int winx, int winy, const float ink[4]) +{ + const bGPDspoint *pt = points; + + /* get final position using parent matrix */ + float fpt[3]; + copy_v3_v3(fpt, &pt->x); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (sflag & GP_STROKE_3DSPACE) { + immBindBuiltinProgram(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + } + else { + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_AA); + + /* get 2D coordinates of point */ + float co[3] = { 0.0f }; + gp_calc_2d_stroke_fxy(fpt, sflag, offsx, offsy, winx, winy, co); + copy_v3_v3(fpt, co); + } + + /* set color */ + immUniformColor3fvAlpha(ink, ink[3]); + + /* set point thickness (since there's only one of these) */ + immUniform1f("size", (float)(thickness + 2) * pt->pressure); + + immBegin(GPU_PRIM_POINTS, 1); + immVertex3fv(pos, fpt); + immEnd(); + + immUnbindProgram(); +} + +/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ +static void gp_draw_stroke_3d(const bGPDspoint *points, int totpoints, short thickness, bool UNUSED(debug), + short UNUSED(sflag), const float ink[4], bool cyclic) +{ + float curpressure = points[0].pressure; + float cyclic_fpt[3]; + int draw_points = 0; + + /* if cyclic needs one vertex more */ + int cyclic_add = 0; + if (cyclic) { + cyclic_add++; + } + + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + + /* TODO: implement this with a geometry shader to draw one continuous tapered stroke */ + + /* draw stroke curve */ + GPU_line_width(max_ff(curpressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + const bGPDspoint *pt = points; + for (int i = 0; i < totpoints; i++, pt++) { + /* 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. + */ + if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) { + /* if the pressure changes before get at least 2 vertices, need to repeat last point to avoid assert in immEnd() */ + if (draw_points < 2) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + } + immEnd(); + draw_points = 0; + + curpressure = pt->pressure; + GPU_line_width(max_ff(curpressure * thickness, 1.0f)); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1 + cyclic_add); + + /* need to roll-back one point to ensure that there are no gaps in the stroke */ + if (i != 0) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + draw_points++; + } + } + + /* now the point we want */ + immVertex3fv(pos, &pt->x); + draw_points++; + + if (cyclic && i == 0) { + /* save first point to use in cyclic */ + copy_v3_v3(cyclic_fpt, &pt->x); + } + } + + if (cyclic) { + /* draw line to first point to complete the cycle */ + immVertex3fv(pos, cyclic_fpt); + draw_points++; + } + + /* if less of two points, need to repeat last point to avoid assert in immEnd() */ + if (draw_points < 2) { + const bGPDspoint *pt2 = pt - 1; + immVertex3fv(pos, &pt2->x); + } + + immEnd(); + immUnbindProgram(); +} + +/* ----- Fancy 2D-Stroke Drawing ------ */ + +/* draw a given stroke in 2d */ +static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, + bool UNUSED(debug), int offsx, int offsy, int winx, int winy, const float ink[4]) +{ + /* otherwise thickness is twice that of the 3D view */ + float thickness = (float)thickness_s * 0.5f; + + /* strokes in Image Editor need a scale factor, since units there are not pixels! */ + float scalefac = 1.0f; + if ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) { + scalefac = 0.001f; + } + + /* TODO: fancy++ with the magic of shaders */ + + /* tessellation code - draw stroke as series of connected quads (triangle strips in fact) with connection + * edges rotated to minimize shrinking artifacts, and rounded endcaps + */ + { + const bGPDspoint *pt1, *pt2; + float s0[2], s1[2]; /* segment 'center' points */ + float pm[2]; /* normal from previous segment. */ + int i; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformColor3fvAlpha(ink, ink[3]); + immBegin(GPU_PRIM_TRI_STRIP, totpoints * 2 + 4); + + /* get x and y coordinates from first point */ + gp_calc_2d_stroke_fxy(&points->x, sflag, offsx, offsy, winx, winy, s0); + + for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { + float t0[2], t1[2]; /* tessellated coordinates */ + 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 point2 (point1 has already been computed in previous iteration). */ + gp_calc_2d_stroke_fxy(&pt2->x, sflag, offsx, offsy, winx, winy, s1); + + /* calculate gradient and normal - 'angle'=(ny/nx) */ + m1[1] = s1[1] - s0[1]; + m1[0] = s1[0] - s0[0]; + normalize_v2(m1); + m2[1] = -m1[0]; + m2[0] = m1[1]; + + /* always use pressure from first point here */ + pthick = (pt1->pressure * thickness * scalefac); + + /* if the first segment, start of segment is segment's normal */ + if (i == 0) { + /* draw start cap first + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s0[0] - (m1[0] * pthick * 0.75f); + sc[1] = s0[1] - (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* First two points of cap. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + + /* calculate points for start of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of start cap (and first two points of first segment). */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + /* if not the first segment, use bisector of angle between segments */ + else { + float mb[2]; /* bisector normal */ + float athick, dfac; /* actual thickness, difference between thicknesses */ + + /* calculate gradient of bisector (as average of normals) */ + mb[0] = (pm[0] + m2[0]) / 2; + mb[1] = (pm[1] + m2[1]) / 2; + normalize_v2(mb); + + /* calculate gradient to apply + * - as basis, use just pthick * bisector gradient + * - if cross-section not as thick as it should be, add extra padding to fix it + */ + mt[0] = mb[0] * pthick; + mt[1] = mb[1] * pthick; + athick = len_v2(mt); + dfac = pthick - (athick * 2); + + if (((athick * 2.0f) < pthick) && (IS_EQF(athick, pthick) == 0)) { + mt[0] += (mb[0] * dfac); + mt[1] += (mb[1] * dfac); + } + + /* calculate points for start of segment */ + t0[0] = s0[0] - mt[0]; + t0[1] = s0[1] - mt[1]; + t1[0] = s0[0] + mt[0]; + t1[1] = s0[1] + mt[1]; + + /* Last two points of previous segment, and first two points of current segment. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + + /* if last segment, also draw end of segment (defined as segment's normal) */ + if (i == totpoints - 2) { + /* for once, we use second point's pressure (otherwise it won't be drawn) */ + pthick = (pt2->pressure * thickness * scalefac); + + /* calculate points for end of segment */ + mt[0] = m2[0] * pthick; + mt[1] = m2[1] * pthick; + + t0[0] = s1[0] - mt[0]; + t0[1] = s1[1] - mt[1]; + t1[0] = s1[0] + mt[0]; + t1[1] = s1[1] + mt[1]; + + /* Last two points of last segment (and first two points of end cap). */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + + /* draw end cap as last step + * - make points slightly closer to center (about halfway across) + */ + mt[0] = m2[0] * pthick * 0.5f; + mt[1] = m2[1] * pthick * 0.5f; + sc[0] = s1[0] + (m1[0] * pthick * 0.75f); + sc[1] = s1[1] + (m1[1] * pthick * 0.75f); + + t0[0] = sc[0] - mt[0]; + t0[1] = sc[1] - mt[1]; + t1[0] = sc[0] + mt[0]; + t1[1] = sc[1] + mt[1]; + + /* Last two points of end cap. */ + immVertex2fv(pos, t0); + immVertex2fv(pos, t1); + } + + /* store computed point2 coordinates as point1 ones of next segment. */ + copy_v2_v2(s0, s1); + /* store stroke's 'natural' normal for next stroke to use */ + copy_v2_v2(pm, m2); + } + + immEnd(); + immUnbindProgram(); + } +} + +/* ----- Strokes Drawing ------ */ + +/* Helper for doing all the checks on whether a stroke can be drawn */ +static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) +{ + /* skip stroke if it isn't in the right display space for this drawing context */ + /* 1) 3D Strokes */ + if ((dflag & GP_DRAWDATA_ONLY3D) && !(gps->flag & GP_STROKE_3DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLY3D) && (gps->flag & GP_STROKE_3DSPACE)) + return false; + + /* 2) Screen Space 2D Strokes */ + if ((dflag & GP_DRAWDATA_ONLYV2D) && !(gps->flag & GP_STROKE_2DSPACE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYV2D) && (gps->flag & GP_STROKE_2DSPACE)) + return false; + + /* 3) Image Space (2D) */ + if ((dflag & GP_DRAWDATA_ONLYI2D) && !(gps->flag & GP_STROKE_2DIMAGE)) + return false; + if (!(dflag & GP_DRAWDATA_ONLYI2D) && (gps->flag & GP_STROKE_2DIMAGE)) + return false; + + /* skip stroke if it doesn't have any valid data */ + if ((gps->points == NULL) || (gps->totpoints < 1)) + return false; + + /* stroke can be drawn */ + return true; +} + +/* draw a set of strokes */ +static void gp_draw_strokes( + bGPdata *UNUSED(gpd), bGPDlayer *UNUSED(gpl), const bGPDframe *gpf, int offsx, int offsy, int winx, int winy, + int dflag, bool debug, short lthick, const float color[4]) +{ + GPU_enable_program_point_size(); + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if (gp_can_draw_stroke(gps, dflag) == false) { + continue; + } + + /* 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); + GPU_depth_test(true); + + /* first arg is normally rv3d->dist, but this isn't + * available here and seems to work quite well without */ + bglPolygonOffset(1.0f, 1.0f); + } + + /* 3D Lines - OpenGL primitives-based */ + if (gps->totpoints == 1) { + gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); + } + else { + gp_draw_stroke_3d(gps->points, gps->totpoints, lthick, debug, gps->flag, + color, gps->flag & GP_STROKE_CYCLIC); + } + + if (no_xray) { + glDepthMask(mask_orig); + GPU_depth_test(false); + + bglPolygonOffset(0.0, 0.0); + } + } + else { + /* 2D Strokes... */ + if (gps->totpoints == 1) { + gp_draw_stroke_point(gps->points, lthick, dflag, gps->flag, offsx, offsy, winx, winy, color); + } + else { + gp_draw_stroke_2d(gps->points, gps->totpoints, lthick, dflag, gps->flag, debug, + offsx, offsy, winx, winy, color); + } + } + } + + GPU_disable_program_point_size(); +} + +/* Draw selected verts for strokes being edited */ +static void gp_draw_strokes_edit( + bGPdata *gpd, bGPDlayer *gpl, const bGPDframe *gpf, + int offsx, int offsy, int winx, int winy, + short dflag, short UNUSED(lflag), float alpha) +{ + /* 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) { + glGetIntegerv(GL_DEPTH_WRITEMASK, &mask_orig); + glDepthMask(0); + GPU_depth_test(true); + + /* first arg is normally rv3d->dist, but this isn't + * available here and seems to work quite well without */ + bglPolygonOffset(1.0f, 1.0f); + } + } + + GPU_enable_program_point_size(); + + /* draw stroke verts */ + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if (gp_can_draw_stroke(gps, dflag) == false) + continue; + + /* Optimisation: only draw points for selected strokes + * We assume that selected points can only occur in + * strokes that are selected too. + */ + if ((gps->flag & GP_STROKE_SELECT) == 0) + continue; + + /* Get size of verts: + * - The selected state needs to be larger than the unselected state so that + * they stand out more. + * - We use the theme setting for size of the unselected verts + */ + float bsize = UI_GetThemeValuef(TH_GP_VERTEX_SIZE); + float vsize; + if ((int)bsize > 8) { + vsize = 10.0f; + bsize = 8.0f; + } + else { + vsize = bsize + 2; + } + + float selectColor[4]; + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); + selectColor[3] = alpha; + + GPUVertFormat *format = immVertexFormat(); + uint pos; /* specified later */ + uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (gps->flag & GP_STROKE_3DSPACE) { + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_POINT_VARYING_SIZE_VARYING_COLOR); + } + else { + pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_POINT_VARYING_SIZE_VARYING_COLOR); + } + + immBegin(GPU_PRIM_POINTS, gps->totpoints); + + /* Draw start and end point differently if enabled stroke direction hint */ + bool show_direction_hint = (gpd->flag & GP_DATA_SHOW_DIRECTION) && (gps->totpoints > 1); + + /* Draw all the stroke points (selected or not) */ + bGPDspoint *pt = gps->points; + for (int i = 0; i < gps->totpoints; i++, pt++) { + /* size and color first */ + if (show_direction_hint && i == 0) { + /* start point in green bigger */ + immAttrib3f(color, 0.0f, 1.0f, 0.0f); + immAttrib1f(size, vsize + 4); + } + else if (show_direction_hint && (i == gps->totpoints - 1)) { + /* end point in red smaller */ + immAttrib3f(color, 1.0f, 0.0f, 0.0f); + immAttrib1f(size, vsize + 1); + } + else if (pt->flag & GP_SPOINT_SELECT) { + immAttrib3fv(color, selectColor); + immAttrib1f(size, vsize); + } + else { + immAttrib3fv(color, gpl->color); + immAttrib1f(size, bsize); + } + + /* then position */ + if (gps->flag & GP_STROKE_3DSPACE) { + immVertex3fv(pos, &pt->x); + } + else { + float co[2]; + gp_calc_2d_stroke_fxy(&pt->x, gps->flag, offsx, offsy, winx, winy, co); + immVertex2fv(pos, co); + } + } + + immEnd(); + immUnbindProgram(); + } + + GPU_disable_program_point_size(); + + /* clear depth mask */ + if (dflag & GP_DRAWDATA_ONLY3D) { + if (no_xray) { + glDepthMask(mask_orig); + GPU_depth_test(false); + + bglPolygonOffset(0.0, 0.0); +#if 0 + glDisable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(0, 0); +#endif + } + } +} + +/* ----- General Drawing ------ */ + +/* 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, float alpha) +{ + float ink[4]; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG); + short lthick = gpl->thickness; + + /* apply layer opacity */ + copy_v3_v3(ink, gpl->color); + ink[3] = gpl->opacity; + + /* don't draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* get frame to draw */ + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra, 0); + if (gpf == NULL) + continue; + + /* set basic stroke thickness */ + GPU_line_width(lthick); + + /* Add layer drawing settings to the set of "draw flags" + * NOTE: If the setting doesn't apply, it *must* be cleared, + * as dflag's carry over from the previous layer + */ +#define GP_DRAWFLAG_APPLY(condition, draw_flag_value) { \ + if (condition) dflag |= (draw_flag_value); \ + else dflag &= ~(draw_flag_value); \ + } (void)0 + + /* xray... */ + GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_NO_XRAY), GP_DRAWDATA_NO_XRAY); + +#undef GP_DRAWFLAG_APPLY + + + /* draw the strokes already in active frame */ + gp_draw_strokes(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, debug, lthick, ink); + + /* Draw verts of selected strokes + * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering + * - locked layers can't be edited, so there's no point showing these verts + * as they will have no bearings on what gets edited + * - only show when in editmode, since operators shouldn't work otherwise + * (NOTE: doing it this way means that the toggling editmode shows visible change immediately) + */ + /* XXX: perhaps we don't want to show these when users are drawing... */ + if ((G.f & G_RENDER_OGL) == 0 && + (gpl->flag & GP_LAYER_LOCKED) == 0 && + (gpd->flag & GP_DATA_STROKE_EDITMODE)) + { + gp_draw_strokes_edit(gpd, gpl, gpf, offsx, offsy, winx, winy, dflag, gpl->flag, alpha); + } + + /* Check if may need to draw the active stroke cache, only if this layer is the active layer + * that is being edited. (Stroke buffer is currently stored in gp-data) + */ + if (ED_gpencil_session_active() && (gpl->flag & GP_LAYER_ACTIVE) && + (gpf->flag & GP_FRAME_PAINT)) + { + /* Buffer stroke needs to be drawn with a different linestyle + * to help differentiate them from normal strokes. + * + * It should also be noted that sbuffer contains temporary point types + * i.e. tGPspoints NOT bGPDspoints + */ + gp_draw_stroke_buffer( + gpd->runtime.sbuffer, + gpd->runtime.sbuffer_size, lthick, + dflag, gpd->runtime.sbuffer_sflag, ink); + } + } +} + +/* draw a short status message in the top-right corner */ +static void gp_draw_status_text(const bGPdata *gpd, ARegion *ar) +{ + rcti rect; + + /* Cannot draw any status text when drawing OpenGL Renders */ + if (G.f & G_RENDER_OGL) + return; + + /* Get bounds of region - Necessary to avoid problems with region overlap */ + ED_region_visible_rect(ar, &rect); + + /* for now, this should only be used to indicate when we are in stroke editmode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + const char *printable = IFACE_("GPencil Stroke Editing"); + float printable_size[2]; + + int font_id = BLF_default(); + + BLF_width_and_height(font_id, printable, BLF_DRAW_STR_DUMMY_MAX, &printable_size[0], &printable_size[1]); + + int xco = (rect.xmax - U.widget_unit) - (int)printable_size[0]; + int yco = (rect.ymax - U.widget_unit); + + /* text label */ + UI_FontThemeColor(font_id, TH_TEXT_HI); +#ifdef WITH_INTERNATIONAL + BLF_draw_default(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#else + BLF_draw_default_ascii(xco, yco, 0.0f, printable, BLF_DRAW_STR_DUMMY_MAX); +#endif + + /* grease pencil icon... */ + // XXX: is this too intrusive? + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); + + xco -= U.widget_unit; + yco -= (int)printable_size[1] / 2; + + UI_icon_draw(xco, yco, ICON_GREASEPENCIL); + + GPU_blend(false); + } +} + +/* draw grease-pencil datablock */ +static void gp_draw_data( + bGPdata *gpd, int offsx, int offsy, int winx, int winy, + int cfra, int dflag, float alpha) +{ + /* turn on smooth lines (i.e. anti-aliasing) */ + GPU_line_smooth(true); + + /* turn on alpha-blending */ + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + GPU_blend(true); + + /* draw! */ + gp_draw_data_layers(gpd, offsx, offsy, winx, winy, cfra, dflag, alpha); + + /* turn off alpha blending, then smooth lines */ + GPU_blend(false); // alpha blending + GPU_line_smooth(false); // smooth lines +} + +/* if we have strokes for scenes (3d view)/clips (movie clip editor) + * and objects/tracks, multiple data blocks have to be drawn */ +static void gp_draw_data_all( + Scene *scene, bGPdata *gpd, int offsx, int offsy, int winx, int winy, + int cfra, int dflag, const char spacetype) +{ + bGPdata *gpd_source = NULL; + float alpha = 1.0f; + + if (scene) { + if (spacetype == SPACE_VIEW3D) { + gpd_source = (scene->gpd ? scene->gpd : NULL); + } + else if (spacetype == SPACE_CLIP && scene->clip) { + /* currently drawing only gpencil data from either clip or track, but not both - XXX fix logic behind */ + gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); + } + + if (gpd_source) { + gp_draw_data(gpd_source, offsx, offsy, winx, winy, cfra, dflag, alpha); + } + } + + /* 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, alpha); + } +} + +/* ----- Grease Pencil Sketches Drawing API ------ */ + +/* ............................ + * XXX + * We need to review the calls below, since they may be/are not that suitable for + * the new ways that we intend to be drawing data... + * ............................ */ + +/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */ +void ED_gpencil_draw_2dimage(const bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + + int offsx, offsy, sizex, sizey; + int dflag = GP_DRAWDATA_NOSTATUS; + + bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + if (gpd == NULL) return; + + /* calculate rect */ + switch (sa->spacetype) { + case SPACE_IMAGE: /* image */ + case SPACE_CLIP: /* clip */ + { + /* just draw using standard scaling (settings here are currently ignored anyways) */ + /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax); + + dflag |= GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_IEDITHACK; + break; + } + case SPACE_SEQ: /* sequence */ + { + /* just draw using standard scaling (settings here are currently ignored anyways) */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + /* NOTE: I2D was used in 2.4x, but the old settings for that have been deprecated + * and everything moved to standard View2d + */ + dflag |= GP_DRAWDATA_ONLYV2D; + break; + } + default: /* for spacetype not yet handled */ + offsx = 0; + offsy = 0; + sizex = ar->winx; + sizey = ar->winy; + + dflag |= GP_DRAWDATA_ONLYI2D; + break; + } + + if (ED_screen_animation_playing(wm)) { + /* don't show onionskins during animation playback/scrub (i.e. it obscures the poses) + * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes) + */ + dflag |= GP_DRAWDATA_NO_ONIONS; + } + + /* draw it! */ + gp_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, sa->spacetype); +} + +/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly + * Note: this gets called twice - first time with onlyv2d=true to draw 'canvas' strokes, + * second time with onlyv2d=false for screen-aligned strokes */ +void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d) +{ + wmWindowManager *wm = CTX_wm_manager(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + Scene *scene = CTX_data_scene(C); + int dflag = 0; + + /* check that we have grease-pencil stuff to draw */ + if (sa == NULL) return; + bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + if (gpd == NULL) return; + + /* special hack for Image Editor */ + /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ + if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) + dflag |= GP_DRAWDATA_IEDITHACK; + + /* draw it! */ + if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); + if (ED_screen_animation_playing(wm)) dflag |= GP_DRAWDATA_NO_ONIONS; + + gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); + + /* draw status text (if in screen/pixel-space) */ + if (!onlyv2d) { + gp_draw_status_text(gpd, ar); + } +} + + +/* draw annotations sketches to specified 3d-view assuming that matrices are already set correctly + * Note: this gets called twice - first time with only3d=true to draw 3d-strokes, + * second time with only3d=false for screen-aligned strokes */ +void ED_gpencil_draw_view3d_annotations( + Scene *scene, struct Depsgraph *depsgraph, + View3D *v3d, ARegion *ar, + bool only3d) +{ + int dflag = 0; + RegionView3D *rv3d = ar->regiondata; + int offsx, offsy, winx, winy; + + /* check that we have grease-pencil stuff to draw */ + /* XXX: Hardcoded reference here may get out of sync if we change how we fetch annotation data */ + bGPdata *gpd = scene->gpd; + if (gpd == NULL) return; + + /* when rendering to the offscreen buffer we don't want to + * deal with the camera border, otherwise map the coords to the camera border. */ + if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_RENDER_OGL)) { + rctf rectf; + ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &rectf, true); /* no shift */ + + offsx = round_fl_to_int(rectf.xmin); + offsy = round_fl_to_int(rectf.ymin); + winx = round_fl_to_int(rectf.xmax - rectf.xmin); + winy = round_fl_to_int(rectf.ymax - rectf.ymin); + } + else { + offsx = 0; + offsy = 0; + winx = ar->winx; + winy = ar->winy; + } + + /* set flags */ + if (only3d) { + /* 3D strokes/3D space: + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); + } + + if (v3d->flag2 & V3D_RENDER_OVERRIDE) { + /* don't draw status text when "only render" flag is set */ + dflag |= GP_DRAWDATA_NOSTATUS; + } + + /* draw it! */ + gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); +} + +#if 0 // XXX: Reinstate, after renaming the functions + +void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) +{ + int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D; + + gp_draw_data_all(scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); +} + +#endif + +/* ************************************************** */ diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c new file mode 100644 index 00000000000..6325052fccd --- /dev/null +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -0,0 +1,2382 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008/2018, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/annotate_paint.c + * \ingroup edgpencil + */ + + +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" +#include "BLI_math_geom.h" + +#include "BLT_translation.h" + +#include "PIL_time.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_main.h" +#include "BKE_report.h" +#include "BKE_screen.h" +#include "BKE_tracking.h" + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_windowmanager_types.h" + +#include "UI_view2d.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_clip.h" + +#include "BIF_glutil.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" +#include "GPU_state.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "DEG_depsgraph.h" + +#include "gpencil_intern.h" + +/* ******************************************* */ +/* 'Globals' and Defines */ + +/* values for tGPsdata->status */ +typedef enum eGPencil_PaintStatus { + GP_STATUS_IDLING = 0, /* stroke isn't in progress yet */ + GP_STATUS_PAINTING, /* a stroke is in progress */ + GP_STATUS_ERROR, /* something wasn't correctly set up */ + GP_STATUS_DONE /* painting done */ +} eGPencil_PaintStatus; + +/* Return flags for adding points to stroke buffer */ +typedef enum eGP_StrokeAdd_Result { + GP_STROKEADD_INVALID = -2, /* error occurred - insufficient info to do so */ + GP_STROKEADD_OVERFLOW = -1, /* error occurred - cannot fit any more points */ + GP_STROKEADD_NORMAL, /* point was successfully added */ + GP_STROKEADD_FULL /* cannot add any more points to buffer */ +} eGP_StrokeAdd_Result; + +/* Runtime flags */ +typedef enum eGPencil_PaintFlags { + GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ + GP_PAINTFLAG_STROKEADDED = (1 << 1), + GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), + GP_PAINTFLAG_SELECTMASK = (1 << 3), +} eGPencil_PaintFlags; + + +/* Temporary 'Stroke' Operation data + * "p" = op->customdata + */ +typedef struct tGPsdata { + Main *bmain; + Scene *scene; /* current scene from context */ + struct Depsgraph *depsgraph; + + wmWindow *win; /* window where painting originated */ + ScrArea *sa; /* area where painting originated */ + ARegion *ar; /* region where painting originated */ + View2D *v2d; /* needed for GP_STROKE_2DSPACE */ + rctf *subrect; /* for using the camera rect within the 3d view */ + rctf subrect_data; + + GP_SpaceConversion gsc; /* settings to pass to gp_points_to_xy() */ + + PointerRNA ownerPtr; /* pointer to owner of gp-datablock */ + bGPdata *gpd; /* gp-datablock layer comes from */ + bGPDlayer *gpl; /* layer we're working on */ + bGPDframe *gpf; /* frame we're working on */ + + char *align_flag; /* projection-mode flags (toolsettings - eGPencil_Placement_Flags) */ + + eGPencil_PaintStatus status; /* current status of painting */ + eGPencil_PaintModes paintmode; /* mode for painting */ + eGPencil_PaintFlags flags; /* flags that can get set during runtime (eGPencil_PaintFlags) */ + + short radius; /* radius of influence for eraser */ + + int mval[2]; /* current mouse-position */ + int mvalo[2]; /* previous recorded mouse-position */ + + float pressure; /* current stylus pressure */ + float opressure; /* previous stylus pressure */ + + /* These need to be doubles, as (at least under unix) they are in seconds since epoch, + * float (and its 7 digits precision) is definitively not enough here! + * double, with its 15 digits precision, ensures us millisecond precision for a few centuries at least. + */ + double inittime; /* Used when converting to path */ + double curtime; /* Used when converting to path */ + double ocurtime; /* Used when converting to path */ + + float imat[4][4]; /* inverted transformation matrix applying when converting coords from screen-space + * to region space */ + float mat[4][4]; + + float custom_color[4]; /* custom color - hack for enforcing a particular color for track/mask editing */ + + void *erasercursor; /* radial cursor data for drawing eraser */ + + short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ + int lock_axis; /* lock drawing to one axis */ + + short keymodifier; /* key used for invoking the operator */ +} tGPsdata; + +/* ------ */ + +/* Macros for accessing sensitivity thresholds... */ +/* minimum number of pixels mouse should move before new point created */ +#define MIN_MANHATTEN_PX (U.gp_manhattendist) +/* minimum length of new segment before new point can be added */ +#define MIN_EUCLIDEAN_PX (U.gp_euclideandist) + +static bool gp_stroke_added_check(tGPsdata *p) +{ + return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); +} + +static void gp_stroke_added_enable(tGPsdata *p) +{ + BLI_assert(p->gpf->strokes.last != NULL); + p->flags |= GP_PAINTFLAG_STROKEADDED; +} + +/* ------ */ +/* Forward defines for some functions... */ + +static void gp_session_validatebuffer(tGPsdata *p); + +/* ******************************************* */ +/* Context Wrangling... */ + +/* check if context is suitable for drawing */ +static bool gpencil_draw_poll(bContext *C) +{ + if (ED_operator_regionactive(C)) { + /* check if current context can support GPencil data */ + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + /* check if Grease Pencil isn't already running */ + if (ED_gpencil_session_active() == 0) + return 1; + else + CTX_wm_operator_poll_msg_set(C, "Annotation operator is already active"); + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + } + + return 0; +} + +/* check if projecting strokes into 3d-geometry in the 3D-View */ +static bool gpencil_project_check(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); +} + +/* ******************************************* */ +/* Calculations/Conversions */ + +/* Utilities --------------------------------- */ + +/* get the reference point for stroke-point conversions */ +static void gp_get_3d_reference(tGPsdata *p, float vec[3]) +{ + View3D *v3d = p->sa->spacedata.first; + const float *fp = ED_view3d_cursor3d_get(p->scene, v3d)->location; + + /* use 3D-cursor */ + copy_v3_v3(vec, fp); +} + +/* Stroke Editing ---------------------------- */ + +/* check if the current mouse position is suitable for adding a new point */ +static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) +{ + int dx = abs(mval[0] - pmval[0]); + int dy = abs(mval[1] - pmval[1]); + + /* if buffer is empty, just let this go through (i.e. so that dots will work) */ + if (p->gpd->runtime.sbuffer_size == 0) + return true; + + /* check if mouse moved at least certain distance on both axes (best case) + * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand + */ + else if ((dx > MIN_MANHATTEN_PX) && (dy > MIN_MANHATTEN_PX)) + return true; + + /* check if the distance since the last point is significant enough + * - prevents points being added too densely + * - distance here doesn't use sqrt to prevent slowness... we should still be safe from overflows though + */ + else if ((dx * dx + dy * dy) > MIN_EUCLIDEAN_PX * MIN_EUCLIDEAN_PX) + return true; + + /* mouse 'didn't move' */ + else + return false; +} + +/* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ +static void gp_project_points_to_plane(RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) +{ + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + plane_normal[axis] = 1.0f; + + /* Reproject the points in the plane */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); + + /* calculate line extrem point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); + + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } + } +} + +/* reproject stroke to plane locked to axis in 3d cursor location */ +static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) +{ + bGPdata *gpd = p->gpd; + float origin[3]; + float cursor[3]; + RegionView3D *rv3d = p->ar->regiondata; + + /* verify the stroke mode is CURSOR 3d space mode */ + if ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + return; + } + if ((*p->align_flag & GP_PROJECT_DEPTH_VIEW) || (*p->align_flag & GP_PROJECT_DEPTH_STROKE)) { + return; + } + + /* get 3d cursor and set origin for locked axis only. Uses axis-1 because the enum for XYZ start with 1 */ + gp_get_3d_reference(p, cursor); + zero_v3(origin); + origin[p->lock_axis - 1] = cursor[p->lock_axis - 1]; + + gp_project_points_to_plane(rv3d, gps, origin, p->lock_axis - 1); +} + +/* convert screen-coordinates to buffer-coordinates */ +/* XXX this method needs a total overhaul! */ +static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3], float *depth) +{ + bGPdata *gpd = p->gpd; + + /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ + if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { + if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) { + /* projecting onto 3D-Geometry + * - nothing more needs to be done here, since view_autodist_simple() has already done it + */ + } + else { + float mval_prj[2]; + float rvec[3], dvec[3]; + float mval_f[2] = {UNPACK2(mval)}; + float zfac; + + /* Current method just converts each point in screen-coordinates to + * 3D-coordinates using the 3D-cursor as reference. In general, this + * works OK, but it could of course be improved. + * + * TODO: + * - investigate using nearest point(s) on a previous stroke as + * reference point instead or as offset, for easier stroke matching + */ + + gp_get_3d_reference(p, rvec); + zfac = ED_view3d_calc_zfac(p->ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(p->ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(p->ar, mval_f, dvec, zfac); + sub_v3_v3v3(out, rvec, dvec); + } + else { + zero_v3(out); + } + } + } + + /* 2d - on 'canvas' (assume that p->v2d is set) */ + else if ((gpd->runtime.sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { + UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]); + mul_v3_m4v3(out, p->imat, out); + } + + /* 2d - relative to screen (viewport area) */ + else { + if (p->subrect == NULL) { /* normal 3D view */ + out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; + out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; + } + else { /* camera view, use subrect */ + out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; + out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; + } + } +} + +/* 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; + tGPspoint *pt; + ToolSettings *ts = p->scene->toolsettings; + + /* check painting mode */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only - i.e. only store start and end point in buffer */ + if (gpd->runtime.sbuffer_size == 0) { + /* first point in buffer (start point) */ + pt = (tGPspoint *)(gpd->runtime.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); + + /* increment buffer size */ + gpd->runtime.sbuffer_size++; + } + else { + /* just reset the endpoint to the latest value + * - assume that pointers for this are always valid... + */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); + + /* 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) */ + gpd->runtime.sbuffer_size = 2; + } + + /* can keep carrying on this way :) */ + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ + /* check if still room in buffer */ + if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_OVERFLOW; + + /* get pointer to destination point */ + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); + + /* store settings */ + copy_v2_v2_int(&pt->x, mval); + pt->pressure = pressure; + pt->strength = 1.0f; /* unused for annotations, but initialise for easier conversions to GP Object */ + + /* point time */ + pt->time = (float)(curtime - p->inittime); + + /* increment counters */ + gpd->runtime.sbuffer_size++; + + /* check if another operation can still occur */ + if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) + return GP_STROKEADD_FULL; + else + return GP_STROKEADD_NORMAL; + } + else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* get pointer to destination point */ + pt = (tGPspoint *)(gpd->runtime.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 + * to stroke. This allows to draw lines more interactively (see new segment + * during mouse slide, e.g.) + */ + if (gp_stroke_added_check(p)) { + bGPDstroke *gps = p->gpf->strokes.last; + bGPDspoint *pts; + + /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ + if (gpd->runtime.sbuffer_size == 0) { + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->totpoints++; + } + + pts = &gps->points[gps->totpoints - 1]; + + /* special case for poly lines: normally, + * depth is needed only when creating new stroke from buffer, + * but poly lines are converting to stroke instantly, + * so initialize depth buffer before converting coordinates + */ + if (gpencil_project_check(p)) { + View3D *v3d = p->sa->spacedata.first; + + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init( + p->depsgraph, p->ar, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + } + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + + /* 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 */ + if (gpd->runtime.sbuffer_size == 0) + gpd->runtime.sbuffer_size++; + + return GP_STROKEADD_NORMAL; + } + + /* return invalid state for now... */ + return GP_STROKEADD_INVALID; +} + +/* simplify a stroke (in buffer) before storing it + * - applies a reverse Chaikin filter + * - code adapted from etch-a-ton branch + */ +static void gp_stroke_simplify(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + tGPspoint *old_points = (tGPspoint *)gpd->runtime.sbuffer; + short num_points = gpd->runtime.sbuffer_size; + short flag = gpd->runtime.sbuffer_sflag; + short i, j; + + /* only simplify if simplification is enabled, and we're not doing a straight line */ + if (!(U.gp_settings & GP_PAINT_DOSIMPLIFY) || (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT)) + return; + + /* don't simplify if less than 4 points in buffer */ + if ((num_points <= 4) || (old_points == NULL)) + return; + + /* clear buffer (but don't free mem yet) so that we can write to it + * - firstly set sbuffer to NULL, so a new one is allocated + * - secondly, reset flag after, as it gets cleared auto + */ + gpd->runtime.sbuffer = NULL; + gp_session_validatebuffer(p); + gpd->runtime.sbuffer_sflag = flag; + +/* macro used in loop to get position of new point + * - used due to the mixture of datatypes in use here + */ +#define GP_SIMPLIFY_AVPOINT(offs, sfac) \ + { \ + co[0] += (float)(old_points[offs].x * sfac); \ + co[1] += (float)(old_points[offs].y * sfac); \ + pressure += old_points[offs].pressure * sfac; \ + time += old_points[offs].time * sfac; \ + } (void)0 + + /* XXX Here too, do not lose start and end points! */ + gp_stroke_addpoint(p, &old_points->x, old_points->pressure, p->inittime + (double)old_points->time); + for (i = 0, j = 0; i < num_points; i++) { + if (i - j == 3) { + float co[2], pressure, time; + int mco[2]; + + /* initialize values */ + co[0] = 0.0f; + co[1] = 0.0f; + pressure = 0.0f; + time = 0.0f; + + /* using macro, calculate new point */ + GP_SIMPLIFY_AVPOINT(j, -0.25f); + GP_SIMPLIFY_AVPOINT(j + 1, 0.75f); + GP_SIMPLIFY_AVPOINT(j + 2, 0.75f); + GP_SIMPLIFY_AVPOINT(j + 3, -0.25f); + + /* set values for adding */ + mco[0] = (int)co[0]; + mco[1] = (int)co[1]; + + /* ignore return values on this... assume to be ok for now */ + gp_stroke_addpoint(p, mco, pressure, p->inittime + (double)time); + + j += 2; + } + } + gp_stroke_addpoint(p, &old_points[num_points - 1].x, old_points[num_points - 1].pressure, + p->inittime + (double)old_points[num_points - 1].time); + + /* free old buffer */ + MEM_freeN(old_points); +} + +/* make a new stroke from the buffer data */ +static void gp_stroke_newfrombuffer(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + bGPDlayer *gpl = p->gpl; + bGPDstroke *gps; + bGPDspoint *pt; + tGPspoint *ptc; + ToolSettings *ts = p->scene->toolsettings; + + int i, totelem; + /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ + int depth_margin = (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 4 : 0; + + /* get total number of points to allocate space for + * - drawing straight-lines only requires the endpoints + */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) + totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; + else + totelem = gpd->runtime.sbuffer_size; + + /* exit with error if no valid points from this stroke */ + if (totelem == 0) { + if (G.debug & G_DEBUG) + printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->runtime.sbuffer_size); + return; + } + + /* special case for poly line -- for already added stroke during session + * coordinates are getting added to stroke immediately to allow more + * interactive behavior + */ + if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + if (gp_stroke_added_check(p)) { + return; + } + } + + /* allocate memory for a new stroke */ + gps = MEM_callocN(sizeof(bGPDstroke), "gp_stroke"); + + /* copy appropriate settings for stroke */ + gps->totpoints = totelem; + gps->thickness = gpl->thickness; + gps->flag = gpd->runtime.sbuffer_sflag; + gps->inittime = p->inittime; + + /* enable recalculation flag by default (only used if hq fill) */ + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* allocate enough memory for a continuous array for storage points */ + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->tot_triangles = 0; + + /* set pointer to first non-initialized point */ + pt = gps->points + (gps->totpoints - totelem); + + /* copy points from the buffer to the stroke */ + if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { + /* straight lines only -> only endpoints */ + { + /* first point */ + ptc = gpd->runtime.sbuffer; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + /* 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++; + } + + if (totelem == 2) { + /* last point if applicable */ + ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + + /* 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 if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* first point */ + ptc = gpd->runtime.sbuffer; + + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + pt->time = ptc->time; + } + else { + float *depth_arr = NULL; + + /* get an array of depths, far depths are blended */ + if (gpencil_project_check(p)) { + int mval[2], mval_prev[2] = { 0 }; + int interp_depth = 0; + int found_depth = 0; + + depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); + + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { + copy_v2_v2_int(mval, &ptc->x); + + if ((ED_view3d_autodist_depth(p->ar, mval, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg(p->ar, mval, mval_prev, depth_margin + 1, depth_arr + i) == 0))) + { + interp_depth = true; + } + else { + found_depth = true; + } + + copy_v2_v2_int(mval_prev, mval); + } + + if (found_depth == false) { + /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) + depth_arr[i] = 0.9999f; + } + else { + if (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE_ENDPOINTS) { + /* remove all info between the valid endpoints */ + int first_valid = 0; + int last_valid = 0; + + for (i = 0; i < gpd->runtime.sbuffer_size; i++) { + if (depth_arr[i] != FLT_MAX) + break; + } + first_valid = i; + + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) { + if (depth_arr[i] != FLT_MAX) + break; + } + last_valid = i; + + /* invalidate non-endpoints, so only blend between first and last */ + for (i = first_valid + 1; i < last_valid; i++) + depth_arr[i] = FLT_MAX; + + interp_depth = true; + } + + if (interp_depth) { + interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); + } + } + } + + + pt = gps->points; + + /* convert all points (normal behavior) */ + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; i++, ptc++, pt++) { + /* convert screen-coordinates to appropriate coordinates (and store them) */ + gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); + + /* copy pressure and time */ + pt->pressure = ptc->pressure; + pt->strength = ptc->strength; + CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt->time = ptc->time; + } + + /* if axis locked, reproject to plane locked (only in 3d space) */ + if (p->lock_axis > GP_LOCKAXIS_NONE) { + gp_reproject_toplane(p, gps); + } + + if (depth_arr) + MEM_freeN(depth_arr); + } + + /* add stroke to frame */ + BLI_addtail(&p->gpf->strokes, gps); + gp_stroke_added_enable(p); +} + +/* --- 'Eraser' for 'Paint' Tool ------ */ + +/* helper to free a stroke + * NOTE: gps->dvert and gps->triangles should be NULL, but check anyway for good measure + */ +static void gp_free_stroke(bGPdata *UNUSED(gpd), bGPDframe *gpf, bGPDstroke *gps) +{ + if (gps->points) { + MEM_freeN(gps->points); + } + + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + if (gps->triangles) { + MEM_freeN(gps->triangles); + } + + BLI_freelinkN(&gpf->strokes, gps); +} + + +/* which which point is infront (result should only be used for comparison) */ +static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) +{ + if (rv3d->is_persp) { + return ED_view3d_calc_zfac(rv3d, co, NULL); + } + else { + return -dot_v3v3(rv3d->viewinv[2], co); + } +} + +/* only erase stroke points that are visible (3d view) */ +static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) +{ + if ((p->sa->spacetype == SPACE_VIEW3D) && + (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) + { + RegionView3D *rv3d = p->ar->regiondata; + const int mval[2] = {x, y}; + float mval_3d[3]; + + 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); + + if (depth_pt > depth_mval) { + return true; + } + } + } + return false; +} + +/* eraser tool - evaluation per stroke */ +/* TODO: this could really do with some optimization (KD-Tree/BVH?) */ +static void gp_stroke_eraser_dostroke(tGPsdata *p, + bGPDframe *gpf, bGPDstroke *gps, + const int mval[2], const int mvalo[2], + const int radius, const rcti *rect) +{ + bGPDspoint *pt1, *pt2; + int pc1[2] = {0}; + int pc2[2] = {0}; + int i; + + if (gps->totpoints == 0) { + /* just free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + } + 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]); + + /* 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 */ + if (len_v2v2_int(mval, pc1) <= radius) { + /* free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + } + } + } + } + else { + /* Perform culling? */ + bool do_cull = false; + + /* Clear Tags + * + * Note: It's better this way, as we are sure that + * we don't miss anything, though things will be + * slightly slower as a result + */ + for (i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + pt->flag &= ~GP_SPOINT_TAG; + } + + /* First Pass: Loop over the points in the stroke + * 1) Thin out parts of the stroke under the brush + * 2) Tag "too thin" parts for removal (in second pass) + */ + for (i = 0; (i + 1) < gps->totpoints; i++) { + /* 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; + + gp_point_to_xy(&p->gsc, gps, pt1, &pc1[0], &pc1[1]); + gp_point_to_xy(&p->gsc, gps, pt2, &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]))) + { + /* Check if point segment of stroke had anything to do with + * eraser region (either within stroke painted, or on its lines) + * - this assumes that linewidth is irrelevant + */ + if (gp_stroke_inside_circle(mval, mvalo, radius, pc1[0], pc1[1], pc2[0], pc2[1])) { + if ((gp_stroke_eraser_is_occluded(p, pt1, pc1[0], pc1[1]) == false) || + (gp_stroke_eraser_is_occluded(p, pt2, pc2[0], pc2[1]) == false)) + { + /* Edge is affected - Check individual points now */ + if (len_v2v2_int(mval, pc1) <= radius) { + pt1->flag |= GP_SPOINT_TAG; + } + if (len_v2v2_int(mval, pc2) <= radius) { + pt2->flag |= GP_SPOINT_TAG; + } + do_cull = true; + } + } + } + } + + /* Second Pass: Remove any points that are tagged */ + if (do_cull) { + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false); + } + } +} + +/* erase strokes which fall under the eraser strokes */ +static void gp_stroke_doeraser(tGPsdata *p) +{ + bGPDframe *gpf = p->gpf; + bGPDstroke *gps, *gpn; + rcti rect; + + /* rect is rectangle of eraser */ + rect.xmin = p->mval[0] - p->radius; + rect.ymin = p->mval[1] - p->radius; + rect.xmax = p->mval[0] + p->radius; + rect.ymax = p->mval[1] + p->radius; + + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { + View3D *v3d = p->sa->spacedata.first; + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, 0); + } + } + + /* loop over strokes of active layer only (session init already took care of ensuring validity), + * checking segments for intersections to remove + */ + for (gps = gpf->strokes.first; gps; gps = gpn) { + gpn = gps->next; + /* 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) + */ + if (ED_gpencil_stroke_can_use_direct(p->sa, gps)) { + gp_stroke_eraser_dostroke(p, gpf, gps, p->mval, p->mvalo, p->radius, &rect); + } + } +} + +/* ******************************************* */ +/* Sketching Operator */ + +/* clear the session buffers (call this before AND after a paint operation) */ +static void gp_session_validatebuffer(tGPsdata *p) +{ + bGPdata *gpd = p->gpd; + + /* clear memory of buffer (or allocate it if starting a new session) */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - reset sbuffer\n"); */ + memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); + } + else { + /* printf("\t\tGP - allocate sbuffer\n"); */ + gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); + } + + /* reset indices */ + gpd->runtime.sbuffer_size = 0; + + /* reset flags */ + gpd->runtime.sbuffer_sflag = 0; + + /* reset inittime */ + p->inittime = 0.0; +} + +/* (re)init new painting data */ +static bool gp_session_initdata(bContext *C, tGPsdata *p) +{ + Main *bmain = CTX_data_main(C); + bGPdata **gpd_ptr = NULL; + ScrArea *curarea = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + /* make sure the active view (at the starting time) is a 3d-view */ + if (curarea == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No active view for painting\n"); + return 0; + } + + /* pass on current scene and window */ + p->bmain = CTX_data_main(C); + p->scene = CTX_data_scene(C); + p->depsgraph = CTX_data_depsgraph(C); + p->win = CTX_wm_window(C); + + unit_m4(p->imat); + unit_m4(p->mat); + + switch (curarea->spacetype) { + /* supported views first */ + case SPACE_VIEW3D: + { + /* View3D *v3d = curarea->spacedata.first; */ + /* RegionView3D *rv3d = ar->regiondata; */ + + /* set current area + * - must verify that region data is 3D-view (and not something else) + */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + p->sa = curarea; + p->ar = ar; + p->align_flag = &ts->annotate_v3d_align; + + if (ar->regiondata == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); + return 0; + } + break; + } + case SPACE_NODE: + { + /* SpaceNode *snode = curarea->spacedata.first; */ + + /* set current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_v2d_align; + break; + } + case SPACE_SEQ: + { + SpaceSeq *sseq = curarea->spacedata.first; + + /* set current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_seq_align; + + /* check that gpencil data is allowed to be drawn */ + if (sseq->mainb == SEQ_DRAW_SEQUENCE) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); + return 0; + } + break; + } + case SPACE_IMAGE: + { + /* SpaceImage *sima = curarea->spacedata.first; */ + + /* set the current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_ima_align; + break; + } + case SPACE_CLIP: + { + SpaceClip *sc = curarea->spacedata.first; + MovieClip *clip = ED_space_clip_get_clip(sc); + + if (clip == NULL) { + p->status = GP_STATUS_ERROR; + return false; + } + + /* set the current area */ + p->sa = curarea; + p->ar = ar; + p->v2d = &ar->v2d; + p->align_flag = &ts->gpencil_v2d_align; + + invert_m4_m4(p->imat, sc->unistabmat); + + /* custom color for new layer */ + p->custom_color[0] = 1.0f; + p->custom_color[1] = 0.0f; + p->custom_color[2] = 0.5f; + p->custom_color[3] = 0.9f; + + if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { + int framenr = ED_space_clip_get_clip_frame_number(sc); + MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); + MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : NULL; + + if (marker) { + p->imat[3][0] -= marker->pos[0]; + p->imat[3][1] -= marker->pos[1]; + } + else { + p->status = GP_STATUS_ERROR; + return false; + } + } + + invert_m4_m4(p->mat, p->imat); + copy_m4_m4(p->gsc.mat, p->mat); + break; + } + /* unsupported views */ + default: + { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Annotations are not supported in this editor\n"); + return 0; + } + } + + /* get gp-data */ + gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); + if ((gpd_ptr == NULL) || !ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Current context doesn't allow for any Annotation data\n"); + return 0; + } + else { + /* if no existing GPencil block exists, add one */ + if (*gpd_ptr == NULL) { + bGPdata *gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); + *gpd_ptr = gpd; + + /* mark datablock as being used for annotations */ + gpd->flag |= GP_DATA_ANNOTATIONS; + + /* annotations always in front of all objects */ + gpd->xray_mode = GP_XRAY_FRONT; + } + p->gpd = *gpd_ptr; + } + + if (ED_gpencil_session_active() == 0) { + /* initialize undo stack, + * also, existing undo stack would make buffer drawn + */ + gpencil_undo_init(p->gpd); + } + + /* clear out buffer (stored in gp-data), in case something contaminated it */ + gp_session_validatebuffer(p); + + /* lock axis */ + p->lock_axis = ts->gp_sculpt.lock_axis; + + return 1; +} + +/* init new painting session */ +static tGPsdata *gp_session_initpaint(bContext *C) +{ + tGPsdata *p = NULL; + + /* create new context data */ + p = MEM_callocN(sizeof(tGPsdata), "Annotation Drawing Data"); + + gp_session_initdata(C, p); + + /* radius for eraser circle is defined in userprefs now */ + /* NOTE: we do this here, so that if we exit immediately, + * erase size won't get lost + */ + p->radius = U.gp_eraser; + + /* return context data for running paint operator */ + return p; +} + +/* cleanup after a painting session */ +static void gp_session_cleanup(tGPsdata *p) +{ + bGPdata *gpd = (p) ? p->gpd : NULL; + + /* error checking */ + if (gpd == NULL) + return; + + /* free stroke buffer */ + if (gpd->runtime.sbuffer) { + /* printf("\t\tGP - free sbuffer\n"); */ + MEM_freeN(gpd->runtime.sbuffer); + gpd->runtime.sbuffer = NULL; + } + + /* clear flags */ + gpd->runtime.sbuffer_size = 0; + gpd->runtime.sbuffer_sflag = 0; + p->inittime = 0.0; +} + +static void gp_session_free(tGPsdata *p) +{ + MEM_freeN(p); +} + + +/* init new stroke */ +static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Depsgraph *depsgraph) +{ + Scene *scene = p->scene; + ToolSettings *ts = scene->toolsettings; + + /* get active layer (or add a new one if non-existent) */ + p->gpl = BKE_gpencil_layer_getactive(p->gpd); + if (p->gpl == NULL) { + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("Note"), true); + + if (p->custom_color[3]) + copy_v3_v3(p->gpl->color, p->custom_color); + } + if (p->gpl->flag & GP_LAYER_LOCKED) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: Cannot paint on locked layer\n"); + return; + } + + /* get active frame (add a new one if not matching frame) */ + if (paintmode == GP_PAINTMODE_ERASER) { + /* Eraser mode: + * 1) Only allow erasing on the active layer (unlike for 3d-art Grease Pencil), + * since we won't be exposing layer locking in the UI + * 2) Ensure that p->gpf refers to the frame used for the active layer + * (to avoid problems with other tools which expect it to exist) + */ + bool has_layer_to_erase = false; + + if (gpencil_layer_is_editable(p->gpl)) { + /* Ensure that there's stuff to erase here (not including selection mask below)... */ + if (p->gpl->actframe && p->gpl->actframe->strokes.first) { + has_layer_to_erase = true; + } + } + + /* Ensure active frame is set correctly... */ + p->gpf = p->gpl->actframe; + + /* Restrict eraser to only affecting selected strokes, if the "selection mask" is on + * (though this is only available in editmode) + */ + if (p->gpd->flag & GP_DATA_STROKE_EDITMODE) { + if (ts->gp_sculpt.flag & GP_BRUSHEDIT_FLAG_SELECT_MASK) { + p->flags |= GP_PAINTFLAG_SELECTMASK; + } + } + + if (has_layer_to_erase == false) { + p->status = GP_STATUS_ERROR; + //if (G.debug & G_DEBUG) + printf("Error: Eraser will not be affecting anything (gpencil_paint_init)\n"); + return; + } + } + else { + /* Drawing Modes - Add a new frame if needed on the active layer */ + short add_frame_mode = GP_GETFRAME_ADD_NEW; + + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) + add_frame_mode = GP_GETFRAME_ADD_COPY; + else + add_frame_mode = GP_GETFRAME_ADD_NEW; + + p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + + if (p->gpf == NULL) { + p->status = GP_STATUS_ERROR; + if (G.debug & G_DEBUG) + printf("Error: No frame created (gpencil_paint_init)\n"); + return; + } + else { + p->gpf->flag |= GP_FRAME_PAINT; + } + } + + /* set 'eraser' for this stroke if using eraser */ + p->paintmode = paintmode; + if (p->paintmode == GP_PAINTMODE_ERASER) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; + + /* check if we should respect depth while erasing */ + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->gpl->flag & GP_LAYER_NO_XRAY) { + p->flags |= GP_PAINTFLAG_V3D_ERASER_DEPTH; + } + } + } + else { + /* disable eraser flags - so that we can switch modes during a session */ + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; + + if (p->sa->spacetype == SPACE_VIEW3D) { + if (p->gpl->flag & GP_LAYER_NO_XRAY) { + p->flags &= ~GP_PAINTFLAG_V3D_ERASER_DEPTH; + } + } + } + + /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ + p->flags |= GP_PAINTFLAG_FIRSTRUN; + + + /* when drawing in the camera view, in 2D space, set the subrect */ + p->subrect = NULL; + if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { + if (p->sa->spacetype == SPACE_VIEW3D) { + View3D *v3d = p->sa->spacedata.first; + RegionView3D *rv3d = p->ar->regiondata; + + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + ED_view3d_calc_camera_border(p->scene, depsgraph, p->ar, v3d, rv3d, &p->subrect_data, true); /* no shift */ + p->subrect = &p->subrect_data; + } + } + } + + /* init stroke point space-conversion settings... */ + p->gsc.gpd = p->gpd; + p->gsc.gpl = p->gpl; + + p->gsc.sa = p->sa; + p->gsc.ar = p->ar; + p->gsc.v2d = p->v2d; + + p->gsc.subrect_data = p->subrect_data; + p->gsc.subrect = p->subrect; + + copy_m4_m4(p->gsc.mat, p->mat); + + + /* check if points will need to be made in view-aligned space */ + if (*p->align_flag & GP_PROJECT_VIEWSPACE) { + switch (p->sa->spacetype) { + case SPACE_VIEW3D: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; + break; + } + case SPACE_NODE: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + break; + } + case SPACE_SEQ: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima = (SpaceImage *)p->sa->spacedata.first; + + /* only set these flags if the image editor doesn't have an image active, + * otherwise user will be confused by strokes not appearing after they're drawn + * + * Admittedly, this is a bit hacky, but it works much nicer from an ergonomic standpoint! + */ + if (ELEM(NULL, sima, sima->image)) { + /* make strokes be drawn in screen space */ + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_2DSPACE; + *(p->align_flag) &= ~GP_PROJECT_VIEWSPACE; + } + else { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + } + break; + } + case SPACE_CLIP: + { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_2DSPACE; + break; + } + } + } +} + +/* finish off a stroke (clears buffer, but doesn't finish the paint operation) */ +static void gp_paint_strokeend(tGPsdata *p) +{ + ToolSettings *ts = p->scene->toolsettings; + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (gpencil_project_check(p)) { + View3D *v3d = p->sa->spacedata.first; + + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(p->win, p->ar); + ED_view3d_autodist_init(p->depsgraph, p->ar, v3d, (ts->annotate_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); + } + + /* check if doing eraser or not */ + if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { + /* simplify stroke before transferring? */ + gp_stroke_simplify(p); + + /* transfer stroke to frame */ + gp_stroke_newfrombuffer(p); + } + + /* clean up buffer now */ + gp_session_validatebuffer(p); +} + +/* finish off stroke painting operation */ +static void gp_paint_cleanup(tGPsdata *p) +{ + /* p->gpd==NULL happens when stroke failed to initialize, + * for example when GP is hidden in current space (sergey) + */ + if (p->gpd) { + /* finish off a stroke */ + gp_paint_strokeend(p); + } + + /* "unlock" frame */ + if (p->gpf) + p->gpf->flag &= ~GP_FRAME_PAINT; +} + +/* ------------------------------- */ + +/* Helper callback for drawing the cursor itself */ +static void gpencil_draw_eraser(bContext *UNUSED(C), int x, int y, void *p_ptr) +{ + tGPsdata *p = (tGPsdata *)p_ptr; + + if (p->paintmode == GP_PAINTMODE_ERASER) { + GPUVertFormat *format = immVertexFormat(); + const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + GPU_line_smooth(true); + GPU_blend(true); + GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); + + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(shdr_pos, x, y, p->radius, 40); + + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("dash_factor", 0.5f); + + imm_draw_circle_wire_2d(shdr_pos, x, y, p->radius, + /* XXX Dashed shader gives bad results with sets of small segments currently, + * temp hack around the issue. :( */ + max_ii(8, p->radius / 2)); /* was fixed 40 */ + + immUnbindProgram(); + + GPU_blend(false); + GPU_line_smooth(false); + } +} + +/* Turn brush cursor in 3D view on/off */ +static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short enable) +{ + if (p->erasercursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), p->erasercursor); + p->erasercursor = NULL; + } + else if (enable && !p->erasercursor) { + /* enable cursor */ + p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), + NULL, /* XXX */ + gpencil_draw_eraser, p); + } +} + +/* Check if tablet eraser is being used (when processing events) */ +static bool gpencil_is_tablet_eraser_active(const wmEvent *event) +{ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + return (wmtab->Active == EVT_TABLET_ERASER); + } + + return false; +} + +/* ------------------------------- */ + +static void gpencil_draw_exit(bContext *C, wmOperator *op) +{ + tGPsdata *p = op->customdata; + + /* clear undo stack */ + gpencil_undo_finish(); + + /* restore cursor to indicate end of drawing */ + WM_cursor_modal_restore(CTX_wm_window(C)); + + /* don't assume that operator data exists at all */ + if (p) { + /* check size of buffer before cleanup, to determine if anything happened here */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* 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. + */ + U.gp_eraser = p->radius; + + /* cleanup */ + gp_paint_cleanup(p); + gp_session_cleanup(p); + gp_session_free(p); + } + + op->customdata = NULL; +} + +static void gpencil_draw_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_draw_exit(C, op); +} + +/* ------------------------------- */ + + +static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPsdata *p; + eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); + + /* check context */ + p = op->customdata = gp_session_initpaint(C); + if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { + /* something wasn't set correctly in context */ + gpencil_draw_exit(C, op); + return 0; + } + + /* init painting data */ + gp_paint_initstroke(p, paintmode, CTX_data_depsgraph(C)); + if (p->status == GP_STATUS_ERROR) { + gpencil_draw_exit(C, op); + return 0; + } + + if (event != NULL) { + p->keymodifier = event->keymodifier; + } + else { + p->keymodifier = -1; + } + + /* everything is now setup ok */ + return 1; +} + + +/* ------------------------------- */ + +/* ensure that the correct cursor icon is set */ +static void gpencil_draw_cursor_set(tGPsdata *p) +{ + if (p->paintmode == GP_PAINTMODE_ERASER) + WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ + else + WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); +} + +/* update UI indicators of status, including cursor and header prints */ +static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) +{ + /* header prints */ + switch (p->status) { + case GP_STATUS_PAINTING: + switch (p->paintmode) { + case GP_PAINTMODE_DRAW_POLY: + /* Provide usage tips, since this is modal, and unintuitive without hints */ + ED_workspace_status_text(C, IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)")); + break; + default: + /* Do nothing - the others are self explanatory, exit quickly once the mouse is released + * Showing any text would just be annoying as it would flicker. + */ + break; + } + break; + + case GP_STATUS_IDLING: + /* print status info */ + switch (p->paintmode) { + case GP_PAINTMODE_ERASER: + ED_workspace_status_text(C, IFACE_("Annotation Eraser: Hold and drag LMB or RMB to erase | " + "ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW_STRAIGHT: + ED_workspace_status_text(C, IFACE_("Annotation Line Draw: Hold and drag LMB to draw | " + "ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW: + ED_workspace_status_text(C, IFACE_("Annotation Freehand Draw: Hold and drag LMB to draw | " + "E/ESC/Enter to end (or click outside this area)")); + break; + case GP_PAINTMODE_DRAW_POLY: + ED_workspace_status_text(C, IFACE_("Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)")); + break; + + default: /* unhandled future cases */ + ED_workspace_status_text(C, IFACE_("Annotation Session: ESC/Enter to end (or click outside this area)")); + break; + } + break; + + case GP_STATUS_ERROR: + case GP_STATUS_DONE: + /* clear status string */ + ED_workspace_status_text(C, NULL); + break; + } +} + +/* ------------------------------- */ + +/* create a new stroke point at the point indicated by the painting context */ +static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) +{ + /* handle drawing/erasing -> test for erasing first */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + /* do 'live' erasing now */ + gp_stroke_doeraser(p); + + /* store used values */ + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + } + /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ + else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { + /* try to add point */ + short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + + /* handle errors while adding point */ + if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { + /* finish off old stroke */ + gp_paint_strokeend(p); + /* And start a new one!!! Else, projection errors! */ + gp_paint_initstroke(p, p->paintmode, depsgraph); + + /* start a new stroke, starting from previous point */ + /* XXX Must manually reset inittime... */ + /* XXX We only need to reuse previous point if overflow! */ + if (ok == GP_STROKEADD_OVERFLOW) { + p->inittime = p->ocurtime; + gp_stroke_addpoint(p, p->mvalo, p->opressure, p->ocurtime); + } + else { + p->inittime = p->curtime; + } + gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); + } + else if (ok == GP_STROKEADD_INVALID) { + /* the painting operation cannot continue... */ + BKE_report(op->reports, RPT_ERROR, "Cannot paint stroke"); + p->status = GP_STATUS_ERROR; + + if (G.debug & G_DEBUG) + printf("Error: Grease-Pencil Paint - Add Point Invalid\n"); + return; + } + + /* store used values */ + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->ocurtime = p->curtime; + } +} + +/* handle draw event */ +static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsgraph *depsgraph) +{ + tGPsdata *p = op->customdata; + PointerRNA itemptr; + float mousef[2]; + int tablet = 0; + + /* convert from window-space to area-space mouse coordinates + * NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding... + */ + 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) */ + if (event->tablet_data) { + const wmTabletData *wmtab = event->tablet_data; + + tablet = (wmtab->Active != EVT_TABLET_NONE); + p->pressure = wmtab->Pressure; + + /* Hack for pressure sensitive eraser on D+RMB when using a tablet: + * The pen has to float over the tablet surface, resulting in + * zero pressure (T47101). Ignore pressure values if floating + * (i.e. "effectively zero" pressure), and only when the "active" + * end is the stylus (i.e. the default when not eraser) + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if ((wmtab->Active != EVT_TABLET_ERASER) && (p->pressure < 0.001f)) { + p->pressure = 1.0f; + } + } + } + else { + /* No tablet data -> No pressure info is available */ + p->pressure = 1.0f; + } + + /* special exception for start of strokes (i.e. maybe for just a dot) */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + p->mvalo[0] = p->mval[0]; + 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... + */ + if (tablet && (p->pressure >= 0.99f)) + 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); + + mousef[0] = p->mval[0]; + mousef[1] = p->mval[1]; + RNA_float_set_array(&itemptr, "mouse", mousef); + RNA_float_set(&itemptr, "pressure", p->pressure); + RNA_boolean_set(&itemptr, "is_start", (p->flags & GP_PAINTFLAG_FIRSTRUN) != 0); + + RNA_float_set(&itemptr, "time", p->curtime - p->inittime); + + /* apply the current latest drawing point */ + gpencil_draw_apply(op, p, depsgraph); + + /* force refresh */ + ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ +} + +/* ------------------------------- */ + +/* operator 'redo' (i.e. after changing some properties, but also for repeat last) */ +static int gpencil_draw_exec(bContext *C, wmOperator *op) +{ + tGPsdata *p = NULL; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + + /* printf("GPencil - Starting Re-Drawing\n"); */ + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, NULL)) { + if (op->customdata) MEM_freeN(op->customdata); + /* printf("\tGP - no valid data\n"); */ + return OPERATOR_CANCELLED; + } + else + p = op->customdata; + + /* printf("\tGP - Start redrawing stroke\n"); */ + + /* loop over the stroke RNA elements recorded (i.e. progress of mouse movement), + * setting the relevant values in context at each step, then applying + */ + RNA_BEGIN (op->ptr, itemptr, "stroke") + { + float mousef[2]; + + /* printf("\t\tGP - stroke elem\n"); */ + + /* get relevant data for this point from stroke */ + RNA_float_get_array(&itemptr, "mouse", mousef); + p->mval[0] = (int)mousef[0]; + p->mval[1] = (int)mousef[1]; + p->pressure = RNA_float_get(&itemptr, "pressure"); + p->curtime = (double)RNA_float_get(&itemptr, "time") + p->inittime; + + if (RNA_boolean_get(&itemptr, "is_start")) { + /* if first-run flag isn't set already (i.e. not true first stroke), + * then we must terminate the previous one first before continuing + */ + if ((p->flags & GP_PAINTFLAG_FIRSTRUN) == 0) { + /* TODO: both of these ops can set error-status, but we probably don't need to worry */ + gp_paint_strokeend(p); + gp_paint_initstroke(p, p->paintmode, depsgraph); + } + } + + /* if first run, set previous data too */ + if (p->flags & GP_PAINTFLAG_FIRSTRUN) { + p->flags &= ~GP_PAINTFLAG_FIRSTRUN; + + p->mvalo[0] = p->mval[0]; + p->mvalo[1] = p->mval[1]; + p->opressure = p->pressure; + p->ocurtime = p->curtime; + } + + /* apply this data as necessary now (as per usual) */ + gpencil_draw_apply(op, p, depsgraph); + } + RNA_END; + + /* printf("\tGP - done\n"); */ + + /* cleanup */ + gpencil_draw_exit(C, op); + + /* refreshes */ + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* done */ + return OPERATOR_FINISHED; +} + +/* ------------------------------- */ + +/* start of interactive drawing part of operator */ +static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + tGPsdata *p = NULL; + + /* GPXX Need a better solution */ + if ((ob != NULL) && (ob->type == OB_GPENCIL)) { + BKE_report(op->reports, RPT_ERROR, "Cannot draw annotation with a Grease Pencil object active"); + return OPERATOR_CANCELLED; + } + + if (G.debug & G_DEBUG) + printf("GPencil - Starting Drawing\n"); + + /* try to initialize context data needed while drawing */ + if (!gpencil_draw_init(C, op, event)) { + if (op->customdata) + MEM_freeN(op->customdata); + if (G.debug & G_DEBUG) + printf("\tGP - no valid data\n"); + return OPERATOR_CANCELLED; + } + else + p = op->customdata; + + /* TODO: set any additional settings that we can take from the events? + * TODO? if tablet is erasing, force eraser to be on? */ + + /* TODO: move cursor setting stuff to stroke-start so that paintmode can be changed midway... */ + + /* if eraser is on, draw radial aid */ + 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, CTX_data_depsgraph(C)); + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + else { + /* toolbar invoked - don't start drawing yet... */ + /* printf("\tGP - hotkey invoked... waiting for click-drag\n"); */ + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + /* add a modal handler for this operator, so that we can then draw continuous strokes */ + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +/* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ +static bool gpencil_area_exists(bContext *C, ScrArea *sa_test) +{ + bScreen *sc = CTX_wm_screen(C); + return (BLI_findindex(&sc->areabase, sa_test) != -1); +} + +static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) +{ + tGPsdata *p = op->customdata; + + /* we must check that we're still within the area that we're set up to work from + * otherwise we could crash (see bug #20586) + */ + if (CTX_wm_area(C) != p->sa) { + printf("\t\t\tGP - wrong area execution abort!\n"); + p->status = GP_STATUS_ERROR; + } + + /* printf("\t\tGP - start stroke\n"); */ + + /* we may need to set up paint env again if we're resuming */ + /* XXX: watch it with the paintmode! in future, + * it'd be nice to allow changing paint-mode when in sketching-sessions */ + + if (gp_session_initdata(C, p)) + gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); + + if (p->status != GP_STATUS_ERROR) { + p->status = GP_STATUS_PAINTING; + op->flag &= ~OP_IS_MODAL_CURSOR_REGION; + } + + return op->customdata; +} + +static void gpencil_stroke_end(wmOperator *op) +{ + tGPsdata *p = op->customdata; + + gp_paint_cleanup(p); + + gpencil_undo_push(p->gpd); + + gp_session_cleanup(p); + + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; + + p->gpd = NULL; + p->gpl = NULL; + p->gpf = NULL; +} + +/* events handling during interactive drawing part of operator */ +static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPsdata *p = op->customdata; + int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through to support MMB view nav, etc. */ + + /* if (event->type == NDOF_MOTION) + * return OPERATOR_PASS_THROUGH; + * ------------------------------- + * [mce] Not quite what I was looking + * for, but a good start! GP continues to + * draw on the screen while the 3D mouse + * moves the viewpoint. Problem is that + * the stroke is converted to 3D only after + * it is finished. This approach should work + * better in tools that immediately apply + * in 3D space. + */ + + if (p->status == GP_STATUS_IDLING) { + ARegion *ar = CTX_wm_region(C); + p->ar = ar; + } + + /* we don't pass on key events, GP is used with key-modifiers - prevents Dkey to insert drivers */ + if (ISKEYBOARD(event->type)) { + if (ELEM(event->type, LEFTARROWKEY, DOWNARROWKEY, RIGHTARROWKEY, UPARROWKEY, ZKEY)) { + /* allow some keys: + * - for frame changing [#33412] + * - for undo (during sketching sessions) + */ + } + else if (ELEM(event->type, PAD0, PAD1, PAD2, PAD3, PAD4, PAD5, PAD6, PAD7, PAD8, PAD9)) { + /* allow numpad keys so that camera/view manipulations can still take place + * - PAD0 in particular is really important for Grease Pencil drawing, + * as animators may be working "to camera", so having this working + * is essential for ensuring that they can quickly return to that view + */ + } + else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { + /* Add Blank Frame + * - Since this operator is non-modal, we can just call it here, and keep going... + * - This operator is especially useful when animating + */ + WM_operator_name_call(C, "GPENCIL_OT_blank_frame_add", WM_OP_EXEC_DEFAULT, NULL); + estate = OPERATOR_RUNNING_MODAL; + } + else { + estate = OPERATOR_RUNNING_MODAL; + } + } + + //printf("\tGP - handle modal event...\n"); + + /* 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, 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; + estate = OPERATOR_FINISHED; + } + + /* toggle painting mode upon mouse-button movement + * - LEFTMOUSE = standard drawing (all) / straight line drawing (all) / polyline (toolbox only) + * - RIGHTMOUSE = polyline (hotkey) / eraser (all) + * (Disabling RIGHTMOUSE case here results in bugs like [#32647]) + * also making sure we have a valid event value, to not exit too early + */ + if (ELEM(event->type, LEFTMOUSE, RIGHTMOUSE) && (ELEM(event->val, KM_PRESS, KM_RELEASE))) { + /* if painting, end stroke */ + if (p->status == GP_STATUS_PAINTING) { + int sketch = 0; + + /* basically, this should be mouse-button up = end stroke + * BUT, polyline drawing is an exception -- all knots should be added during one session + */ + sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); + + if (sketch) { + /* end stroke only, and then wait to resume painting soon */ + /* printf("\t\tGP - end stroke only\n"); */ + gpencil_stroke_end(op); + + /* If eraser mode is on, turn it off after the stroke finishes + * NOTE: This just makes it nicer to work with drawing sessions + */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + p->paintmode = RNA_enum_get(op->ptr, "mode"); + + /* if the original mode was *still* eraser, + * we'll let it say for now, since this gives + * users an opportunity to have visual feedback + * when adjusting eraser size + */ + if (p->paintmode != GP_PAINTMODE_ERASER) { + /* turn off cursor... + * NOTE: this should be enough for now + * Just hiding this makes it seem like + * you can paint again... + */ + gpencil_draw_toggle_eraser_cursor(C, p, false); + } + } + + /* we've just entered idling state, so this event was processed (but no others yet) */ + estate = OPERATOR_RUNNING_MODAL; + + /* stroke could be smoothed, send notifier to refresh screen */ + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + } + else { + /* printf("\t\tGP - end of stroke + op\n"); */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_PRESS) { + bool in_bounds = false; + + /* Check if we're outside the bounds of the active region + * NOTE: An exception here is that if launched from the toolbar, + * whatever region we're now in should become the new region + */ + if ((p->ar) && (p->ar->regiontype == RGN_TYPE_TOOLS)) { + /* Change to whatever region is now under the mouse */ + ARegion *current_region = BKE_area_find_region_xy(p->sa, RGN_TYPE_ANY, event->x, event->y); + + if (G.debug & G_DEBUG) { + printf("found alternative region %p (old was %p) - at %d %d (sa: %d %d -> %d %d)\n", + current_region, p->ar, event->x, event->y, + p->sa->totrct.xmin, p->sa->totrct.ymin, p->sa->totrct.xmax, p->sa->totrct.ymax); + } + + if (current_region) { + /* Assume that since we found the cursor in here, it is in bounds + * and that this should be the region that we begin drawing in + */ + p->ar = current_region; + in_bounds = true; + } + else { + /* Out of bounds, or invalid in some other way */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: Region under cursor is out of bounds, so cannot be drawn on\n", __func__); + } + } + else if (p->ar) { + rcti region_rect; + + /* Perform bounds check using */ + ED_region_visible_rect(p->ar, ®ion_rect); + in_bounds = BLI_rcti_isect_pt_v(®ion_rect, event->mval); + } + else { + /* No region */ + p->status = GP_STATUS_ERROR; + estate = OPERATOR_CANCELLED; + + if (G.debug & G_DEBUG) + printf("%s: No active region found in GP Paint session data\n", __func__); + } + + if (in_bounds) { + /* Switch paintmode (temporarily if need be) based on which button was used + * NOTE: This is to make it more convenient to erase strokes when using drawing sessions + */ + if ((event->type == RIGHTMOUSE) || gpencil_is_tablet_eraser_active(event)) { + /* turn on eraser */ + p->paintmode = GP_PAINTMODE_ERASER; + } + else if (event->type == LEFTMOUSE) { + /* restore drawmode to default */ + p->paintmode = RNA_enum_get(op->ptr, "mode"); + } + + gpencil_draw_toggle_eraser_cursor(C, p, p->paintmode == GP_PAINTMODE_ERASER); + + /* not painting, so start stroke (this should be mouse-button down) */ + p = gpencil_stroke_begin(C, op); + + if (p->status == GP_STATUS_ERROR) { + estate = OPERATOR_CANCELLED; + } + } + else if (p->status != GP_STATUS_ERROR) { + /* User clicked outside bounds of window while idling, so exit paintmode + * NOTE: Don't enter this case if an error occurred while finding the + * region (as above) + */ + p->status = GP_STATUS_DONE; + estate = OPERATOR_FINISHED; + } + } + else if (event->val == KM_RELEASE) { + p->status = GP_STATUS_IDLING; + op->flag |= OP_IS_MODAL_CURSOR_REGION; + } + } + + /* handle mode-specific events */ + if (p->status == GP_STATUS_PAINTING) { + /* handle painting mouse-movements? */ + if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { + /* handle drawing event */ + /* printf("\t\tGP - add point\n"); */ + gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); + + /* finish painting operation if anything went wrong just now */ + if (p->status == GP_STATUS_ERROR) { + printf("\t\t\t\tGP - add error done!\n"); + estate = OPERATOR_CANCELLED; + } + else { + /* event handled, so just tag as running modal */ + /* printf("\t\t\t\tGP - add point handled!\n"); */ + estate = OPERATOR_RUNNING_MODAL; + } + } + /* eraser size */ + else if ((p->paintmode == GP_PAINTMODE_ERASER) && + ELEM(event->type, WHEELUPMOUSE, WHEELDOWNMOUSE, PADPLUSKEY, PADMINUS)) + { + /* just resize the brush (local version) + * TODO: fix the hardcoded size jumps (set to make a visible difference) and hardcoded keys + */ + /* printf("\t\tGP - resize eraser\n"); */ + switch (event->type) { + case WHEELDOWNMOUSE: /* larger */ + case PADPLUSKEY: + p->radius += 5; + break; + + case WHEELUPMOUSE: /* smaller */ + case PADMINUS: + p->radius -= 5; + + if (p->radius <= 0) + p->radius = 1; + break; + } + + /* force refresh */ + ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ + + /* event handled, so just tag as running modal */ + estate = OPERATOR_RUNNING_MODAL; + } + /* there shouldn't be any other events, but just in case there are, let's swallow them + * (i.e. to prevent problems with undo) + */ + else { + /* swallow event to save ourselves trouble */ + estate = OPERATOR_RUNNING_MODAL; + } + } + + /* gpencil modal operator stores area, which can be removed while using it (like fullscreen) */ + if (0 == gpencil_area_exists(C, p->sa)) + estate = OPERATOR_CANCELLED; + else { + /* update status indicators - cursor, header, etc. */ + gpencil_draw_status_indicators(C, p); + gpencil_draw_cursor_set(p); /* cursor may have changed outside our control - T44084 */ + } + + /* process last operations before exiting */ + switch (estate) { + case OPERATOR_FINISHED: + /* one last flush before we're done */ + gpencil_draw_exit(C, op); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + break; + + case OPERATOR_CANCELLED: + gpencil_draw_exit(C, op); + break; + + case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: + /* event doesn't need to be handled */ +#if 0 + printf("unhandled event -> %d (mmb? = %d | mmv? = %d)\n", + event->type, event->type == MIDDLEMOUSE, event->type==MOUSEMOVE); +#endif + break; + } + + /* return status code */ + return estate; +} + +/* ------------------------------- */ + +static const EnumPropertyItem prop_gpencil_drawmodes[] = { + {GP_PAINTMODE_DRAW, "DRAW", 0, "Draw Freehand", "Draw freehand stroke(s)"}, + {GP_PAINTMODE_DRAW_STRAIGHT, "DRAW_STRAIGHT", 0, "Draw Straight Lines", "Draw straight line segment(s)"}, + {GP_PAINTMODE_DRAW_POLY, "DRAW_POLY", 0, "Draw Poly Line", "Click to place endpoints of straight line segments (connected)"}, + {GP_PAINTMODE_ERASER, "ERASER", 0, "Eraser", "Erase Annotation strokes"}, + {0, NULL, 0, NULL, NULL} +}; + +void GPENCIL_OT_annotate(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Annotation Draw"; + ot->idname = "GPENCIL_OT_annotate"; + ot->description = "Make annotations on the active data"; + + /* api callbacks */ + ot->exec = gpencil_draw_exec; + ot->invoke = gpencil_draw_invoke; + ot->modal = gpencil_draw_modal; + ot->cancel = gpencil_draw_cancel; + ot->poll = gpencil_draw_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* settings for drawing */ + ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_drawmodes, 0, "Mode", "Way to interpret mouse movements"); + + prop = RNA_def_collection_runtime(ot->srna, "stroke", &RNA_OperatorStrokeElement, "Stroke", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ + prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/drawgpencil.c b/source/blender/editors/gpencil/drawgpencil.c index 29b24886017..180fb65e743 100644 --- a/source/blender/editors/gpencil/drawgpencil.c +++ b/source/blender/editors/gpencil/drawgpencil.c @@ -46,6 +46,7 @@ #include "BLF_api.h" #include "BLT_translation.h" +#include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -55,8 +56,13 @@ #include "DNA_object_types.h" #include "BKE_context.h" +#include "BKE_brush.h" #include "BKE_global.h" +#include "BKE_paint.h" #include "BKE_gpencil.h" +#include "BKE_image.h" + +#include "DEG_depsgraph.h" #include "WM_api.h" @@ -74,6 +80,10 @@ #include "UI_interface_icons.h" #include "UI_resources.h" +#include "IMB_imbuf_types.h" + +#include "gpencil_intern.h" + /* ************************************************** */ /* GREASE PENCIL DRAWING */ @@ -88,8 +98,7 @@ typedef enum eDrawStrokeFlags { GP_DRAWDATA_NO_XRAY = (1 << 5), /* don't draw xray in 3D view (which is default) */ GP_DRAWDATA_NO_ONIONS = (1 << 6), /* no onionskins should be drawn (for animation playback) */ GP_DRAWDATA_VOLUMETRIC = (1 << 7), /* draw strokes as "volumetric" circular billboards */ - GP_DRAWDATA_FILL = (1 << 8), /* fill insides/bounded-regions of strokes */ - GP_DRAWDATA_HQ_FILL = (1 << 9) /* Use high quality fill */ + GP_DRAWDATA_FILL = (1 << 8) /* fill insides/bounded-regions of strokes */ } eDrawStrokeFlags; @@ -100,12 +109,12 @@ typedef enum eDrawStrokeFlags { #endif /* conversion utility (float --> normalized unsigned byte) */ -#define F2UB(x) (unsigned char)(255.0f * x) +#define F2UB(x) (uchar)(255.0f * x) /* ----- Tool Buffer Drawing ------ */ /* helper functions to set color of buffer point */ -static void gp_set_tpoint_varying_color(const tGPspoint *pt, const float ink[4], unsigned attrib_id) +static void gp_set_tpoint_varying_color(const tGPspoint *pt, const float ink[4], uint attrib_id) { float alpha = ink[3] * pt->strength; CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); @@ -119,7 +128,7 @@ static void gp_set_point_uniform_color(const bGPDspoint *pt, const float ink[4]) immUniformColor3fvAlpha(ink, alpha); } -static void gp_set_point_varying_color(const bGPDspoint *pt, const float ink[4], unsigned attrib_id) +static void gp_set_point_varying_color(const bGPDspoint *pt, const float ink[4], uint attrib_id) { float alpha = ink[3] * pt->strength; CLAMP(alpha, GPENCIL_STRENGTH_MIN, 1.0f); @@ -134,7 +143,7 @@ static void gp_draw_stroke_buffer_fill(const tGPspoint *points, int totpoints, f } int tot_triangles = totpoints - 2; /* allocate memory for temporary areas */ - unsigned int(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * tot_triangles, "GP Stroke buffer temp triangulation"); + uint(*tmp_triangles)[3] = MEM_mallocN(sizeof(*tmp_triangles) * tot_triangles, "GP Stroke buffer temp triangulation"); float(*points2d)[2] = MEM_mallocN(sizeof(*points2d) * totpoints, "GP Stroke buffer temp 2d points"); /* Convert points to array and triangulate @@ -146,7 +155,7 @@ static void gp_draw_stroke_buffer_fill(const tGPspoint *points, int totpoints, f points2d[i][0] = pt->x; points2d[i][1] = pt->y; } - BLI_polyfill_calc((const float(*)[2])points2d, (unsigned int)totpoints, 0, (unsigned int(*)[3])tmp_triangles); + BLI_polyfill_calc((const float(*)[2])points2d, (uint)totpoints, 0, (uint(*)[3])tmp_triangles); /* draw triangulation data */ if (tot_triangles > 0) { @@ -253,7 +262,7 @@ static void gp_draw_stroke_buffer(const tGPspoint *points, int totpoints, short if (i != 0) { gp_set_tpoint_varying_color(pt - 1, ink, color); immVertex2iv(pos, &(pt - 1)->x); - ++draw_points; + draw_points++; } oldpressure = pt->pressure; /* reset our threshold */ @@ -262,7 +271,7 @@ static void gp_draw_stroke_buffer(const tGPspoint *points, int totpoints, short /* now the point we want */ gp_set_tpoint_varying_color(pt, ink, color); immVertex2iv(pos, &pt->x); - ++draw_points; + draw_points++; } /* need to have 2 points to avoid immEnd assert error */ if (draw_points < 2) { @@ -402,6 +411,50 @@ static void gp_draw_stroke_volumetric_3d( /* --------------- Stroke Fills ----------------- */ +/* calc bounding box in 2d using flat projection data */ +static void gp_calc_2d_bounding_box(const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], bool expand) +{ + copy_v2_v2(minv, points2d[0]); + copy_v2_v2(maxv, points2d[0]); + + for (int i = 1; i < totpoints; i++) { + /* min */ + if (points2d[i][0] < minv[0]) { + minv[0] = points2d[i][0]; + } + if (points2d[i][1] < minv[1]) { + minv[1] = points2d[i][1]; + } + /* max */ + if (points2d[i][0] > maxv[0]) { + maxv[0] = points2d[i][0]; + } + if (points2d[i][1] > maxv[1]) { + maxv[1] = points2d[i][1]; + } + } + /* If not expanded, use a perfect square */ + if (expand == false) { + if (maxv[0] > maxv[1]) { + maxv[1] = maxv[0]; + } + else { + maxv[0] = maxv[1]; + } + } +} + +/* calc texture coordinates using flat projected points */ +static void gp_calc_stroke_text_coordinates(const float(*points2d)[2], int totpoints, float minv[2], float maxv[2], float(*r_uv)[2]) +{ + float d[2]; + d[0] = maxv[0] - minv[0]; + d[1] = maxv[1] - minv[1]; + for (int i = 0; i < totpoints; i++) { + r_uv[i][0] = (points2d[i][0] - minv[0]) / d[0]; + r_uv[i][1] = (points2d[i][1] - minv[1]) / d[1]; + } +} /* Get points of stroke always flat to view not affected by camera view or view position */ static void gp_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*points2d)[2], int *r_direction) @@ -447,7 +500,6 @@ static void gp_stroke_2d_flat(const bGPDspoint *points, int totpoints, float(*po *r_direction = (int)locy[2]; } - /* Triangulate stroke for high quality fill (this is done only if cache is null or stroke was modified) */ static void gp_triangulate_stroke_fill(bGPDstroke *gps) { @@ -455,14 +507,23 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) /* 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"); + uint (*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"); + float(*uv)[2] = MEM_mallocN(sizeof(*uv) * gps->totpoints, "GP Stroke temp 2d uv data"); int direction = 0; /* convert to 2d and triangulate */ gp_stroke_2d_flat(gps->points, gps->totpoints, points2d, &direction); - BLI_polyfill_calc(points2d, (unsigned int)gps->totpoints, direction, tmp_triangles); + BLI_polyfill_calc(points2d, (uint)gps->totpoints, direction, tmp_triangles); + + /* calc texture coordinates automatically */ + float minv[2]; + float maxv[2]; + /* first needs bounding box data */ + gp_calc_2d_bounding_box((const float(*)[2])points2d, gps->totpoints, minv, maxv, false); + /* calc uv data */ + gp_calc_stroke_text_coordinates((const float(*)[2])points2d, gps->totpoints, minv, maxv, uv); /* Number of triangles */ gps->tot_triangles = gps->totpoints - 2; @@ -476,7 +537,12 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) } for (int i = 0; i < gps->tot_triangles; i++) { - memcpy(gps->triangles[i].verts, tmp_triangles[i], sizeof(uint[3])); + bGPDtriangle *stroke_triangle = &gps->triangles[i]; + memcpy(stroke_triangle->verts, tmp_triangles[i], sizeof(uint[3])); + /* copy texture coordinates */ + copy_v2_v2(stroke_triangle->uv[0], uv[tmp_triangles[i][0]]); + copy_v2_v2(stroke_triangle->uv[1], uv[tmp_triangles[i][1]]); + copy_v2_v2(stroke_triangle->uv[2], uv[tmp_triangles[i][2]]); } } else { @@ -493,73 +559,128 @@ static void gp_triangulate_stroke_fill(bGPDstroke *gps) } /* clear memory */ - if (tmp_triangles) MEM_freeN(tmp_triangles); - if (points2d) MEM_freeN(points2d); + MEM_SAFE_FREE(tmp_triangles); + MEM_SAFE_FREE(points2d); + MEM_SAFE_FREE(uv); } - -/* draw fills for shapes */ -static void gp_draw_stroke_fill( - bGPdata *gpd, bGPDstroke *gps, - int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float color[4]) +/* add a new fill point and texture coordinates to vertex buffer */ +static void gp_add_filldata_tobuffer( + const bGPDspoint *pt, const float uv[2], uint pos, uint texcoord, short flag, + int offsx, int offsy, int winx, int winy, const float diff_mat[4][4]) { float fpt[3]; + float co[2]; - BLI_assert(gps->totpoints >= 3); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* if 2d, need conversion */ + if (!(flag & GP_STROKE_3DSPACE)) { + gp_calc_2d_stroke_fxy(fpt, flag, offsx, offsy, winx, winy, co); + copy_v2_v2(fpt, co); + fpt[2] = 0.0f; /* 2d always is z=0.0f */ + } - bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); + immAttrib2f(texcoord, uv[0], uv[1]); /* texture coordinates */ + immVertex3fv(pos, fpt); /* position */ +} - /* Triangulation fill if high quality flag is enabled */ - if (palcolor->flag & PC_COLOR_HQ_FILL) { - /* 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); - } - BLI_assert(gps->tot_triangles >= 1); +#if 0 /* GPXX disabled, not used in annotations */ +/* assign image texture for filling stroke */ +static int gp_set_filling_texture(Image *image, short flag) +{ + ImBuf *ibuf; + uint *bind = &image->bindcode[TEXTARGET_TEXTURE_2D]; + int error = GL_NO_ERROR; + ImageUser iuser = { NULL }; + void *lock; - uint pos; - if (gps->flag & GP_STROKE_3DSPACE) { - pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - } - else { - pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - } + iuser.ok = true; - immUniformColor4fv(color); + ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); - /* Draw all triangles for filling the polygon (cache must be calculated before) */ - immBegin(GPU_PRIM_TRIS, gps->tot_triangles * 3); - /* TODO: use batch instead of immediate mode, to share vertices */ + if (ibuf == NULL || ibuf->rect == NULL) { + BKE_image_release_ibuf(image, ibuf, NULL); + return (int)GL_INVALID_OPERATION; + } - bGPDtriangle *stroke_triangle = gps->triangles; - bGPDspoint *pt; + GPU_create_gl_tex(bind, ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, GL_TEXTURE_2D, + false, false, image); - if (gps->flag & GP_STROKE_3DSPACE) { - for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { - for (int j = 0; j < 3; j++) { - pt = &gps->points[stroke_triangle->verts[j]]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); - immVertex3fv(pos, fpt); - } - } - } - else { - for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { - for (int j = 0; j < 3; j++) { - float co[2]; - pt = &gps->points[stroke_triangle->verts[j]]; - mul_v3_m4v3(fpt, diff_mat, &pt->x); - gp_calc_2d_stroke_fxy(fpt, gps->flag, offsx, offsy, winx, winy, co); - immVertex3fv(pos, fpt); - } - } - } + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + if (flag & GP_STYLE_COLOR_TEX_CLAMP) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + BKE_image_release_ibuf(image, ibuf, NULL); - immEnd(); - immUnbindProgram(); + return error; +} +#endif + +/* draw fills for shapes */ +static void gp_draw_stroke_fill( + bGPdata *gpd, bGPDstroke *gps, + int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float color[4]) +{ + BLI_assert(gps->totpoints >= 3); + Material *ma = gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; + + /* 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); + } + BLI_assert(gps->tot_triangles >= 1); + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_GPENCIL_FILL); + + immUniformColor4fv(color); + immUniform4fv("color2", gp_style->mix_rgba); + immUniform1i("fill_type", gp_style->fill_style); + immUniform1f("mix_factor", gp_style->mix_factor); + + immUniform1f("gradient_angle", gp_style->gradient_angle); + immUniform1f("gradient_radius", gp_style->gradient_radius); + immUniform1f("pattern_gridsize", gp_style->pattern_gridsize); + immUniform2fv("gradient_scale", gp_style->gradient_scale); + immUniform2fv("gradient_shift", gp_style->gradient_shift); + + immUniform1f("texture_angle", gp_style->texture_angle); + immUniform2fv("texture_scale", gp_style->texture_scale); + immUniform2fv("texture_offset", gp_style->texture_offset); + immUniform1f("texture_opacity", gp_style->texture_opacity); + immUniform1i("t_mix", gp_style->flag & GP_STYLE_COLOR_TEX_MIX ? 1 : 0); + immUniform1i("t_flip", gp_style->flag & GP_STYLE_COLOR_FLIP_FILL ? 1 : 0); +#if 0 /* GPXX disabled, not used in annotations */ + /* image texture */ + if ((gp_style->fill_style == GP_STYLE_FILL_STYLE_TEXTURE) || (gp_style->flag & GP_STYLE_COLOR_TEX_MIX)) { + gp_set_filling_texture(gp_style->ima, gp_style->flag); } +#endif + /* Draw all triangles for filling the polygon (cache must be calculated before) */ + immBegin(GPU_PRIM_TRIS, gps->tot_triangles * 3); + /* TODO: use batch instead of immediate mode, to share vertices */ + + const bGPDtriangle *stroke_triangle = gps->triangles; + for (int i = 0; i < gps->tot_triangles; i++, stroke_triangle++) { + for (int j = 0; j < 3; j++) { + gp_add_filldata_tobuffer( + &gps->points[stroke_triangle->verts[j]], stroke_triangle->uv[j], + pos, texcoord, gps->flag, + offsx, offsy, winx, winy, diff_mat); + } + } + + immEnd(); + immUnbindProgram(); } /* ----- Existing Strokes Drawing (3D and Point) ------ */ @@ -601,87 +722,81 @@ static void gp_draw_stroke_point( immUnbindProgram(); } -/* draw a given stroke in 3d (i.e. in 3d-space), using simple ogl lines */ -static void gp_draw_stroke_3d(const bGPDspoint *points, int totpoints, short thickness, bool UNUSED(debug), - short UNUSED(sflag), const float diff_mat[4][4], const float ink[4], bool cyclic) +/* draw a given stroke in 3d (i.e. in 3d-space) */ +static void gp_draw_stroke_3d(tGPDdraw *tgpw, short thickness, const float ink[4], bool cyclic) { + bGPDspoint *points = tgpw->gps->points; + int totpoints = tgpw->gps->totpoints; + + const float viewport[2] = { (float)tgpw->winx, (float)tgpw->winy }; float curpressure = points[0].pressure; float fpt[3]; - float cyclic_fpt[3]; - int draw_points = 0; - - /* if cyclic needs one vertex more */ - int cyclic_add = 0; - if (cyclic) { - ++cyclic_add; - } + /* if cyclic needs more vertex */ + int cyclic_add = (cyclic) ? 1 : 0; GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + uint thickattrib = GPU_vertformat_attr_add(format, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR); + immBindBuiltinProgram(GPU_SHADER_GPENCIL_STROKE); + immUniform2fv("Viewport", viewport); + immUniform1f("pixsize", tgpw->rv3d->pixsize); + immUniform1f("pixelsize", U.pixelsize); + float obj_scale = (tgpw->ob->size[0] + tgpw->ob->size[1] + tgpw->ob->size[2]) / 3.0f; - /* TODO: implement this with a geometry shader to draw one continuous tapered stroke */ + immUniform1f("objscale", obj_scale); + int keep_size = (int)((tgpw->gpd) && (tgpw->gpd->flag & GP_DATA_STROKE_KEEPTHICKNESS)); + immUniform1i("keep_size", keep_size); + immUniform1i("pixfactor", tgpw->gpd->pixfactor); + immUniform1i("xraymode", tgpw->gpd->xray_mode); /* draw stroke curve */ GPU_line_width(max_ff(curpressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + immBeginAtMost(GPU_PRIM_LINE_STRIP_ADJ, totpoints + cyclic_add + 2); const bGPDspoint *pt = points; - for (int i = 0; i < totpoints; i++, pt++) { - gp_set_point_varying_color(pt, ink, color); - /* 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. - */ - if (fabsf(pt->pressure - curpressure) > 0.2f / (float)thickness) { - /* if the pressure changes before get at least 2 vertices, need to repeat last point to avoid assert in immEnd() */ - if (draw_points < 2) { - const bGPDspoint *pt2 = pt - 1; - mul_v3_m4v3(fpt, diff_mat, &pt2->x); - immVertex3fv(pos, fpt); + for (int i = 0; i < totpoints; i++, pt++) { + /* first point for adjacency (not drawn) */ + if (i == 0) { + gp_set_point_varying_color(points, ink, color); + immAttrib1f(thickattrib, max_ff(curpressure * thickness, 1.0f)); + if ((cyclic) && (totpoints > 2)) { + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 1)->x); } - immEnd(); - draw_points = 0; - - curpressure = pt->pressure; - GPU_line_width(max_ff(curpressure * thickness, 1.0f)); - immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints - i + 1 + cyclic_add); - - /* need to roll-back one point to ensure that there are no gaps in the stroke */ - if (i != 0) { - const bGPDspoint *pt2 = pt - 1; - mul_v3_m4v3(fpt, diff_mat, &pt2->x); - gp_set_point_varying_color(pt2, ink, color); - immVertex3fv(pos, fpt); - ++draw_points; + else { + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); } + mul_v3_fl(fpt, -1.0f); + immVertex3fv(pos, fpt); } - - /* now the point we want */ - mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* set point */ + gp_set_point_varying_color(pt, ink, color); + immAttrib1f(thickattrib, max_ff(curpressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &pt->x); immVertex3fv(pos, fpt); - ++draw_points; - if (cyclic && i == 0) { - /* save first point to use in cyclic */ - copy_v3_v3(cyclic_fpt, fpt); - } + curpressure = pt->pressure; } - if (cyclic) { + if (cyclic && totpoints > 2) { /* draw line to first point to complete the cycle */ - immVertex3fv(pos, cyclic_fpt); - ++draw_points; - } + immAttrib1f(thickattrib, max_ff(points->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &points->x); + immVertex3fv(pos, fpt); - /* if less of two points, need to repeat last point to avoid assert in immEnd() */ - if (draw_points < 2) { - const bGPDspoint *pt2 = pt - 1; - mul_v3_m4v3(fpt, diff_mat, &pt2->x); - gp_set_point_varying_color(pt2, ink, color); + /* now add adjacency point (not drawn) */ + immAttrib1f(thickattrib, max_ff((points + 1)->pressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + 1)->x); + immVertex3fv(pos, fpt); + } + /* last adjacency point (not drawn) */ + else { + gp_set_point_varying_color(points + totpoints - 1, ink, color); + immAttrib1f(thickattrib, max_ff(curpressure * thickness, 1.0f)); + mul_v3_m4v3(fpt, tgpw->diff_mat, &(points + totpoints - 2)->x); + mul_v3_fl(fpt, -1.0f); immVertex3fv(pos, fpt); } @@ -692,8 +807,9 @@ static void gp_draw_stroke_3d(const bGPDspoint *points, int totpoints, short thi /* ----- Fancy 2D-Stroke Drawing ------ */ /* draw a given stroke in 2d */ -static void gp_draw_stroke_2d(const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, - bool UNUSED(debug), int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float ink[4]) +static void gp_draw_stroke_2d( + const bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, + bool UNUSED(debug), int offsx, int offsy, int winx, int winy, const float diff_mat[4][4], const float ink[4]) { /* otherwise thickness is twice that of the 3D view */ float thickness = (float)thickness_s * 0.5f; @@ -900,10 +1016,7 @@ static bool gp_can_draw_stroke(const bGPDstroke *gps, const int dflag) } /* draw a set of strokes */ -static void gp_draw_strokes( - bGPdata *gpd, const 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, const float diff_mat[4][4]) +static void gp_draw_strokes(tGPDdraw *tgpw) { float tcolor[4]; float tfill[4]; @@ -912,31 +1025,41 @@ static void gp_draw_strokes( GPU_enable_program_point_size(); - for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + for (bGPDstroke *gps = tgpw->t_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, tgpw->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) || + Material *ma = tgpw->gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; + + if ((gp_style == NULL) || + (gp_style->flag & GP_STYLE_COLOR_HIDE) || /* if onion and ghost flag do not draw*/ - (onion && (palcolor->flag & PC_COLOR_ONIONSKIN))) + (tgpw->onion && (gp_style->flag & GP_STYLE_COLOR_ONIONSKIN))) + { + continue; + } + + /* if disable fill, the colors with fill must be omitted too except fill boundary strokes */ + if ((tgpw->disable_fill == 1) && + (gp_style->fill_rgba[3] > 0.0f) && + ((gps->flag & GP_STROKE_NOFILL) == 0)) { continue; } /* calculate thickness */ - sthickness = gps->thickness + lthick; + sthickness = gps->thickness + tgpw->lthick; if (sthickness <= 0) { continue; } /* check which stroke-drawer to use */ - if (dflag & GP_DRAWDATA_ONLY3D) { - const int no_xray = (dflag & GP_DRAWDATA_NO_XRAY); + if (tgpw->dflag & GP_DRAWDATA_ONLY3D) { + const int no_xray = (tgpw->dflag & GP_DRAWDATA_NO_XRAY); int mask_orig = 0; if (no_xray) { @@ -951,57 +1074,64 @@ static void gp_draw_strokes( /* 3D Fill */ //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 ((gps->totpoints >= 3) && (tgpw->disable_fill != 1)) { + /* set color using material, tint color and opacity */ + interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { const float *color; - if (!onion) { + if (!tgpw->onion) { color = tfill; } else { - if (custonion) { - color = tintcolor; + if (tgpw->custonion) { + color = tgpw->tintcolor; } else { - ARRAY_SET_ITEMS(tfill, UNPACK3(palcolor->fill), tintcolor[3]); + ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); color = tfill; } } - gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat, color); + gp_draw_stroke_fill( + tgpw->gpd, gps, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, color); } } /* 3D Stroke */ - /* 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; + /* set color using material tint color and opacity */ + if (!tgpw->onion) { + interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; copy_v4_v4(ink, tcolor); } else { - if (custonion) { - copy_v4_v4(ink, tintcolor); + if (tgpw->custonion) { + copy_v4_v4(ink, tgpw->tintcolor); } else { - ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); copy_v4_v4(ink, tcolor); } } - if (palcolor->flag & PC_COLOR_VOLUMETRIC) { + if (gp_style->mode == GP_STYLE_MODE_DOTS) { /* volumetric stroke drawing */ - gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink); + if (tgpw->disable_fill != 1) { + gp_draw_stroke_volumetric_3d(gps->points, gps->totpoints, sthickness, ink); + } } else { /* 3D Lines - OpenGL primitives-based */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, - diff_mat, ink); + if (tgpw->disable_fill != 1) { + gp_draw_stroke_point(gps->points, sthickness, tgpw->dflag, gps->flag, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, + tgpw->diff_mat, ink); + } } else { - gp_draw_stroke_3d(gps->points, gps->totpoints, sthickness, debug, gps->flag, - diff_mat, ink, gps->flag & GP_STROKE_CYCLIC); + tgpw->gps = gps; + gp_draw_stroke_3d(tgpw, sthickness, ink, gps->flag & GP_STROKE_CYCLIC); } } if (no_xray) { @@ -1014,58 +1144,63 @@ static void gp_draw_strokes( else { /* 2D - Fill */ 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) { + /* set color using material, tint color and opacity */ + interp_v3_v3v3(tfill, gp_style->fill_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tfill[3] = gp_style->fill_rgba[3] * tgpw->opacity; + if ((tfill[3] > GPENCIL_ALPHA_OPACITY_THRESH) || (gp_style->fill_style > 0)) { const float *color; - if (!onion) { + if (!tgpw->onion) { color = tfill; } else { - if (custonion) { - color = tintcolor; + if (tgpw->custonion) { + color = tgpw->tintcolor; } else { - ARRAY_SET_ITEMS(tfill, palcolor->fill[0], palcolor->fill[1], palcolor->fill[2], - tintcolor[3]); + ARRAY_SET_ITEMS(tfill, UNPACK3(gp_style->fill_rgba), tgpw->tintcolor[3]); color = tfill; } } - gp_draw_stroke_fill(gpd, gps, offsx, offsy, winx, winy, diff_mat, color); + gp_draw_stroke_fill( + tgpw->gpd, gps, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, color); } } /* 2D Strokes... */ - /* 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; + /* set color using material, tint color and opacity */ + if (!tgpw->onion) { + interp_v3_v3v3(tcolor, gp_style->stroke_rgba, tgpw->tintcolor, tgpw->tintcolor[3]); + tcolor[3] = gp_style->stroke_rgba[3] * tgpw->opacity; copy_v4_v4(ink, tcolor); } else { - if (custonion) { - copy_v4_v4(ink, tintcolor); + if (tgpw->custonion) { + copy_v4_v4(ink, tgpw->tintcolor); } else { - ARRAY_SET_ITEMS(tcolor, palcolor->color[0], palcolor->color[1], palcolor->color[2], opacity); + ARRAY_SET_ITEMS(tcolor, UNPACK3(gp_style->stroke_rgba), tgpw->opacity); copy_v4_v4(ink, tcolor); } } - if (palcolor->flag & PC_COLOR_VOLUMETRIC) { + if (gp_style->mode == GP_STYLE_MODE_DOTS) { /* blob/disk-based "volumetric" drawing */ - gp_draw_stroke_volumetric_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, - offsx, offsy, winx, winy, diff_mat, ink); + gp_draw_stroke_volumetric_2d( + gps->points, gps->totpoints, sthickness, tgpw->dflag, gps->flag, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, ink); } else { /* normal 2D strokes */ if (gps->totpoints == 1) { - gp_draw_stroke_point(gps->points, sthickness, dflag, gps->flag, offsx, offsy, winx, winy, - diff_mat, ink); + gp_draw_stroke_point( + gps->points, sthickness, tgpw->dflag, gps->flag, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, + tgpw->diff_mat, ink); } else { - gp_draw_stroke_2d(gps->points, gps->totpoints, sthickness, dflag, gps->flag, debug, - offsx, offsy, winx, winy, diff_mat, ink); + gp_draw_stroke_2d( + gps->points, gps->totpoints, sthickness, tgpw->dflag, gps->flag, false, + tgpw->offsx, tgpw->offsy, tgpw->winx, tgpw->winy, tgpw->diff_mat, ink); } } } @@ -1114,14 +1249,16 @@ static void gp_draw_strokes_edit( if ((gps->flag & GP_STROKE_SELECT) == 0) continue; - /* verify palette color lock */ + /* verify color lock */ { - bGPDpalettecolor *palcolor = ED_gpencil_stroke_getcolor(gpd, gps); - if (palcolor != NULL) { - if (palcolor->flag & PC_COLOR_HIDE) { + Material *ma = gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; + + if (gp_style != NULL) { + if (gp_style->flag & GP_STYLE_COLOR_HIDE) { continue; } - if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) { + if (((lflag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_COLOR_LOCKED)) { continue; } } @@ -1143,8 +1280,9 @@ static void gp_draw_strokes_edit( } /* 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); + /* set color using material */ + Material *ma = gpd->mat[gps->mat_nr]; + MaterialGPencilStyle *gp_style = ma->gp_style; float selectColor[4]; UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, selectColor); @@ -1153,7 +1291,7 @@ static void gp_draw_strokes_edit( GPUVertFormat *format = immVertexFormat(); uint pos; /* specified later */ uint size = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 3, GPU_FETCH_INT_TO_FLOAT_UNIT); if (gps->flag & GP_STROKE_3DSPACE) { pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -1189,7 +1327,7 @@ static void gp_draw_strokes_edit( immAttrib1f(size, vsize); } else { - immAttrib3fv(color, palcolor->color); + immAttrib3fv(color, gp_style->stroke_rgba); immAttrib1f(size, bsize); } @@ -1229,98 +1367,75 @@ static void gp_draw_strokes_edit( /* ----- General Drawing ------ */ -/* draw onion-skinning for a layer */ -static void gp_draw_onionskins( - bGPdata *gpd, const bGPDlayer *gpl, const bGPDframe *gpf, int offsx, int offsy, int winx, int winy, - int UNUSED(cfra), int dflag, bool debug, const float diff_mat[4][4]) + +/* draw interpolate strokes (used only while operator is running) */ +void ED_gp_draw_interpolation(const bContext *C, tGPDinterpolate *tgpi, const int type) { - const float default_color[3] = {UNPACK3(U.gpencil_new_layer_col)}; - const float alpha = 1.0f; + tGPDdraw tgpw; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + tGPDinterpolate_layer *tgpil; + Object *obact = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + 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, default_color); + UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color); + color[3] = 0.6f; + int dflag = 0; + /* if 3d stuff, enable flags */ + if (type == REGION_DRAW_POST_VIEW) { + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); } - if (gpl->gstep > 0) { - /* draw previous frames first */ - for (bGPDframe *gf = gpf->prev; gf; gf = gf->prev) { - /* check if frame is drawable */ - if ((gpf->framenum - gf->framenum) <= gpl->gstep) { - /* alpha decreases with distance from curframe index */ - float fac = 1.0f - ((float)(gpf->framenum - gf->framenum) / (float)(gpl->gstep + 1)); - color[3] = alpha * fac * 0.66f; - 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; - } - } - else if (gpl->gstep == 0) { - /* 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(gpd, gpf->prev, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, - true, gpl->flag & GP_LAYER_GHOST_PREVCOL, diff_mat); - } - } - else { - /* don't draw - disabled */ - } + tgpw.rv3d = rv3d; + tgpw.depsgraph = depsgraph; + tgpw.ob = obact; + tgpw.gpd = tgpi->gpd; + tgpw.offsx = 0; + tgpw.offsy = 0; + tgpw.winx = tgpi->ar->winx; + tgpw.winy = tgpi->ar->winy; + tgpw.dflag = dflag; - /* 2) Now draw next frames */ - if (gpl->flag & GP_LAYER_GHOST_NEXTCOL) { - copy_v3_v3(color, gpl->gcolor_next); - } - else { - copy_v3_v3(color, default_color); - } + /* turn on alpha-blending */ + glEnable(GL_BLEND); + for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { + /* calculate parent position */ + ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpil->gpl, tgpw.diff_mat); + if (tgpil->interFrame) { + tgpw.gpl = tgpil->gpl; + tgpw.gpf = tgpil->interFrame; + tgpw.t_gpf = tgpil->interFrame; - if (gpl->gstep_next > 0) { - /* now draw next frames */ - for (bGPDframe *gf = gpf->next; gf; gf = gf->next) { - /* check if frame is drawable */ - if ((gf->framenum - gpf->framenum) <= gpl->gstep_next) { - /* alpha decreases with distance from curframe index */ - float fac = 1.0f - ((float)(gf->framenum - gpf->framenum) / (float)(gpl->gstep_next + 1)); - color[3] = alpha * fac * 0.66f; - 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; - } - } - else if (gpl->gstep_next == 0) { - /* 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(gpd, gpf->next, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, 1.0f, color, - true, gpl->flag & GP_LAYER_GHOST_NEXTCOL, diff_mat); + tgpw.lthick = tgpil->gpl->line_change; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, color); + tgpw.onion = true; + tgpw.custonion = true; + + gp_draw_strokes(&tgpw); } } - else { - /* don't draw - disabled */ - } + glDisable(GL_BLEND); } /* draw interpolate strokes (used only while operator is running) */ -void ED_gp_draw_interpolation(tGPDinterpolate *tgpi, const int type) +void ED_gp_draw_primitives(const bContext *C, tGPDprimitive *tgpi, const int type) { - tGPDinterpolate_layer *tgpil; - float diff_mat[4][4]; - float color[4]; + tGPDdraw tgpw; + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + + /* if idle, do not draw */ + if (tgpi->flag == 0) { + return; + } - int offsx = 0; - int offsy = 0; - int winx = tgpi->ar->winx; - int winy = tgpi->ar->winy; + Object *obact = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + float color[4]; UI_GetThemeColor3fv(TH_GP_VERTEX_SELECT, color); color[3] = 0.6f; int dflag = 0; @@ -1329,32 +1444,71 @@ void ED_gp_draw_interpolation(tGPDinterpolate *tgpi, const int type) dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); } + tgpw.rv3d = rv3d; + tgpw.depsgraph = depsgraph; + tgpw.ob = obact; + tgpw.gpd = tgpi->gpd; + tgpw.offsx = 0; + tgpw.offsy = 0; + tgpw.winx = tgpi->ar->winx; + tgpw.winy = tgpi->ar->winy; + tgpw.dflag = dflag; + /* turn on alpha-blending */ GPU_blend(true); - for (tgpil = tgpi->ilayers.first; tgpil; tgpil = tgpil->next) { - /* calculate parent position */ - ED_gpencil_parent_location(tgpil->gpl, diff_mat); - if (tgpil->interFrame) { - gp_draw_strokes(tgpi->gpd, tgpil->interFrame, offsx, offsy, winx, winy, dflag, false, - tgpil->gpl->thickness, 1.0f, color, true, true, diff_mat); + /* calculate parent position */ + ED_gpencil_parent_location(depsgraph, obact, tgpi->gpd, tgpi->gpl, tgpw.diff_mat); + if (tgpi->gpf) { + tgpw.gps = tgpi->gpf->strokes.first; + if (tgpw.gps->totpoints > 0) { + tgpw.gpl = tgpi->gpl; + tgpw.gpf = tgpi->gpf; + tgpw.t_gpf = tgpi->gpf; + + tgpw.lthick = tgpi->gpl->line_change; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, color); + tgpw.onion = true; + tgpw.custonion = true; + + gp_draw_strokes(&tgpw); } } GPU_blend(false); } +/* wrapper to draw strokes for filling operator */ +void ED_gp_draw_fill(tGPDdraw *tgpw) +{ + gp_draw_strokes(tgpw); +} + /* loop over gpencil data layers, drawing them */ -static void gp_draw_data_layers( - const bGPDbrush *brush, float alpha, bGPdata *gpd, +static void gp_draw_data_layers(RegionView3D *rv3d, + const Brush *brush, float alpha, Object *ob, bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { float diff_mat[4][4]; + tGPDdraw tgpw; + + tgpw.rv3d = rv3d; + tgpw.depsgraph = NULL; /* XXX: This is not used here */ + tgpw.ob = ob; + tgpw.gpd = gpd; + tgpw.gpl = NULL; + tgpw.gpf = NULL; + tgpw.t_gpf = NULL; + tgpw.offsx = offsx; + tgpw.offsy = offsy; + tgpw.winx = winx; + tgpw.winy = winy; + tgpw.dflag = dflag; for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* calculate parent position */ - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, diff_mat); - bool debug = (gpl->flag & GP_LAYER_DRAWDEBUG); - short lthick = brush->thickness + gpl->thickness; + short lthick = brush->size + gpl->line_change; /* don't draw layer if hidden */ if (gpl->flag & GP_LAYER_HIDE) @@ -1383,30 +1537,20 @@ static void gp_draw_data_layers( /* volumetric strokes... */ GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_VOLUMETRIC), GP_DRAWDATA_VOLUMETRIC); - /* HQ fills... */ - GP_DRAWFLAG_APPLY((gpl->flag & GP_LAYER_HQ_FILL), GP_DRAWDATA_HQ_FILL); - #undef GP_DRAWFLAG_APPLY - /* Draw 'onionskins' (frame left + right) - * - It is only possible to show these if the option is enabled - * - The "no onions" flag prevents ghosts from appearing during animation playback/scrubbing - * and in renders - * - The per-layer "always show" flag however overrides the playback/render restriction, - * allowing artists to selectively turn onionskins on/off during playback - */ - if ((gpl->flag & GP_LAYER_ONIONSKIN) && - ((dflag & GP_DRAWDATA_NO_ONIONS) == 0 || (gpl->flag & GP_LAYER_GHOST_ALWAYS))) - { - /* Drawing method - only immediately surrounding (gstep = 0), - * or within a frame range on either side (gstep > 0) - */ - gp_draw_onionskins(gpd, gpl, gpf, offsx, offsy, winx, winy, cfra, dflag, debug, diff_mat); - } + tgpw.gpl = gpl; + tgpw.gpf = gpf; + tgpw.t_gpf = gpf; // XXX? + tgpw.lthick = gpl->line_change; + tgpw.opacity = gpl->opacity; + copy_v4_v4(tgpw.tintcolor, gpl->tintcolor); + tgpw.onion = false; + tgpw.custonion = false; + copy_m4_m4(tgpw.diff_mat, diff_mat); /* draw the strokes already in active frame */ - gp_draw_strokes(gpd, gpf, offsx, offsy, winx, winy, dflag, debug, gpl->thickness, - gpl->opacity, gpl->tintcolor, false, false, diff_mat); + gp_draw_strokes(&tgpw); /* Draw verts of selected strokes * - when doing OpenGL renders, we don't want to be showing these, as that ends up flickering @@ -1435,19 +1579,25 @@ static void gp_draw_data_layers( * It should also be noted that sbuffer contains temporary point types * i.e. tGPspoints NOT bGPDspoints */ - if (gpd->sflag & PC_COLOR_VOLUMETRIC) { - gp_draw_stroke_volumetric_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, - dflag, gpd->scolor); + if (gpd->runtime.mode == GP_STYLE_MODE_DOTS) { + gp_draw_stroke_volumetric_buffer( + gpd->runtime.sbuffer, + gpd->runtime.sbuffer_size, lthick, + dflag, gpd->runtime.scolor); } else { - gp_draw_stroke_buffer(gpd->sbuffer, gpd->sbuffer_size, lthick, dflag, gpd->sbuffer_sflag, gpd->scolor, gpd->sfill); + gp_draw_stroke_buffer( + gpd->runtime.sbuffer, + gpd->runtime.sbuffer_size, lthick, + dflag, gpd->runtime.sbuffer_sflag, + gpd->runtime.scolor, gpd->runtime.sfill); } } } } /* draw a short status message in the top-right corner */ -static void gp_draw_status_text(const bGPdata *gpd, ARegion *ar) +static void UNUSED_FUNCTION(gp_draw_status_text)(const bGPdata *gpd, ARegion *ar) { rcti rect; @@ -1493,8 +1643,8 @@ static void gp_draw_status_text(const bGPdata *gpd, ARegion *ar) } /* draw grease-pencil datablock */ -static void gp_draw_data( - const bGPDbrush *brush, float alpha, bGPdata *gpd, +static void gp_draw_data(RegionView3D *rv3d, + const Brush *brush, float alpha, Object *ob, bGPdata *gpd, int offsx, int offsy, int winx, int winy, int cfra, int dflag) { /* turn on smooth lines (i.e. anti-aliasing) */ @@ -1510,7 +1660,7 @@ static void gp_draw_data( GPU_blend(true); /* draw! */ - gp_draw_data_layers(brush, alpha, gpd, offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data_layers(rv3d, brush, alpha, ob, gpd, offsx, offsy, winx, winy, cfra, dflag); /* turn off alpha blending, then smooth lines */ GPU_blend(false); // alpha blending @@ -1519,33 +1669,22 @@ static void gp_draw_data( /* if we have strokes for scenes (3d view)/clips (movie clip editor) * and objects/tracks, multiple data blocks have to be drawn */ -static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, int winx, int winy, - int cfra, int dflag, const char spacetype) +static void gp_draw_data_all( + RegionView3D *rv3d, Scene *scene, bGPdata *gpd, int offsx, int offsy, int winx, int winy, + int cfra, int dflag, const char UNUSED(spacetype)) { bGPdata *gpd_source = NULL; - ToolSettings *ts; - bGPDbrush *brush = NULL; + ToolSettings *ts = NULL; + Brush *brush = NULL; if (scene) { ts = scene->toolsettings; - brush = BKE_gpencil_brush_getactive(ts); - /* if no brushes, create default set */ - if (brush == NULL) { - BKE_gpencil_brush_init_presets(ts); - brush = BKE_gpencil_brush_getactive(ts); - } - - if (spacetype == SPACE_VIEW3D) { - gpd_source = (scene->gpd ? scene->gpd : NULL); - } - else if (spacetype == SPACE_CLIP && scene->clip) { - /* currently drawing only gpencil data from either clip or track, but not both - XXX fix logic behind */ - gpd_source = (scene->clip->gpd ? scene->clip->gpd : NULL); - } + brush = BKE_brush_getactive_gpencil(ts); if (gpd_source) { if (brush != NULL) { - gp_draw_data(brush, ts->gp_sculpt.alpha, gpd_source, - offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data( + rv3d, brush, 1.0f, NULL, gpd_source, + offsx, offsy, winx, winy, cfra, dflag); } } } @@ -1554,76 +1693,70 @@ static void gp_draw_data_all(Scene *scene, bGPdata *gpd, int offsx, int offsy, i * 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)) { if (brush != NULL) { - gp_draw_data(brush, ts->gp_sculpt.alpha, gpd, - offsx, offsy, winx, winy, cfra, dflag); + gp_draw_data( + rv3d, brush, 1.0f, NULL, gpd, + offsx, offsy, winx, winy, cfra, dflag); } } } /* ----- Grease Pencil Sketches Drawing API ------ */ -/* ............................ - * XXX - * We need to review the calls below, since they may be/are not that suitable for - * the new ways that we intend to be drawing data... - * ............................ */ -/* draw grease-pencil sketches to specified 2d-view that uses ibuf corrections */ -void ED_gpencil_draw_2dimage(const bContext *C) +/* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly + * Note: this gets called twice - first time with only3d=true to draw 3d-strokes, + * second time with only3d=false for screen-aligned strokes */ +void ED_gpencil_draw_view3d( + wmWindowManager *wm, + Scene *scene, + ViewLayer *view_layer, + struct Depsgraph *depsgraph, + View3D *v3d, + ARegion *ar, + bool only3d) { - wmWindowManager *wm = CTX_wm_manager(C); - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - - int offsx, offsy, sizex, sizey; - int dflag = GP_DRAWDATA_NOSTATUS; + int dflag = 0; + RegionView3D *rv3d = ar->regiondata; + int offsx, offsy, winx, winy; - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX + /* check that we have grease-pencil stuff to draw */ + // XXX: This is the only place that still uses this function + bGPdata *gpd = ED_gpencil_data_get_active_v3d(view_layer); if (gpd == NULL) return; - /* calculate rect */ - switch (sa->spacetype) { - case SPACE_IMAGE: /* image */ - case SPACE_CLIP: /* clip */ - { - /* just draw using standard scaling (settings here are currently ignored anyways) */ - /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; + /* when rendering to the offscreen buffer we don't want to + * deal with the camera border, otherwise map the coords to the camera border. */ + if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_RENDER_OGL)) { + rctf rectf; + ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &rectf, true); /* no shift */ - wmOrtho2(ar->v2d.cur.xmin, ar->v2d.cur.xmax, ar->v2d.cur.ymin, ar->v2d.cur.ymax); + offsx = round_fl_to_int(rectf.xmin); + offsy = round_fl_to_int(rectf.ymin); + winx = round_fl_to_int(rectf.xmax - rectf.xmin); + winy = round_fl_to_int(rectf.ymax - rectf.ymin); + } + else { + offsx = 0; + offsy = 0; + winx = ar->winx; + winy = ar->winy; + } - dflag |= GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_IEDITHACK; - break; - } - case SPACE_SEQ: /* sequence */ - { - /* just draw using standard scaling (settings here are currently ignored anyways) */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - /* NOTE: I2D was used in 2.4x, but the old settings for that have been deprecated - * and everything moved to standard View2d - */ - dflag |= GP_DRAWDATA_ONLYV2D; - break; - } - default: /* for spacetype not yet handled */ - offsx = 0; - offsy = 0; - sizex = ar->winx; - sizey = ar->winy; - - dflag |= GP_DRAWDATA_ONLYI2D; - break; + /* set flags */ + if (only3d) { + /* 3D strokes/3D space: + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ + dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); } - if (ED_screen_animation_playing(wm)) { + if (v3d->flag2 & V3D_RENDER_OVERRIDE) { + /* don't draw status text when "only render" flag is set */ + dflag |= GP_DRAWDATA_NOSTATUS; + } + + if ((wm == NULL) || ED_screen_animation_playing(wm)) { /* don't show onionskins during animation playback/scrub (i.e. it obscures the poses) * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes) */ @@ -1631,85 +1764,46 @@ void ED_gpencil_draw_2dimage(const bContext *C) } /* draw it! */ - gp_draw_data_all(scene, gpd, offsx, offsy, sizex, sizey, CFRA, dflag, sa->spacetype); + gp_draw_data_all(rv3d, scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); } -/* draw grease-pencil sketches to specified 2d-view assuming that matrices are already set correctly - * Note: this gets called twice - first time with onlyv2d=true to draw 'canvas' strokes, - * second time with onlyv2d=false for screen-aligned strokes */ -void ED_gpencil_draw_view2d(const bContext *C, bool onlyv2d) -{ - wmWindowManager *wm = CTX_wm_manager(C); - ScrArea *sa = CTX_wm_area(C); - ARegion *ar = CTX_wm_region(C); - Scene *scene = CTX_data_scene(C); - int dflag = 0; - - /* check that we have grease-pencil stuff to draw */ - if (sa == NULL) return; - bGPdata *gpd = ED_gpencil_data_get_active(C); // XXX - if (gpd == NULL) return; - - /* special hack for Image Editor */ - /* FIXME: the opengl poly-strokes don't draw at right thickness when done this way, so disabled */ - if (ELEM(sa->spacetype, SPACE_IMAGE, SPACE_CLIP)) - dflag |= GP_DRAWDATA_IEDITHACK; - - /* draw it! */ - if (onlyv2d) dflag |= (GP_DRAWDATA_ONLYV2D | GP_DRAWDATA_NOSTATUS); - if (ED_screen_animation_playing(wm)) dflag |= GP_DRAWDATA_NO_ONIONS; - - gp_draw_data_all(scene, gpd, 0, 0, ar->winx, ar->winy, CFRA, dflag, sa->spacetype); - - /* draw status text (if in screen/pixel-space) */ - if (!onlyv2d) { - gp_draw_status_text(gpd, ar); - } -} - -/* draw grease-pencil sketches to specified 3d-view assuming that matrices are already set correctly - * Note: this gets called twice - first time with only3d=true to draw 3d-strokes, - * second time with only3d=false for screen-aligned strokes */ -void ED_gpencil_draw_view3d(wmWindowManager *wm, - Scene *scene, - ViewLayer *view_layer, - struct Depsgraph *depsgraph, - View3D *v3d, - ARegion *ar, - bool only3d) +/* draw grease-pencil sketches to specified 3d-view for gp object + * assuming that matrices are already set correctly + */ +void ED_gpencil_draw_view3d_object(wmWindowManager *wm, Scene *scene, Depsgraph *depsgraph, Object *ob, View3D *v3d, ARegion *ar, bool only3d) { int dflag = 0; RegionView3D *rv3d = ar->regiondata; - int offsx, offsy, winx, winy; + int offsx, offsy, winx, winy; /* check that we have grease-pencil stuff to draw */ - bGPdata *gpd = ED_gpencil_data_get_active_v3d(scene, view_layer); + bGPdata *gpd = ob->data; if (gpd == NULL) return; /* when rendering to the offscreen buffer we don't want to - * deal with the camera border, otherwise map the coords to the camera border. */ + * deal with the camera border, otherwise map the coords to the camera border. */ if ((rv3d->persp == RV3D_CAMOB) && !(G.f & G_RENDER_OGL)) { rctf rectf; ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &rectf, true); /* no shift */ offsx = round_fl_to_int(rectf.xmin); offsy = round_fl_to_int(rectf.ymin); - winx = round_fl_to_int(rectf.xmax - rectf.xmin); - winy = round_fl_to_int(rectf.ymax - rectf.ymin); + winx = round_fl_to_int(rectf.xmax - rectf.xmin); + winy = round_fl_to_int(rectf.ymax - rectf.ymin); } else { offsx = 0; offsy = 0; - winx = ar->winx; - winy = ar->winy; + winx = ar->winx; + winy = ar->winy; } /* set flags */ if (only3d) { /* 3D strokes/3D space: - * - only 3D space points - * - don't status text either (as it's the wrong space) - */ + * - only 3D space points + * - don't status text either (as it's the wrong space) + */ dflag |= (GP_DRAWDATA_ONLY3D | GP_DRAWDATA_NOSTATUS); } @@ -1720,20 +1814,23 @@ void ED_gpencil_draw_view3d(wmWindowManager *wm, if ((wm == NULL) || ED_screen_animation_playing(wm)) { /* don't show onionskins during animation playback/scrub (i.e. it obscures the poses) - * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes) - */ + * OpenGL Renders (i.e. final output), or depth buffer (i.e. not real strokes) + */ dflag |= GP_DRAWDATA_NO_ONIONS; } /* draw it! */ - gp_draw_data_all(scene, gpd, offsx, offsy, winx, winy, CFRA, dflag, v3d->spacetype); + ToolSettings *ts = scene->toolsettings; + Brush *brush = BKE_brush_getactive_gpencil(ts); + if (brush != NULL) { + gp_draw_data(rv3d, brush, 1.0f, ob, gpd, + offsx, offsy, winx, winy, CFRA, dflag); + } } -void ED_gpencil_draw_ex(Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) +void ED_gpencil_draw_ex(RegionView3D *rv3d, Scene *scene, bGPdata *gpd, int winx, int winy, const int cfra, const char spacetype) { int dflag = GP_DRAWDATA_NOSTATUS | GP_DRAWDATA_ONLYV2D; - gp_draw_data_all(scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); + gp_draw_data_all(rv3d, scene, gpd, 0, 0, winx, winy, cfra, dflag, spacetype); } - -/* ************************************************** */ diff --git a/source/blender/editors/gpencil/editaction_gpencil.c b/source/blender/editors/gpencil/editaction_gpencil.c index 5e62a87caf3..88f935eb8bf 100644 --- a/source/blender/editors/gpencil/editaction_gpencil.c +++ b/source/blender/editors/gpencil/editaction_gpencil.c @@ -492,6 +492,8 @@ bool ED_gpencil_anim_copybuf_paste(bAnimContext *ac, const short offset_mode) /* make a copy of stroke, then of its points array */ gpsn = MEM_dupallocN(gps); gpsn->points = MEM_dupallocN(gps->points); + gpsn->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsn); /* duplicate triangle information */ gpsn->triangles = MEM_dupallocN(gps->triangles); /* append stroke to frame */ diff --git a/source/blender/editors/gpencil/gpencil_add_monkey.c b/source/blender/editors/gpencil/gpencil_add_monkey.c new file mode 100644 index 00000000000..8a7128adde1 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_add_monkey.c @@ -0,0 +1,1567 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017 Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Matias Mendiola + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/gpencil_add_monkey.c + * \ingroup edgpencil + */ + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_gpencil_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_context.h" +#include "BKE_gpencil.h" +#include "BKE_main.h" +#include "BKE_material.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "ED_gpencil.h" + +/* Definition of the most important info from a color */ +typedef struct ColorTemplate { + const char *name; + float line[4]; + float fill[4]; +} ColorTemplate; + +/* Add color an ensure duplications (matched by name) */ +static int gpencil_monkey_color(Main *bmain, Object *ob, const ColorTemplate *pct) +{ + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + if (STREQ(ma->id.name, pct->name)) { + return i; + } + } + + /* create a new one */ + BKE_object_material_slot_add(bmain, ob); + ma = BKE_material_add_gpencil(bmain, pct->name); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + copy_v4_v4(ma->gp_style->stroke_rgba, pct->line); + copy_v4_v4(ma->gp_style->fill_rgba, pct->fill); + + return BKE_object_material_slot_find_index(ob, ma) - 1; +} + +/* ***************************************************************** */ +/* Monkey Geometry */ + +static const float data0[538 * GP_PRIM_DATABUF_SIZE] = { + -0.509f, 0.0f, -0.156f, 0.267f, 0.362f, -0.522f, 0.0f, -0.159f, 0.31f, 0.407f, -0.531f, 0.0f, -0.16f, 0.347f, 0.426f, -0.543f, -0.0f, -0.162f, 0.38f, 0.439f, + -0.554f, -0.0f, -0.163f, 0.409f, 0.448f, -0.566f, -0.0f, -0.165f, 0.433f, 0.458f, -0.578f, -0.0f, -0.167f, 0.454f, 0.478f, -0.591f, -0.0f, -0.168f, 0.471f, 0.5f, + -0.604f, -0.0f, -0.169f, 0.485f, 0.51f, -0.619f, -0.0f, -0.171f, 0.496f, 0.516f, -0.634f, -0.0f, -0.171f, 0.504f, 0.519f, -0.649f, -0.0f, -0.171f, 0.511f, 0.519f, + -0.665f, -0.0f, -0.17f, 0.516f, 0.521f, -0.681f, -0.0f, -0.17f, 0.521f, 0.53f, -0.697f, -0.0f, -0.169f, 0.524f, 0.533f, -0.713f, -0.0f, -0.167f, 0.527f, 0.533f, + -0.729f, 0.0f, -0.165f, 0.53f, 0.534f, -0.745f, 0.0f, -0.161f, 0.531f, 0.534f, -0.761f, 0.0f, -0.157f, 0.533f, 0.535f, -0.777f, 0.0f, -0.153f, 0.534f, 0.535f, + -0.792f, 0.0f, -0.148f, 0.535f, 0.536f, -0.808f, 0.0f, -0.144f, 0.535f, 0.535f, -0.822f, 0.0f, -0.139f, 0.536f, 0.537f, -0.837f, 0.0f, -0.133f, 0.536f, 0.537f, + -0.852f, 0.0f, -0.128f, 0.536f, 0.537f, -0.866f, 0.0f, -0.122f, 0.536f, 0.537f, -0.88f, 0.0f, -0.115f, 0.536f, 0.537f, -0.894f, 0.0f, -0.109f, 0.536f, 0.537f, + -0.908f, 0.0f, -0.101f, 0.535f, 0.535f, -0.922f, 0.0f, -0.092f, 0.535f, 0.535f, -0.936f, 0.0f, -0.082f, 0.534f, 0.534f, -0.949f, 0.0f, -0.072f, 0.534f, 0.534f, + -0.963f, 0.0f, -0.061f, 0.534f, 0.534f, -0.976f, 0.0f, -0.05f, 0.534f, 0.534f, -0.988f, 0.0f, -0.039f, 0.534f, 0.534f, -1.0f, 0.0f, -0.028f, 0.533f, 0.534f, + -1.011f, 0.0f, -0.017f, 0.533f, 0.533f, -1.022f, 0.0f, -0.007f, 0.533f, 0.534f, -1.033f, 0.0f, 0.004f, 0.533f, 0.533f, -1.043f, 0.0f, 0.014f, 0.532f, 0.532f, + -1.053f, 0.0f, 0.025f, 0.532f, 0.532f, -1.062f, 0.0f, 0.036f, 0.531f, 0.531f, -1.071f, 0.0f, 0.046f, 0.531f, 0.531f, -1.078f, 0.0f, 0.057f, 0.531f, 0.531f, + -1.085f, 0.0f, 0.068f, 0.531f, 0.531f, -1.092f, 0.0f, 0.08f, 0.532f, 0.532f, -1.098f, 0.0f, 0.091f, 0.533f, 0.533f, -1.104f, 0.0f, 0.105f, 0.535f, 0.535f, + -1.11f, 0.0f, 0.119f, 0.539f, 0.539f, -1.115f, 0.0f, 0.133f, 0.54f, 0.54f, -1.118f, 0.0f, 0.148f, 0.541f, 0.541f, -1.121f, 0.0f, 0.162f, 0.542f, 0.542f, + -1.123f, 0.0f, 0.177f, 0.542f, 0.542f, -1.125f, 0.0f, 0.193f, 0.543f, 0.543f, -1.125f, 0.0f, 0.208f, 0.543f, 0.543f, -1.125f, 0.0f, 0.225f, 0.543f, 0.543f, + -1.124f, 0.0f, 0.241f, 0.545f, 0.545f, -1.122f, 0.0f, 0.258f, 0.546f, 0.546f, -1.119f, 0.0f, 0.274f, 0.548f, 0.548f, -1.116f, 0.0f, 0.29f, 0.549f, 0.549f, + -1.111f, 0.0f, 0.305f, 0.549f, 0.549f, -1.106f, 0.0f, 0.318f, 0.549f, 0.549f, -1.1f, 0.0f, 0.33f, 0.549f, 0.549f, -1.094f, 0.0f, 0.34f, 0.549f, 0.549f, + -1.087f, 0.0f, 0.349f, 0.55f, 0.55f, -1.08f, 0.0f, 0.357f, 0.549f, 0.549f, -1.072f, 0.0f, 0.365f, 0.55f, 0.55f, -1.063f, 0.0f, 0.372f, 0.551f, 0.551f, + -1.054f, 0.0f, 0.379f, 0.552f, 0.552f, -1.044f, 0.0f, 0.385f, 0.553f, 0.553f, -1.034f, 0.0f, 0.391f, 0.553f, 0.553f, -1.024f, 0.0f, 0.396f, 0.554f, 0.554f, + -1.013f, 0.0f, 0.401f, 0.554f, 0.554f, -1.003f, 0.0f, 0.405f, 0.554f, 0.554f, -0.991f, 0.0f, 0.409f, 0.554f, 0.554f, -0.978f, 0.0f, 0.412f, 0.555f, 0.555f, + -0.964f, -0.0f, 0.414f, 0.555f, 0.555f, -0.949f, -0.0f, 0.414f, 0.556f, 0.556f, -0.934f, -0.0f, 0.413f, 0.556f, 0.556f, -0.919f, -0.0f, 0.412f, 0.557f, 0.557f, + -0.905f, -0.0f, 0.41f, 0.557f, 0.557f, -0.892f, -0.0f, 0.406f, 0.557f, 0.557f, -0.879f, -0.0f, 0.402f, 0.557f, 0.558f, -0.867f, -0.0f, 0.398f, 0.557f, 0.557f, + -0.855f, -0.0f, 0.394f, 0.557f, 0.557f, -0.843f, -0.0f, 0.388f, 0.557f, 0.557f, -0.831f, -0.0f, 0.381f, 0.558f, 0.557f, -0.82f, -0.0f, 0.375f, 0.558f, 0.557f, + -0.81f, -0.0f, 0.368f, 0.558f, 0.558f, -0.801f, -0.0f, 0.362f, 0.558f, 0.558f, -0.793f, -0.0f, 0.357f, 0.557f, 0.559f, -0.784f, 0.0f, 0.353f, 0.557f, 0.559f, + -0.776f, 0.0f, 0.35f, 0.556f, 0.559f, -0.768f, 0.0f, 0.348f, 0.556f, 0.559f, -0.76f, 0.0f, 0.346f, 0.555f, 0.559f, -0.752f, 0.0f, 0.346f, 0.554f, 0.559f, + -0.744f, 0.0f, 0.347f, 0.553f, 0.554f, -0.737f, 0.0f, 0.348f, 0.552f, 0.548f, -0.729f, 0.0f, 0.351f, 0.551f, 0.544f, -0.723f, 0.0f, 0.355f, 0.551f, 0.546f, + -0.716f, 0.0f, 0.36f, 0.55f, 0.546f, -0.709f, 0.0f, 0.366f, 0.55f, 0.547f, -0.702f, 0.0f, 0.372f, 0.549f, 0.547f, -0.696f, 0.0f, 0.379f, 0.549f, 0.547f, + -0.689f, 0.0f, 0.386f, 0.549f, 0.548f, -0.683f, 0.0f, 0.394f, 0.549f, 0.548f, -0.676f, 0.0f, 0.403f, 0.549f, 0.549f, -0.67f, 0.0f, 0.413f, 0.549f, 0.548f, + -0.664f, 0.0f, 0.422f, 0.549f, 0.549f, -0.658f, 0.0f, 0.432f, 0.55f, 0.549f, -0.652f, 0.0f, 0.441f, 0.551f, 0.548f, -0.646f, 0.0f, 0.451f, 0.552f, 0.548f, + -0.639f, 0.0f, 0.46f, 0.554f, 0.548f, -0.632f, 0.0f, 0.469f, 0.556f, 0.549f, -0.624f, 0.0f, 0.478f, 0.559f, 0.549f, -0.616f, 0.0f, 0.487f, 0.563f, 0.549f, + -0.609f, 0.0f, 0.497f, 0.567f, 0.549f, -0.6f, 0.0f, 0.507f, 0.572f, 0.558f, -0.592f, 0.0f, 0.518f, 0.577f, 0.574f, -0.584f, 0.0f, 0.528f, 0.582f, 0.587f, + -0.575f, 0.0f, 0.538f, 0.586f, 0.592f, -0.566f, 0.0f, 0.548f, 0.591f, 0.595f, -0.556f, 0.0f, 0.557f, 0.594f, 0.597f, -0.546f, 0.0f, 0.567f, 0.597f, 0.598f, + -0.536f, 0.0f, 0.577f, 0.6f, 0.6f, -0.525f, 0.0f, 0.586f, 0.602f, 0.603f, -0.514f, 0.0f, 0.596f, 0.604f, 0.605f, -0.503f, 0.0f, 0.606f, 0.605f, 0.606f, + -0.492f, 0.0f, 0.615f, 0.606f, 0.607f, -0.482f, 0.0f, 0.624f, 0.607f, 0.607f, -0.471f, 0.0f, 0.632f, 0.608f, 0.607f, -0.462f, 0.0f, 0.64f, 0.609f, 0.607f, + -0.453f, 0.0f, 0.647f, 0.61f, 0.61f, -0.444f, 0.0f, 0.654f, 0.612f, 0.611f, -0.435f, 0.0f, 0.66f, 0.614f, 0.613f, -0.427f, 0.0f, 0.666f, 0.616f, 0.615f, + -0.418f, 0.0f, 0.672f, 0.617f, 0.618f, -0.409f, 0.0f, 0.677f, 0.619f, 0.621f, -0.399f, 0.0f, 0.683f, 0.621f, 0.622f, -0.389f, 0.0f, 0.69f, 0.623f, 0.623f, + -0.379f, 0.0f, 0.696f, 0.624f, 0.624f, -0.368f, 0.0f, 0.702f, 0.626f, 0.626f, -0.356f, 0.0f, 0.708f, 0.628f, 0.628f, -0.345f, 0.0f, 0.713f, 0.63f, 0.63f, + -0.333f, 0.0f, 0.719f, 0.633f, 0.631f, -0.32f, 0.0f, 0.724f, 0.637f, 0.632f, -0.307f, 0.0f, 0.729f, 0.641f, 0.64f, -0.294f, 0.0f, 0.732f, 0.646f, 0.644f, + -0.281f, 0.0f, 0.736f, 0.65f, 0.655f, -0.268f, 0.0f, 0.739f, 0.654f, 0.657f, -0.255f, 0.0f, 0.742f, 0.657f, 0.658f, -0.243f, 0.0f, 0.745f, 0.659f, 0.661f, + -0.23f, 0.0f, 0.747f, 0.662f, 0.663f, -0.217f, 0.0f, 0.75f, 0.664f, 0.664f, -0.203f, 0.0f, 0.753f, 0.666f, 0.666f, -0.19f, 0.0f, 0.755f, 0.667f, 0.668f, + -0.177f, 0.0f, 0.757f, 0.669f, 0.67f, -0.163f, 0.0f, 0.76f, 0.671f, 0.671f, -0.15f, 0.0f, 0.762f, 0.673f, 0.672f, -0.136f, 0.0f, 0.764f, 0.674f, 0.674f, + -0.122f, 0.0f, 0.767f, 0.676f, 0.676f, -0.108f, 0.0f, 0.769f, 0.677f, 0.678f, -0.093f, 0.0f, 0.771f, 0.678f, 0.68f, -0.079f, 0.0f, 0.773f, 0.678f, 0.68f, + -0.064f, 0.0f, 0.774f, 0.679f, 0.679f, -0.049f, 0.0f, 0.775f, 0.68f, 0.68f, -0.033f, 0.0f, 0.775f, 0.68f, 0.68f, -0.018f, 0.0f, 0.776f, 0.68f, 0.68f, + -0.002f, 0.0f, 0.776f, 0.681f, 0.68f, 0.013f, 0.0f, 0.777f, 0.681f, 0.681f, 0.029f, 0.0f, 0.777f, 0.682f, 0.681f, 0.045f, 0.0f, 0.777f, 0.682f, 0.681f, + 0.061f, 0.0f, 0.777f, 0.683f, 0.683f, 0.077f, 0.0f, 0.776f, 0.683f, 0.683f, 0.094f, 0.0f, 0.775f, 0.684f, 0.684f, 0.11f, 0.0f, 0.774f, 0.685f, 0.683f, + 0.126f, 0.0f, 0.773f, 0.685f, 0.685f, 0.142f, 0.0f, 0.771f, 0.687f, 0.685f, 0.158f, 0.0f, 0.769f, 0.688f, 0.685f, 0.174f, 0.0f, 0.767f, 0.69f, 0.686f, + 0.19f, 0.0f, 0.765f, 0.691f, 0.692f, 0.206f, 0.0f, 0.762f, 0.693f, 0.694f, 0.222f, 0.0f, 0.757f, 0.695f, 0.696f, 0.238f, 0.0f, 0.752f, 0.697f, 0.697f, + 0.254f, 0.0f, 0.747f, 0.699f, 0.698f, 0.27f, 0.0f, 0.742f, 0.7f, 0.7f, 0.286f, 0.0f, 0.736f, 0.702f, 0.702f, 0.302f, 0.0f, 0.73f, 0.704f, 0.704f, + 0.318f, 0.0f, 0.724f, 0.705f, 0.71f, 0.335f, 0.0f, 0.717f, 0.707f, 0.71f, 0.351f, 0.0f, 0.709f, 0.708f, 0.71f, 0.367f, 0.0f, 0.701f, 0.709f, 0.711f, + 0.382f, 0.0f, 0.692f, 0.71f, 0.713f, 0.397f, 0.0f, 0.683f, 0.711f, 0.713f, 0.41f, 0.0f, 0.675f, 0.712f, 0.713f, 0.422f, 0.0f, 0.666f, 0.712f, 0.714f, + 0.434f, 0.0f, 0.658f, 0.713f, 0.714f, 0.446f, 0.0f, 0.649f, 0.714f, 0.714f, 0.458f, 0.0f, 0.641f, 0.714f, 0.714f, 0.47f, 0.0f, 0.632f, 0.715f, 0.715f, + 0.483f, 0.0f, 0.622f, 0.715f, 0.716f, 0.496f, 0.0f, 0.611f, 0.715f, 0.716f, 0.51f, 0.0f, 0.6f, 0.716f, 0.717f, 0.523f, 0.0f, 0.588f, 0.716f, 0.716f, + 0.536f, 0.0f, 0.576f, 0.717f, 0.717f, 0.55f, 0.0f, 0.563f, 0.717f, 0.717f, 0.564f, 0.0f, 0.549f, 0.717f, 0.717f, 0.577f, 0.0f, 0.536f, 0.718f, 0.717f, + 0.59f, 0.0f, 0.522f, 0.718f, 0.717f, 0.603f, 0.0f, 0.508f, 0.718f, 0.718f, 0.615f, 0.0f, 0.496f, 0.718f, 0.718f, 0.625f, 0.0f, 0.484f, 0.718f, 0.718f, + 0.635f, 0.0f, 0.473f, 0.719f, 0.718f, 0.645f, 0.0f, 0.461f, 0.719f, 0.718f, 0.654f, 0.0f, 0.45f, 0.719f, 0.718f, 0.662f, 0.0f, 0.44f, 0.719f, 0.719f, + 0.67f, 0.0f, 0.431f, 0.719f, 0.719f, 0.676f, 0.0f, 0.422f, 0.719f, 0.719f, 0.682f, 0.0f, 0.414f, 0.719f, 0.719f, 0.687f, 0.0f, 0.407f, 0.719f, 0.719f, + 0.692f, 0.0f, 0.4f, 0.719f, 0.719f, 0.697f, 0.0f, 0.394f, 0.719f, 0.719f, 0.701f, 0.0f, 0.388f, 0.718f, 0.718f, 0.705f, 0.0f, 0.383f, 0.718f, 0.717f, + 0.708f, 0.0f, 0.378f, 0.718f, 0.717f, 0.711f, 0.0f, 0.374f, 0.717f, 0.717f, 0.714f, 0.0f, 0.37f, 0.717f, 0.717f, 0.717f, 0.0f, 0.366f, 0.717f, 0.717f, + 0.719f, 0.0f, 0.362f, 0.718f, 0.717f, 0.722f, 0.0f, 0.359f, 0.718f, 0.718f, 0.724f, 0.0f, 0.356f, 0.718f, 0.717f, 0.727f, 0.0f, 0.352f, 0.717f, 0.719f, + 0.73f, 0.0f, 0.349f, 0.717f, 0.719f, 0.734f, 0.0f, 0.347f, 0.715f, 0.719f, 0.737f, 0.0f, 0.344f, 0.714f, 0.714f, 0.742f, 0.0f, 0.341f, 0.713f, 0.709f, + 0.746f, 0.0f, 0.339f, 0.714f, 0.707f, 0.751f, 0.0f, 0.336f, 0.718f, 0.704f, 0.757f, 0.0f, 0.334f, 0.724f, 0.705f, 0.763f, 0.0f, 0.332f, 0.732f, 0.705f, + 0.769f, -0.0f, 0.329f, 0.742f, 0.704f, 0.775f, -0.0f, 0.328f, 0.753f, 0.713f, 0.782f, -0.0f, 0.327f, 0.764f, 0.804f, 0.789f, -0.0f, 0.327f, 0.774f, 0.813f, + 0.797f, -0.0f, 0.327f, 0.783f, 0.815f, 0.805f, -0.0f, 0.328f, 0.791f, 0.815f, 0.814f, -0.0f, 0.329f, 0.797f, 0.816f, 0.823f, -0.0f, 0.331f, 0.802f, 0.815f, + 0.832f, 0.0f, 0.335f, 0.806f, 0.816f, 0.841f, 0.0f, 0.341f, 0.809f, 0.816f, 0.851f, 0.0f, 0.346f, 0.811f, 0.816f, 0.861f, 0.0f, 0.351f, 0.812f, 0.816f, + 0.871f, 0.0f, 0.356f, 0.813f, 0.815f, 0.881f, 0.0f, 0.361f, 0.814f, 0.816f, 0.893f, 0.0f, 0.365f, 0.814f, 0.816f, 0.906f, 0.0f, 0.368f, 0.814f, 0.817f, + 0.922f, 0.0f, 0.372f, 0.813f, 0.816f, 0.939f, 0.0f, 0.375f, 0.812f, 0.817f, 0.957f, 0.0f, 0.377f, 0.811f, 0.817f, 0.977f, 0.0f, 0.379f, 0.81f, 0.815f, + 0.995f, 0.0f, 0.38f, 0.808f, 0.813f, 1.012f, 0.0f, 0.379f, 0.806f, 0.807f, 1.028f, 0.0f, 0.377f, 0.803f, 0.803f, 1.042f, 0.0f, 0.374f, 0.8f, 0.801f, + 1.054f, 0.0f, 0.371f, 0.797f, 0.8f, 1.065f, 0.0f, 0.366f, 0.794f, 0.8f, 1.076f, 0.0f, 0.361f, 0.791f, 0.792f, 1.085f, 0.0f, 0.355f, 0.788f, 0.781f, + 1.093f, 0.0f, 0.348f, 0.785f, 0.781f, 1.1f, 0.0f, 0.34f, 0.783f, 0.78f, 1.106f, 0.0f, 0.33f, 0.782f, 0.78f, 1.113f, 0.0f, 0.321f, 0.781f, 0.778f, + 1.117f, 0.0f, 0.31f, 0.78f, 0.777f, 1.122f, -0.0f, 0.299f, 0.779f, 0.777f, 1.125f, -0.0f, 0.286f, 0.778f, 0.776f, 1.129f, -0.0f, 0.274f, 0.778f, 0.777f, + 1.131f, -0.0f, 0.262f, 0.778f, 0.777f, 1.132f, -0.0f, 0.249f, 0.777f, 0.777f, 1.134f, -0.0f, 0.237f, 0.777f, 0.778f, 1.134f, -0.0f, 0.225f, 0.777f, 0.778f, + 1.135f, -0.0f, 0.213f, 0.776f, 0.777f, 1.134f, -0.0f, 0.201f, 0.776f, 0.776f, 1.134f, -0.0f, 0.189f, 0.776f, 0.775f, 1.132f, -0.0f, 0.177f, 0.775f, 0.776f, + 1.13f, -0.0f, 0.164f, 0.775f, 0.775f, 1.129f, -0.0f, 0.152f, 0.774f, 0.774f, 1.126f, -0.0f, 0.141f, 0.774f, 0.773f, 1.122f, -0.0f, 0.13f, 0.774f, 0.772f, + 1.118f, -0.0f, 0.118f, 0.773f, 0.772f, 1.113f, -0.0f, 0.108f, 0.773f, 0.773f, 1.107f, -0.0f, 0.097f, 0.773f, 0.774f, 1.102f, -0.0f, 0.087f, 0.772f, 0.773f, + 1.095f, -0.0f, 0.077f, 0.772f, 0.773f, 1.088f, -0.0f, 0.067f, 0.771f, 0.772f, 1.081f, -0.0f, 0.057f, 0.771f, 0.773f, 1.073f, -0.0f, 0.048f, 0.77f, 0.772f, + 1.066f, -0.0f, 0.038f, 0.769f, 0.767f, 1.058f, -0.0f, 0.029f, 0.768f, 0.766f, 1.05f, -0.0f, 0.019f, 0.768f, 0.765f, 1.041f, -0.0f, 0.011f, 0.767f, 0.765f, + 1.032f, -0.0f, 0.003f, 0.767f, 0.766f, 1.023f, -0.0f, -0.004f, 0.766f, 0.765f, 1.013f, -0.0f, -0.011f, 0.766f, 0.765f, 1.003f, -0.0f, -0.019f, 0.765f, 0.766f, + 0.993f, -0.0f, -0.026f, 0.765f, 0.765f, 0.983f, -0.0f, -0.034f, 0.764f, 0.765f, 0.972f, -0.0f, -0.041f, 0.762f, 0.765f, 0.962f, -0.0f, -0.048f, 0.761f, 0.765f, + 0.951f, -0.0f, -0.055f, 0.759f, 0.762f, 0.94f, -0.0f, -0.063f, 0.756f, 0.761f, 0.929f, -0.0f, -0.07f, 0.754f, 0.755f, 0.918f, -0.0f, -0.078f, 0.751f, 0.751f, + 0.907f, -0.0f, -0.085f, 0.748f, 0.747f, 0.896f, -0.0f, -0.092f, 0.745f, 0.744f, 0.884f, -0.0f, -0.099f, 0.742f, 0.742f, 0.873f, -0.0f, -0.105f, 0.739f, 0.738f, + 0.861f, -0.0f, -0.11f, 0.736f, 0.737f, 0.849f, 0.0f, -0.115f, 0.733f, 0.731f, 0.836f, 0.0f, -0.119f, 0.73f, 0.73f, 0.823f, 0.0f, -0.124f, 0.728f, 0.727f, + 0.81f, 0.0f, -0.128f, 0.725f, 0.725f, 0.796f, 0.0f, -0.132f, 0.723f, 0.723f, 0.783f, 0.0f, -0.136f, 0.72f, 0.719f, 0.77f, 0.0f, -0.141f, 0.718f, 0.717f, + 0.756f, 0.0f, -0.145f, 0.715f, 0.712f, 0.742f, 0.0f, -0.15f, 0.713f, 0.708f, 0.728f, 0.0f, -0.152f, 0.711f, 0.707f, 0.713f, 0.0f, -0.155f, 0.709f, 0.706f, + 0.699f, 0.0f, -0.156f, 0.706f, 0.706f, 0.684f, 0.0f, -0.158f, 0.704f, 0.705f, 0.67f, 0.0f, -0.159f, 0.702f, 0.705f, 0.656f, 0.0f, -0.16f, 0.7f, 0.704f, + 0.642f, 0.0f, -0.161f, 0.698f, 0.702f, 0.628f, 0.0f, -0.161f, 0.695f, 0.698f, 0.614f, 0.0f, -0.162f, 0.693f, 0.695f, 0.6f, 0.0f, -0.162f, 0.691f, 0.691f, + 0.587f, 0.0f, -0.162f, 0.688f, 0.686f, 0.574f, 0.0f, -0.162f, 0.686f, 0.685f, 0.561f, 0.0f, -0.161f, 0.683f, 0.683f, 0.548f, 0.0f, -0.161f, 0.681f, 0.683f, + 0.535f, 0.0f, -0.161f, 0.678f, 0.678f, 0.523f, 0.0f, -0.16f, 0.676f, 0.676f, 0.512f, 0.0f, -0.16f, 0.673f, 0.674f, 0.501f, 0.0f, -0.16f, 0.671f, 0.67f, + 0.49f, 0.0f, -0.16f, 0.668f, 0.668f, 0.48f, 0.0f, -0.161f, 0.666f, 0.663f, 0.469f, 0.0f, -0.162f, 0.665f, 0.66f, 0.458f, 0.0f, -0.165f, 0.663f, 0.66f, + 0.447f, 0.0f, -0.167f, 0.662f, 0.659f, 0.437f, 0.0f, -0.171f, 0.661f, 0.659f, 0.426f, 0.0f, -0.175f, 0.66f, 0.659f, 0.415f, 0.0f, -0.18f, 0.66f, 0.659f, + 0.404f, 0.0f, -0.185f, 0.659f, 0.659f, 0.393f, 0.0f, -0.191f, 0.659f, 0.657f, 0.383f, 0.0f, -0.196f, 0.659f, 0.657f, 0.373f, 0.0f, -0.202f, 0.658f, 0.659f, + 0.363f, -0.0f, -0.208f, 0.658f, 0.658f, 0.353f, -0.0f, -0.215f, 0.658f, 0.659f, 0.344f, -0.0f, -0.223f, 0.658f, 0.659f, 0.336f, -0.0f, -0.23f, 0.658f, 0.659f, + 0.327f, -0.0f, -0.238f, 0.658f, 0.658f, 0.319f, -0.0f, -0.245f, 0.657f, 0.657f, 0.312f, -0.0f, -0.253f, 0.657f, 0.656f, 0.305f, -0.0f, -0.261f, 0.656f, 0.658f, + 0.299f, -0.0f, -0.269f, 0.655f, 0.658f, 0.293f, 0.0f, -0.278f, 0.653f, 0.657f, 0.288f, 0.0f, -0.287f, 0.65f, 0.657f, 0.283f, 0.0f, -0.295f, 0.646f, 0.656f, + 0.279f, 0.0f, -0.304f, 0.642f, 0.655f, 0.275f, 0.0f, -0.313f, 0.637f, 0.642f, 0.271f, 0.0f, -0.322f, 0.633f, 0.637f, 0.268f, 0.0f, -0.331f, 0.628f, 0.609f, + 0.265f, 0.0f, -0.341f, 0.624f, 0.607f, 0.263f, 0.0f, -0.35f, 0.62f, 0.608f, 0.261f, 0.0f, -0.359f, 0.617f, 0.608f, 0.259f, 0.0f, -0.369f, 0.614f, 0.607f, + 0.258f, 0.0f, -0.379f, 0.612f, 0.606f, 0.257f, 0.0f, -0.389f, 0.61f, 0.606f, 0.258f, 0.0f, -0.399f, 0.609f, 0.605f, 0.258f, 0.0f, -0.41f, 0.608f, 0.604f, + 0.26f, 0.0f, -0.421f, 0.608f, 0.606f, 0.263f, 0.0f, -0.431f, 0.607f, 0.606f, 0.266f, 0.0f, -0.441f, 0.607f, 0.606f, 0.27f, 0.0f, -0.452f, 0.606f, 0.607f, + 0.274f, 0.0f, -0.463f, 0.606f, 0.607f, 0.279f, 0.0f, -0.475f, 0.605f, 0.607f, 0.283f, 0.0f, -0.487f, 0.604f, 0.607f, 0.288f, 0.0f, -0.498f, 0.603f, 0.607f, + 0.293f, 0.0f, -0.511f, 0.601f, 0.607f, 0.297f, 0.0f, -0.523f, 0.598f, 0.606f, 0.301f, 0.0f, -0.536f, 0.595f, 0.605f, 0.305f, 0.0f, -0.549f, 0.591f, 0.602f, + 0.309f, 0.0f, -0.562f, 0.588f, 0.597f, 0.312f, 0.0f, -0.576f, 0.583f, 0.585f, 0.315f, 0.0f, -0.59f, 0.579f, 0.577f, 0.318f, 0.0f, -0.604f, 0.574f, 0.576f, + 0.321f, 0.0f, -0.618f, 0.569f, 0.57f, 0.323f, 0.0f, -0.633f, 0.564f, 0.564f, 0.326f, 0.0f, -0.647f, 0.559f, 0.554f, 0.328f, 0.0f, -0.663f, 0.555f, 0.549f, + 0.33f, 0.0f, -0.678f, 0.551f, 0.546f, 0.332f, 0.0f, -0.693f, 0.547f, 0.543f, 0.334f, 0.0f, -0.709f, 0.544f, 0.543f, 0.336f, 0.0f, -0.726f, 0.541f, 0.541f, + 0.338f, 0.0f, -0.742f, 0.538f, 0.54f, 0.338f, 0.0f, -0.758f, 0.536f, 0.538f, 0.338f, 0.0f, -0.773f, 0.534f, 0.53f, 0.337f, 0.0f, -0.787f, 0.532f, 0.528f, + 0.337f, 0.0f, -0.801f, 0.53f, 0.528f, 0.336f, 0.0f, -0.814f, 0.529f, 0.528f, 0.334f, 0.0f, -0.827f, 0.527f, 0.528f, 0.333f, 0.0f, -0.84f, 0.525f, 0.529f, + 0.331f, 0.0f, -0.853f, 0.523f, 0.529f, 0.328f, 0.0f, -0.866f, 0.521f, 0.528f, 0.324f, 0.0f, -0.877f, 0.519f, 0.516f, 0.32f, 0.0f, -0.889f, 0.516f, 0.515f, + 0.315f, 0.0f, -0.9f, 0.513f, 0.515f, 0.31f, 0.0f, -0.91f, 0.51f, 0.514f, 0.304f, 0.0f, -0.921f, 0.507f, 0.513f, 0.297f, 0.0f, -0.931f, 0.505f, 0.507f, + 0.289f, 0.0f, -0.94f, 0.502f, 0.498f, 0.281f, 0.0f, -0.948f, 0.499f, 0.494f, 0.272f, 0.0f, -0.956f, 0.497f, 0.491f, 0.262f, 0.0f, -0.963f, 0.495f, 0.49f, + 0.253f, 0.0f, -0.969f, 0.494f, 0.491f, 0.242f, 0.0f, -0.975f, 0.493f, 0.491f, 0.231f, 0.0f, -0.98f, 0.492f, 0.49f, 0.22f, 0.0f, -0.986f, 0.491f, 0.489f, + 0.208f, 0.0f, -0.99f, 0.491f, 0.49f, 0.195f, 0.0f, -0.994f, 0.491f, 0.491f, 0.181f, 0.0f, -0.998f, 0.491f, 0.491f, 0.168f, 0.0f, -1.001f, 0.491f, 0.492f, + 0.154f, 0.0f, -1.005f, 0.491f, 0.492f, 0.141f, 0.0f, -1.008f, 0.492f, 0.492f, 0.126f, 0.0f, -1.01f, 0.492f, 0.492f, 0.112f, 0.0f, -1.011f, 0.492f, 0.492f, + 0.097f, 0.0f, -1.013f, 0.492f, 0.492f, 0.081f, 0.0f, -1.013f, 0.492f, 0.492f, 0.066f, 0.0f, -1.014f, 0.493f, 0.493f, 0.05f, 0.0f, -1.014f, 0.493f, 0.494f, + 0.035f, 0.0f, -1.014f, 0.493f, 0.494f, 0.019f, 0.0f, -1.013f, 0.493f, 0.494f, 0.004f, 0.0f, -1.012f, 0.493f, 0.494f, -0.011f, 0.0f, -1.011f, 0.493f, 0.493f, + -0.026f, 0.0f, -1.01f, 0.492f, 0.493f, -0.041f, 0.0f, -1.008f, 0.492f, 0.492f, -0.056f, 0.0f, -1.006f, 0.492f, 0.492f, -0.07f, 0.0f, -1.004f, 0.491f, 0.492f, + -0.084f, 0.0f, -1.001f, 0.491f, 0.492f, -0.098f, 0.0f, -0.999f, 0.491f, 0.491f, -0.112f, 0.0f, -0.995f, 0.491f, 0.49f, -0.125f, 0.0f, -0.992f, 0.49f, 0.49f, + -0.138f, 0.0f, -0.987f, 0.49f, 0.491f, -0.15f, 0.0f, -0.983f, 0.49f, 0.49f, -0.162f, 0.0f, -0.978f, 0.49f, 0.49f, -0.174f, 0.0f, -0.973f, 0.489f, 0.489f, + -0.185f, 0.0f, -0.967f, 0.489f, 0.488f, -0.196f, 0.0f, -0.961f, 0.489f, 0.489f, -0.207f, 0.0f, -0.955f, 0.489f, 0.489f, -0.218f, 0.0f, -0.949f, 0.489f, 0.49f, + -0.229f, 0.0f, -0.943f, 0.489f, 0.489f, -0.24f, 0.0f, -0.936f, 0.489f, 0.489f, -0.25f, 0.0f, -0.929f, 0.489f, 0.489f, -0.261f, 0.0f, -0.922f, 0.489f, 0.489f, + -0.271f, 0.0f, -0.914f, 0.489f, 0.49f, -0.28f, 0.0f, -0.907f, 0.49f, 0.49f, -0.289f, 0.0f, -0.898f, 0.49f, 0.489f, -0.298f, 0.0f, -0.89f, 0.49f, 0.489f, + -0.306f, 0.0f, -0.882f, 0.49f, 0.49f, -0.314f, 0.0f, -0.875f, 0.491f, 0.489f, -0.322f, 0.0f, -0.866f, 0.492f, 0.489f, -0.328f, 0.0f, -0.857f, 0.492f, 0.489f, + -0.333f, 0.0f, -0.847f, 0.493f, 0.49f, -0.336f, 0.0f, -0.836f, 0.494f, 0.488f, -0.338f, 0.0f, -0.824f, 0.496f, 0.49f, -0.338f, 0.0f, -0.811f, 0.497f, 0.49f, + -0.338f, 0.0f, -0.798f, 0.499f, 0.491f, -0.337f, 0.0f, -0.785f, 0.501f, 0.497f, -0.337f, 0.0f, -0.772f, 0.503f, 0.5f, -0.337f, 0.0f, -0.759f, 0.505f, 0.504f, + -0.336f, -0.0f, -0.746f, 0.507f, 0.505f, -0.336f, -0.0f, -0.733f, 0.51f, 0.51f, -0.335f, -0.0f, -0.719f, 0.512f, 0.513f, -0.334f, -0.0f, -0.706f, 0.515f, 0.515f, + -0.333f, -0.0f, -0.692f, 0.518f, 0.516f, -0.332f, -0.0f, -0.678f, 0.52f, 0.522f, -0.331f, -0.0f, -0.665f, 0.523f, 0.523f, -0.329f, -0.0f, -0.651f, 0.525f, 0.528f, + -0.327f, -0.0f, -0.637f, 0.528f, 0.53f, -0.325f, -0.0f, -0.624f, 0.53f, 0.532f, -0.322f, -0.0f, -0.61f, 0.532f, 0.534f, -0.319f, -0.0f, -0.597f, 0.535f, 0.535f, + -0.316f, -0.0f, -0.584f, 0.537f, 0.538f, -0.313f, -0.0f, -0.57f, 0.539f, 0.54f, -0.31f, -0.0f, -0.557f, 0.541f, 0.542f, -0.307f, -0.0f, -0.544f, 0.542f, 0.545f, + -0.303f, -0.0f, -0.531f, 0.544f, 0.546f, -0.3f, -0.0f, -0.519f, 0.546f, 0.549f, -0.298f, -0.0f, -0.506f, 0.547f, 0.549f, -0.295f, -0.0f, -0.494f, 0.548f, 0.549f, + -0.292f, -0.0f, -0.482f, 0.549f, 0.55f, -0.29f, -0.0f, -0.47f, 0.55f, 0.552f, -0.287f, -0.0f, -0.459f, 0.551f, 0.552f, -0.285f, -0.0f, -0.447f, 0.551f, 0.552f, + -0.284f, -0.0f, -0.436f, 0.552f, 0.552f, -0.282f, -0.0f, -0.425f, 0.552f, 0.553f, -0.281f, -0.0f, -0.413f, 0.553f, 0.553f, -0.28f, -0.0f, -0.402f, 0.553f, 0.553f, + -0.28f, -0.0f, -0.392f, 0.553f, 0.553f, -0.281f, -0.0f, -0.381f, 0.554f, 0.553f, -0.283f, -0.0f, -0.369f, 0.554f, 0.554f, -0.286f, -0.0f, -0.359f, 0.554f, 0.554f, + -0.289f, -0.0f, -0.348f, 0.555f, 0.554f, -0.294f, -0.0f, -0.337f, 0.555f, 0.555f, -0.299f, -0.0f, -0.327f, 0.555f, 0.554f, -0.305f, -0.0f, -0.317f, 0.556f, 0.555f, + -0.312f, -0.0f, -0.307f, 0.556f, 0.555f, -0.319f, -0.0f, -0.297f, 0.556f, 0.557f, -0.326f, 0.0f, -0.287f, 0.557f, 0.558f, -0.334f, 0.0f, -0.278f, 0.557f, 0.557f, + -0.341f, 0.0f, -0.268f, 0.557f, 0.558f, -0.349f, 0.0f, -0.259f, 0.558f, 0.558f, -0.359f, 0.0f, -0.251f, 0.558f, 0.558f, -0.368f, 0.0f, -0.243f, 0.558f, 0.558f, + -0.378f, 0.0f, -0.235f, 0.558f, 0.559f, -0.388f, 0.0f, -0.228f, 0.558f, 0.558f, -0.398f, 0.0f, -0.221f, 0.559f, 0.559f, -0.408f, 0.0f, -0.214f, 0.559f, 0.559f, + -0.418f, 0.0f, -0.208f, 0.559f, 0.559f, -0.427f, 0.0f, -0.202f, 0.559f, 0.558f, -0.436f, 0.0f, -0.196f, 0.559f, 0.559f, -0.445f, 0.0f, -0.191f, 0.559f, 0.559f, + -0.453f, 0.0f, -0.187f, 0.558f, 0.559f, -0.462f, 0.0f, -0.183f, 0.558f, 0.558f, -0.469f, 0.0f, -0.18f, 0.558f, 0.558f, -0.477f, 0.0f, -0.176f, 0.558f, 0.558f, + -0.484f, 0.0f, -0.174f, 0.557f, 0.558f, -0.493f, 0.0f, -0.17f, 0.555f, 0.559f, +}; + + +static const float data1[136 * GP_PRIM_DATABUF_SIZE] = { + -0.369f, 0.0f, -0.048f, 0.065f, 0.065f, -0.378f, 0.0f, -0.046f, 0.239f, 0.293f, -0.383f, 0.0f, -0.044f, 0.316f, 0.339f, -0.39f, 0.0f, -0.041f, 0.348f, 0.355f, + -0.398f, 0.0f, -0.038f, 0.364f, 0.368f, -0.405f, 0.0f, -0.035f, 0.373f, 0.374f, -0.413f, 0.0f, -0.031f, 0.381f, 0.381f, -0.421f, 0.0f, -0.026f, 0.388f, 0.391f, + -0.429f, 0.0f, -0.02f, 0.392f, 0.394f, -0.437f, 0.0f, -0.014f, 0.395f, 0.396f, -0.445f, 0.0f, -0.008f, 0.397f, 0.397f, -0.453f, 0.0f, -0.001f, 0.399f, 0.4f, + -0.461f, 0.0f, 0.007f, 0.401f, 0.401f, -0.468f, -0.0f, 0.016f, 0.404f, 0.404f, -0.474f, 0.0f, 0.023f, 0.406f, 0.407f, -0.479f, 0.0f, 0.03f, 0.409f, 0.409f, + -0.485f, 0.0f, 0.039f, 0.412f, 0.412f, -0.49f, 0.0f, 0.048f, 0.415f, 0.415f, -0.495f, 0.0f, 0.057f, 0.417f, 0.417f, -0.499f, 0.0f, 0.068f, 0.42f, 0.421f, + -0.503f, 0.0f, 0.079f, 0.421f, 0.421f, -0.507f, -0.0f, 0.091f, 0.423f, 0.423f, -0.51f, -0.0f, 0.102f, 0.424f, 0.424f, -0.513f, -0.0f, 0.112f, 0.424f, 0.425f, + -0.515f, -0.0f, 0.123f, 0.425f, 0.425f, -0.517f, -0.0f, 0.135f, 0.425f, 0.425f, -0.518f, -0.0f, 0.146f, 0.426f, 0.425f, -0.519f, -0.0f, 0.158f, 0.426f, 0.425f, + -0.52f, -0.0f, 0.169f, 0.426f, 0.426f, -0.52f, -0.0f, 0.181f, 0.427f, 0.427f, -0.519f, -0.0f, 0.192f, 0.427f, 0.427f, -0.518f, -0.0f, 0.203f, 0.427f, 0.427f, + -0.517f, -0.0f, 0.213f, 0.427f, 0.428f, -0.515f, -0.0f, 0.222f, 0.428f, 0.427f, -0.513f, -0.0f, 0.232f, 0.428f, 0.427f, -0.51f, -0.0f, 0.241f, 0.429f, 0.427f, + -0.508f, -0.0f, 0.25f, 0.43f, 0.428f, -0.505f, -0.0f, 0.259f, 0.431f, 0.431f, -0.501f, -0.0f, 0.267f, 0.431f, 0.432f, -0.497f, -0.0f, 0.276f, 0.432f, 0.433f, + -0.493f, -0.0f, 0.284f, 0.433f, 0.433f, -0.488f, -0.0f, 0.293f, 0.434f, 0.434f, -0.484f, -0.0f, 0.301f, 0.434f, 0.435f, -0.479f, -0.0f, 0.308f, 0.435f, 0.436f, + -0.474f, -0.0f, 0.316f, 0.435f, 0.435f, -0.468f, -0.0f, 0.322f, 0.436f, 0.436f, -0.463f, -0.0f, 0.329f, 0.436f, 0.436f, -0.457f, -0.0f, 0.335f, 0.436f, 0.436f, + -0.451f, -0.0f, 0.341f, 0.437f, 0.436f, -0.445f, -0.0f, 0.347f, 0.438f, 0.437f, -0.438f, -0.0f, 0.352f, 0.44f, 0.437f, -0.432f, -0.0f, 0.357f, 0.442f, 0.441f, + -0.426f, 0.0f, 0.362f, 0.444f, 0.446f, -0.419f, 0.0f, 0.366f, 0.445f, 0.447f, -0.413f, 0.0f, 0.369f, 0.446f, 0.447f, -0.407f, 0.0f, 0.373f, 0.446f, 0.447f, + -0.401f, 0.0f, 0.376f, 0.447f, 0.447f, -0.395f, 0.0f, 0.378f, 0.447f, 0.448f, -0.388f, 0.0f, 0.381f, 0.447f, 0.448f, -0.382f, 0.0f, 0.383f, 0.448f, 0.448f, + -0.375f, 0.0f, 0.384f, 0.448f, 0.448f, -0.369f, 0.0f, 0.386f, 0.448f, 0.448f, -0.362f, 0.0f, 0.387f, 0.448f, 0.448f, -0.355f, 0.0f, 0.388f, 0.448f, 0.448f, + -0.348f, 0.0f, 0.388f, 0.448f, 0.448f, -0.341f, 0.0f, 0.387f, 0.448f, 0.449f, -0.334f, 0.0f, 0.387f, 0.448f, 0.448f, -0.327f, 0.0f, 0.386f, 0.448f, 0.449f, + -0.32f, 0.0f, 0.384f, 0.449f, 0.449f, -0.313f, 0.0f, 0.382f, 0.449f, 0.449f, -0.307f, 0.0f, 0.38f, 0.449f, 0.449f, -0.3f, 0.0f, 0.377f, 0.449f, 0.45f, + -0.294f, 0.0f, 0.375f, 0.45f, 0.45f, -0.288f, -0.0f, 0.372f, 0.45f, 0.45f, -0.282f, -0.0f, 0.368f, 0.45f, 0.451f, -0.276f, -0.0f, 0.365f, 0.45f, 0.451f, + -0.27f, -0.0f, 0.361f, 0.45f, 0.451f, -0.264f, -0.0f, 0.357f, 0.45f, 0.451f, -0.258f, -0.0f, 0.352f, 0.45f, 0.45f, -0.251f, -0.0f, 0.347f, 0.45f, 0.451f, + -0.245f, -0.0f, 0.341f, 0.451f, 0.451f, -0.24f, -0.0f, 0.335f, 0.451f, 0.451f, -0.234f, -0.0f, 0.329f, 0.451f, 0.451f, -0.228f, -0.0f, 0.323f, 0.452f, 0.452f, + -0.223f, -0.0f, 0.316f, 0.452f, 0.453f, -0.218f, -0.0f, 0.309f, 0.452f, 0.453f, -0.213f, -0.0f, 0.301f, 0.453f, 0.453f, -0.208f, -0.0f, 0.294f, 0.453f, 0.453f, + -0.204f, -0.0f, 0.286f, 0.453f, 0.453f, -0.2f, -0.0f, 0.277f, 0.453f, 0.454f, -0.196f, -0.0f, 0.269f, 0.453f, 0.454f, -0.192f, -0.0f, 0.26f, 0.454f, 0.454f, + -0.189f, -0.0f, 0.25f, 0.454f, 0.454f, -0.186f, -0.0f, 0.241f, 0.454f, 0.455f, -0.183f, -0.0f, 0.231f, 0.454f, 0.455f, -0.181f, -0.0f, 0.221f, 0.454f, 0.455f, + -0.179f, -0.0f, 0.209f, 0.455f, 0.455f, -0.177f, -0.0f, 0.197f, 0.455f, 0.455f, -0.176f, -0.0f, 0.184f, 0.455f, 0.455f, -0.176f, -0.0f, 0.171f, 0.455f, 0.456f, + -0.176f, -0.0f, 0.158f, 0.455f, 0.456f, -0.177f, -0.0f, 0.145f, 0.455f, 0.456f, -0.178f, -0.0f, 0.132f, 0.455f, 0.456f, -0.18f, -0.0f, 0.12f, 0.456f, 0.456f, + -0.182f, -0.0f, 0.108f, 0.456f, 0.456f, -0.185f, -0.0f, 0.097f, 0.456f, 0.456f, -0.188f, -0.0f, 0.086f, 0.456f, 0.457f, -0.191f, -0.0f, 0.076f, 0.456f, 0.457f, + -0.194f, -0.0f, 0.067f, 0.457f, 0.457f, -0.198f, -0.0f, 0.058f, 0.457f, 0.457f, -0.202f, -0.0f, 0.05f, 0.457f, 0.457f, -0.206f, -0.0f, 0.042f, 0.457f, 0.457f, + -0.21f, -0.0f, 0.034f, 0.458f, 0.457f, -0.215f, -0.0f, 0.027f, 0.458f, 0.457f, -0.22f, -0.0f, 0.02f, 0.458f, 0.458f, -0.225f, -0.0f, 0.014f, 0.458f, 0.458f, + -0.23f, -0.0f, 0.007f, 0.458f, 0.458f, -0.235f, -0.0f, 0.002f, 0.459f, 0.458f, -0.24f, -0.0f, -0.004f, 0.459f, 0.458f, -0.246f, -0.0f, -0.009f, 0.46f, 0.459f, + -0.251f, 0.0f, -0.013f, 0.464f, 0.463f, -0.257f, 0.0f, -0.018f, 0.467f, 0.468f, -0.262f, 0.0f, -0.022f, 0.469f, 0.469f, -0.268f, 0.0f, -0.026f, 0.471f, 0.47f, + -0.274f, 0.0f, -0.029f, 0.477f, 0.478f, -0.28f, 0.0f, -0.033f, 0.478f, 0.478f, -0.286f, 0.0f, -0.036f, 0.478f, 0.478f, -0.292f, 0.0f, -0.038f, 0.479f, 0.479f, + -0.298f, 0.0f, -0.041f, 0.48f, 0.48f, -0.305f, 0.0f, -0.043f, 0.48f, 0.48f, -0.311f, 0.0f, -0.045f, 0.482f, 0.482f, -0.318f, 0.0f, -0.047f, 0.482f, 0.482f, + -0.324f, 0.0f, -0.048f, 0.482f, 0.482f, -0.331f, 0.0f, -0.049f, 0.48f, 0.482f, -0.336f, 0.0f, -0.05f, 0.457f, 0.485f, -0.344f, 0.0f, -0.05f, 0.32f, 0.32f, +}; + +static const float data2[2 * GP_PRIM_DATABUF_SIZE] = { + -0.512f, 0.0f, -0.168f, 0.545f, 0.557f, -0.521f, 0.0f, -0.167f, 0.535f, 0.558f, +}; + +static const float data3[1 * GP_PRIM_DATABUF_SIZE] = { + -1.014f, 0.0f, 0.186f, 0.0f, 0.003f, +}; + +static const float data4[1 * GP_PRIM_DATABUF_SIZE] = { + -1.014f, 0.0f, 0.186f, 0.02f, 0.02f, +}; + +static const float data5[48 * GP_PRIM_DATABUF_SIZE] = { + -1.014f, 0.0f, 0.187f, 0.066f, 0.066f, -1.013f, 0.0f, 0.2f, 0.222f, 0.356f, -1.01f, 0.0f, 0.208f, 0.295f, 0.404f, -1.006f, 0.0f, 0.218f, 0.354f, 0.431f, + -1.001f, 0.0f, 0.226f, 0.392f, 0.445f, -0.994f, 0.0f, 0.233f, 0.418f, 0.453f, -0.987f, 0.0f, 0.238f, 0.437f, 0.457f, -0.979f, 0.0f, 0.242f, 0.45f, 0.47f, + -0.97f, 0.0f, 0.245f, 0.459f, 0.473f, -0.96f, -0.0f, 0.246f, 0.465f, 0.474f, -0.951f, -0.0f, 0.245f, 0.469f, 0.475f, -0.942f, 0.0f, 0.242f, 0.471f, 0.473f, + -0.932f, 0.0f, 0.239f, 0.472f, 0.474f, -0.924f, 0.0f, 0.234f, 0.471f, 0.474f, -0.915f, 0.0f, 0.228f, 0.469f, 0.474f, -0.906f, 0.0f, 0.22f, 0.464f, 0.47f, + -0.898f, 0.0f, 0.212f, 0.458f, 0.46f, -0.89f, 0.0f, 0.203f, 0.451f, 0.453f, -0.882f, 0.0f, 0.193f, 0.443f, 0.443f, -0.875f, 0.0f, 0.182f, 0.435f, 0.437f, + -0.869f, 0.0f, 0.172f, 0.426f, 0.428f, -0.863f, 0.0f, 0.161f, 0.417f, 0.415f, -0.858f, 0.0f, 0.148f, 0.409f, 0.41f, -0.854f, 0.0f, 0.137f, 0.399f, 0.399f, + -0.85f, 0.0f, 0.126f, 0.39f, 0.392f, -0.847f, 0.0f, 0.116f, 0.379f, 0.386f, -0.846f, 0.0f, 0.109f, 0.369f, 0.371f, -0.846f, 0.0f, 0.104f, 0.361f, 0.357f, + -0.847f, 0.0f, 0.101f, 0.355f, 0.339f, -0.849f, 0.0f, 0.101f, 0.353f, 0.334f, -0.853f, 0.0f, 0.103f, 0.354f, 0.345f, -0.859f, 0.0f, 0.108f, 0.357f, 0.35f, + -0.865f, 0.0f, 0.116f, 0.363f, 0.365f, -0.873f, 0.0f, 0.126f, 0.369f, 0.375f, -0.881f, 0.0f, 0.137f, 0.375f, 0.379f, -0.89f, 0.0f, 0.149f, 0.381f, 0.38f, + -0.899f, 0.0f, 0.159f, 0.387f, 0.385f, -0.908f, 0.0f, 0.168f, 0.394f, 0.394f, -0.919f, 0.0f, 0.177f, 0.401f, 0.398f, -0.932f, 0.0f, 0.184f, 0.409f, 0.404f, + -0.945f, 0.0f, 0.191f, 0.418f, 0.415f, -0.958f, 0.0f, 0.195f, 0.427f, 0.431f, -0.969f, 0.0f, 0.197f, 0.434f, 0.443f, -0.979f, 0.0f, 0.197f, 0.436f, 0.445f, + -0.987f, 0.0f, 0.195f, 0.428f, 0.463f, -0.995f, 0.0f, 0.192f, 0.398f, 0.46f, -1.001f, 0.0f, 0.189f, 0.345f, 0.465f, -1.01f, 0.0f, 0.183f, 0.236f, 0.236f, +}; + +static const float data6[47 * GP_PRIM_DATABUF_SIZE] = { + 0.022f, 0.0f, -0.353f, 0.125f, 0.125f, 0.012f, 0.0f, -0.352f, 0.175f, 0.288f, 0.004f, 0.0f, -0.352f, 0.206f, 0.313f, -0.006f, 0.0f, -0.352f, 0.241f, 0.323f, + -0.017f, 0.0f, -0.352f, 0.27f, 0.33f, -0.029f, 0.0f, -0.351f, 0.295f, 0.334f, -0.041f, 0.0f, -0.349f, 0.314f, 0.337f, -0.052f, 0.0f, -0.344f, 0.327f, 0.341f, + -0.063f, 0.0f, -0.337f, 0.336f, 0.344f, -0.072f, 0.0f, -0.329f, 0.341f, 0.345f, -0.081f, 0.0f, -0.32f, 0.345f, 0.345f, -0.088f, 0.0f, -0.311f, 0.348f, 0.345f, + -0.093f, 0.0f, -0.303f, 0.352f, 0.347f, -0.098f, 0.0f, -0.295f, 0.356f, 0.352f, -0.101f, 0.0f, -0.287f, 0.361f, 0.357f, -0.102f, 0.0f, -0.279f, 0.367f, 0.364f, + -0.103f, 0.0f, -0.271f, 0.373f, 0.378f, -0.102f, 0.0f, -0.263f, 0.379f, 0.382f, -0.1f, 0.0f, -0.255f, 0.383f, 0.389f, -0.098f, 0.0f, -0.247f, 0.387f, 0.391f, + -0.094f, 0.0f, -0.24f, 0.389f, 0.393f, -0.09f, 0.0f, -0.233f, 0.391f, 0.393f, -0.086f, 0.0f, -0.227f, 0.392f, 0.393f, -0.082f, 0.0f, -0.222f, 0.393f, 0.393f, + -0.078f, 0.0f, -0.219f, 0.394f, 0.393f, -0.075f, 0.0f, -0.217f, 0.397f, 0.393f, -0.072f, 0.0f, -0.217f, 0.4f, 0.393f, -0.07f, 0.0f, -0.219f, 0.402f, 0.408f, + -0.069f, 0.0f, -0.222f, 0.404f, 0.408f, -0.069f, 0.0f, -0.228f, 0.406f, 0.409f, -0.069f, 0.0f, -0.234f, 0.407f, 0.409f, -0.07f, 0.0f, -0.241f, 0.408f, 0.409f, + -0.07f, 0.0f, -0.248f, 0.408f, 0.409f, -0.07f, 0.0f, -0.256f, 0.409f, 0.409f, -0.07f, 0.0f, -0.263f, 0.409f, 0.41f, -0.069f, 0.0f, -0.271f, 0.41f, 0.411f, + -0.068f, 0.0f, -0.279f, 0.41f, 0.411f, -0.065f, 0.0f, -0.287f, 0.41f, 0.411f, -0.062f, 0.0f, -0.295f, 0.409f, 0.411f, -0.057f, 0.0f, -0.303f, 0.409f, 0.409f, + -0.052f, 0.0f, -0.31f, 0.408f, 0.409f, -0.047f, 0.0f, -0.318f, 0.407f, 0.408f, -0.041f, 0.0f, -0.324f, 0.406f, 0.407f, -0.035f, 0.0f, -0.329f, 0.403f, 0.407f, + -0.027f, 0.0f, -0.333f, 0.4f, 0.408f, -0.021f, 0.0f, -0.336f, 0.398f, 0.403f, -0.012f, 0.0f, -0.339f, 0.393f, 0.393f, +}; + +static const float data7[162 * GP_PRIM_DATABUF_SIZE] = { + -0.291f, 0.0f, -0.34f, 0.093f, 0.093f, -0.289f, -0.0f, -0.35f, 0.149f, 0.176f, -0.287f, -0.0f, -0.357f, 0.182f, 0.242f, -0.284f, -0.0f, -0.365f, 0.215f, 0.257f, + -0.281f, -0.0f, -0.374f, 0.242f, 0.266f, -0.278f, -0.0f, -0.384f, 0.266f, 0.287f, -0.275f, -0.0f, -0.394f, 0.285f, 0.304f, -0.271f, 0.0f, -0.405f, 0.302f, 0.316f, + -0.267f, 0.0f, -0.417f, 0.317f, 0.326f, -0.263f, 0.0f, -0.429f, 0.33f, 0.337f, -0.259f, 0.0f, -0.442f, 0.342f, 0.346f, -0.256f, 0.0f, -0.454f, 0.354f, 0.351f, + -0.253f, 0.0f, -0.467f, 0.365f, 0.362f, -0.251f, 0.0f, -0.48f, 0.376f, 0.38f, -0.249f, -0.0f, -0.493f, 0.386f, 0.391f, -0.247f, -0.0f, -0.505f, 0.394f, 0.396f, + -0.246f, -0.0f, -0.518f, 0.401f, 0.405f, -0.245f, 0.0f, -0.53f, 0.408f, 0.409f, -0.245f, 0.0f, -0.542f, 0.415f, 0.413f, -0.245f, 0.0f, -0.554f, 0.421f, 0.42f, + -0.245f, 0.0f, -0.565f, 0.426f, 0.43f, -0.246f, 0.0f, -0.575f, 0.43f, 0.433f, -0.246f, -0.0f, -0.585f, 0.432f, 0.435f, -0.247f, -0.0f, -0.594f, 0.434f, 0.436f, + -0.247f, -0.0f, -0.603f, 0.435f, 0.436f, -0.248f, -0.0f, -0.612f, 0.436f, 0.436f, -0.25f, -0.0f, -0.621f, 0.437f, 0.438f, -0.252f, -0.0f, -0.631f, 0.437f, 0.438f, + -0.254f, -0.0f, -0.642f, 0.438f, 0.438f, -0.255f, 0.0f, -0.653f, 0.438f, 0.438f, -0.258f, 0.0f, -0.664f, 0.438f, 0.439f, -0.26f, 0.0f, -0.674f, 0.439f, 0.439f, + -0.261f, 0.0f, -0.685f, 0.439f, 0.439f, -0.262f, 0.0f, -0.696f, 0.439f, 0.439f, -0.264f, 0.0f, -0.706f, 0.439f, 0.439f, -0.265f, 0.0f, -0.717f, 0.439f, 0.439f, + -0.265f, 0.0f, -0.727f, 0.438f, 0.439f, -0.266f, 0.0f, -0.738f, 0.437f, 0.439f, -0.266f, 0.0f, -0.749f, 0.435f, 0.438f, -0.266f, 0.0f, -0.76f, 0.433f, 0.433f, + -0.265f, 0.0f, -0.771f, 0.431f, 0.428f, -0.265f, 0.0f, -0.781f, 0.43f, 0.428f, -0.263f, 0.0f, -0.792f, 0.429f, 0.428f, -0.26f, 0.0f, -0.802f, 0.428f, 0.429f, + -0.257f, 0.0f, -0.812f, 0.426f, 0.427f, -0.254f, 0.0f, -0.821f, 0.423f, 0.426f, -0.25f, 0.0f, -0.829f, 0.421f, 0.42f, -0.247f, 0.0f, -0.837f, 0.418f, 0.416f, + -0.242f, 0.0f, -0.844f, 0.417f, 0.415f, -0.238f, 0.0f, -0.85f, 0.415f, 0.413f, -0.234f, 0.0f, -0.857f, 0.415f, 0.413f, -0.229f, 0.0f, -0.864f, 0.414f, 0.413f, + -0.224f, 0.0f, -0.87f, 0.414f, 0.413f, -0.219f, 0.0f, -0.877f, 0.414f, 0.414f, -0.214f, 0.0f, -0.883f, 0.414f, 0.413f, -0.208f, 0.0f, -0.89f, 0.413f, 0.413f, + -0.203f, 0.0f, -0.897f, 0.413f, 0.413f, -0.197f, 0.0f, -0.903f, 0.413f, 0.413f, -0.191f, 0.0f, -0.909f, 0.413f, 0.413f, -0.186f, 0.0f, -0.914f, 0.413f, 0.413f, + -0.181f, 0.0f, -0.92f, 0.413f, 0.413f, -0.175f, -0.0f, -0.925f, 0.413f, 0.413f, -0.17f, -0.0f, -0.931f, 0.413f, 0.413f, -0.164f, -0.0f, -0.936f, 0.413f, 0.413f, + -0.159f, -0.0f, -0.942f, 0.413f, 0.413f, -0.152f, -0.0f, -0.948f, 0.413f, 0.413f, -0.145f, -0.0f, -0.955f, 0.413f, 0.413f, -0.137f, -0.0f, -0.961f, 0.414f, 0.413f, + -0.13f, -0.0f, -0.967f, 0.414f, 0.413f, -0.122f, -0.0f, -0.974f, 0.414f, 0.414f, -0.114f, -0.0f, -0.979f, 0.414f, 0.413f, -0.106f, -0.0f, -0.985f, 0.414f, 0.413f, + -0.098f, -0.0f, -0.989f, 0.414f, 0.414f, -0.091f, -0.0f, -0.993f, 0.414f, 0.413f, -0.083f, -0.0f, -0.997f, 0.414f, 0.414f, -0.075f, -0.0f, -0.999f, 0.414f, 0.414f, + -0.066f, -0.0f, -1.001f, 0.414f, 0.414f, -0.057f, -0.0f, -1.003f, 0.414f, 0.413f, -0.046f, -0.0f, -1.006f, 0.414f, 0.413f, -0.038f, -0.0f, -1.008f, 0.414f, 0.413f, + -0.031f, -0.0f, -1.009f, 0.421f, 0.413f, -0.036f, -0.0f, -1.008f, 0.423f, 0.424f, -0.045f, -0.0f, -1.006f, 0.425f, 0.425f, -0.054f, -0.0f, -1.005f, 0.425f, 0.425f, + -0.064f, -0.0f, -1.005f, 0.425f, 0.425f, -0.073f, -0.0f, -1.004f, 0.425f, 0.425f, -0.084f, -0.0f, -1.003f, 0.425f, 0.425f, -0.095f, -0.0f, -1.001f, 0.424f, 0.424f, + -0.105f, -0.0f, -0.997f, 0.423f, 0.424f, -0.116f, -0.0f, -0.994f, 0.422f, 0.422f, -0.127f, -0.0f, -0.991f, 0.421f, 0.419f, -0.137f, -0.0f, -0.987f, 0.42f, 0.419f, + -0.148f, -0.0f, -0.983f, 0.42f, 0.419f, -0.158f, -0.0f, -0.98f, 0.42f, 0.419f, -0.167f, -0.0f, -0.976f, 0.419f, 0.419f, -0.176f, -0.0f, -0.973f, 0.419f, 0.419f, + -0.184f, -0.0f, -0.969f, 0.419f, 0.419f, -0.192f, -0.0f, -0.966f, 0.419f, 0.418f, -0.2f, 0.0f, -0.962f, 0.419f, 0.418f, -0.207f, 0.0f, -0.957f, 0.419f, 0.419f, + -0.215f, 0.0f, -0.953f, 0.419f, 0.418f, -0.223f, 0.0f, -0.948f, 0.419f, 0.419f, -0.231f, 0.0f, -0.944f, 0.419f, 0.419f, -0.239f, 0.0f, -0.939f, 0.419f, 0.419f, + -0.247f, 0.0f, -0.934f, 0.419f, 0.419f, -0.255f, 0.0f, -0.929f, 0.419f, 0.419f, -0.262f, 0.0f, -0.924f, 0.419f, 0.419f, -0.269f, 0.0f, -0.919f, 0.419f, 0.418f, + -0.275f, 0.0f, -0.914f, 0.419f, 0.419f, -0.281f, 0.0f, -0.909f, 0.419f, 0.418f, -0.287f, 0.0f, -0.904f, 0.419f, 0.418f, -0.293f, 0.0f, -0.899f, 0.419f, 0.418f, + -0.299f, 0.0f, -0.894f, 0.42f, 0.419f, -0.304f, 0.0f, -0.888f, 0.421f, 0.42f, -0.311f, 0.0f, -0.882f, 0.423f, 0.422f, -0.317f, 0.0f, -0.876f, 0.424f, 0.424f, + -0.322f, 0.0f, -0.869f, 0.426f, 0.426f, -0.328f, 0.0f, -0.861f, 0.427f, 0.427f, -0.332f, 0.0f, -0.853f, 0.429f, 0.429f, -0.336f, 0.0f, -0.843f, 0.43f, 0.429f, + -0.339f, 0.0f, -0.834f, 0.432f, 0.431f, -0.341f, 0.0f, -0.821f, 0.435f, 0.434f, -0.342f, 0.0f, -0.809f, 0.438f, 0.439f, -0.343f, 0.0f, -0.796f, 0.44f, 0.44f, + -0.343f, 0.0f, -0.783f, 0.442f, 0.442f, -0.343f, 0.0f, -0.772f, 0.446f, 0.445f, -0.342f, 0.0f, -0.76f, 0.45f, 0.45f, -0.342f, 0.0f, -0.748f, 0.454f, 0.455f, + -0.34f, 0.0f, -0.735f, 0.457f, 0.457f, -0.339f, 0.0f, -0.723f, 0.46f, 0.46f, -0.338f, 0.0f, -0.711f, 0.463f, 0.464f, -0.336f, 0.0f, -0.7f, 0.465f, 0.465f, + -0.335f, 0.0f, -0.688f, 0.466f, 0.466f, -0.332f, 0.0f, -0.676f, 0.467f, 0.467f, -0.331f, 0.0f, -0.664f, 0.467f, 0.467f, -0.33f, 0.0f, -0.651f, 0.467f, 0.467f, + -0.328f, 0.0f, -0.638f, 0.467f, 0.467f, -0.325f, 0.0f, -0.625f, 0.467f, 0.467f, -0.323f, 0.0f, -0.614f, 0.467f, 0.467f, -0.321f, 0.0f, -0.603f, 0.467f, 0.466f, + -0.318f, 0.0f, -0.592f, 0.467f, 0.466f, -0.315f, 0.0f, -0.581f, 0.467f, 0.466f, -0.313f, 0.0f, -0.569f, 0.467f, 0.467f, -0.311f, -0.0f, -0.557f, 0.467f, 0.467f, + -0.309f, -0.0f, -0.543f, 0.467f, 0.467f, -0.306f, -0.0f, -0.531f, 0.467f, 0.467f, -0.303f, -0.0f, -0.519f, 0.467f, 0.467f, -0.301f, -0.0f, -0.507f, 0.467f, 0.468f, + -0.299f, -0.0f, -0.497f, 0.467f, 0.467f, -0.297f, -0.0f, -0.487f, 0.467f, 0.467f, -0.295f, 0.0f, -0.476f, 0.465f, 0.467f, -0.293f, 0.0f, -0.466f, 0.463f, 0.467f, + -0.292f, 0.0f, -0.456f, 0.46f, 0.466f, -0.291f, 0.0f, -0.445f, 0.455f, 0.459f, -0.29f, 0.0f, -0.435f, 0.449f, 0.457f, -0.29f, 0.0f, -0.424f, 0.44f, 0.448f, + -0.29f, 0.0f, -0.413f, 0.43f, 0.44f, -0.29f, 0.0f, -0.403f, 0.418f, 0.437f, -0.29f, -0.0f, -0.393f, 0.404f, 0.415f, -0.291f, -0.0f, -0.384f, 0.388f, 0.393f, + -0.29f, -0.0f, -0.376f, 0.374f, 0.379f, -0.29f, -0.0f, -0.365f, 0.352f, 0.352f, +}; + +static const float data8[55 * GP_PRIM_DATABUF_SIZE] = { + 0.781f, 0.0f, 0.098f, 0.109f, 0.109f, 0.784f, 0.0f, 0.105f, 0.202f, 0.338f, 0.785f, 0.0f, 0.108f, 0.254f, 0.369f, 0.787f, 0.0f, 0.113f, 0.306f, 0.382f, + 0.787f, 0.0f, 0.118f, 0.344f, 0.392f, 0.789f, 0.0f, 0.123f, 0.372f, 0.401f, 0.79f, 0.0f, 0.128f, 0.392f, 0.41f, 0.792f, 0.0f, 0.135f, 0.406f, 0.42f, + 0.794f, 0.0f, 0.142f, 0.416f, 0.424f, 0.797f, 0.0f, 0.152f, 0.424f, 0.428f, 0.801f, 0.0f, 0.161f, 0.429f, 0.431f, 0.807f, 0.0f, 0.172f, 0.432f, 0.435f, + 0.814f, 0.0f, 0.182f, 0.435f, 0.438f, 0.821f, 0.0f, 0.19f, 0.437f, 0.439f, 0.828f, 0.0f, 0.197f, 0.439f, 0.44f, 0.836f, 0.0f, 0.204f, 0.44f, 0.441f, + 0.845f, -0.0f, 0.211f, 0.44f, 0.441f, 0.853f, -0.0f, 0.215f, 0.441f, 0.441f, 0.861f, -0.0f, 0.219f, 0.441f, 0.441f, 0.87f, -0.0f, 0.222f, 0.441f, 0.442f, + 0.878f, -0.0f, 0.224f, 0.441f, 0.442f, 0.886f, -0.0f, 0.226f, 0.441f, 0.442f, 0.895f, -0.0f, 0.227f, 0.44f, 0.442f, 0.903f, 0.0f, 0.226f, 0.439f, 0.441f, + 0.911f, 0.0f, 0.225f, 0.436f, 0.441f, 0.919f, 0.0f, 0.224f, 0.432f, 0.441f, 0.927f, 0.0f, 0.221f, 0.425f, 0.436f, 0.934f, 0.0f, 0.218f, 0.415f, 0.429f, + 0.94f, 0.0f, 0.215f, 0.404f, 0.406f, 0.944f, 0.0f, 0.211f, 0.393f, 0.389f, 0.947f, 0.0f, 0.208f, 0.384f, 0.378f, 0.948f, 0.0f, 0.204f, 0.376f, 0.371f, + 0.946f, 0.0f, 0.2f, 0.369f, 0.364f, 0.943f, 0.0f, 0.196f, 0.365f, 0.358f, 0.937f, 0.0f, 0.193f, 0.364f, 0.354f, 0.931f, 0.0f, 0.189f, 0.366f, 0.359f, + 0.925f, 0.0f, 0.186f, 0.37f, 0.367f, 0.917f, 0.0f, 0.182f, 0.374f, 0.375f, 0.908f, 0.0f, 0.177f, 0.378f, 0.382f, 0.899f, 0.0f, 0.172f, 0.381f, 0.384f, + 0.889f, 0.0f, 0.167f, 0.384f, 0.385f, 0.876f, 0.0f, 0.163f, 0.387f, 0.387f, 0.864f, 0.0f, 0.156f, 0.39f, 0.388f, 0.852f, 0.0f, 0.15f, 0.393f, 0.39f, + 0.841f, 0.0f, 0.144f, 0.396f, 0.396f, 0.832f, 0.0f, 0.138f, 0.399f, 0.401f, 0.826f, 0.0f, 0.133f, 0.401f, 0.404f, 0.82f, 0.0f, 0.127f, 0.403f, 0.405f, + 0.816f, 0.0f, 0.122f, 0.403f, 0.407f, 0.812f, 0.0f, 0.119f, 0.399f, 0.406f, 0.808f, 0.0f, 0.115f, 0.39f, 0.405f, 0.805f, 0.0f, 0.113f, 0.371f, 0.407f, + 0.801f, 0.0f, 0.111f, 0.341f, 0.407f, 0.799f, 0.0f, 0.109f, 0.309f, 0.405f, 0.795f, 0.0f, 0.106f, 0.255f, 0.255f, +}; + +static const float data9[70 * GP_PRIM_DATABUF_SIZE] = { + 0.819f, -0.0f, 0.325f, 0.109f, 0.109f, 0.829f, -0.0f, 0.328f, 0.258f, 0.403f, 0.835f, -0.0f, 0.329f, 0.327f, 0.428f, 0.843f, -0.0f, 0.331f, 0.383f, 0.452f, + 0.851f, -0.0f, 0.332f, 0.419f, 0.465f, 0.861f, -0.0f, 0.334f, 0.444f, 0.473f, 0.87f, -0.0f, 0.336f, 0.461f, 0.48f, 0.881f, -0.0f, 0.337f, 0.473f, 0.486f, + 0.892f, -0.0f, 0.339f, 0.482f, 0.496f, 0.904f, -0.0f, 0.341f, 0.489f, 0.501f, 0.917f, -0.0f, 0.342f, 0.494f, 0.503f, 0.931f, -0.0f, 0.342f, 0.498f, 0.505f, + 0.945f, -0.0f, 0.342f, 0.501f, 0.505f, 0.958f, -0.0f, 0.342f, 0.503f, 0.506f, 0.971f, -0.0f, 0.341f, 0.505f, 0.506f, 0.984f, -0.0f, 0.341f, 0.506f, 0.506f, + 0.997f, -0.0f, 0.339f, 0.507f, 0.508f, 1.009f, -0.0f, 0.337f, 0.507f, 0.507f, 1.021f, -0.0f, 0.333f, 0.508f, 0.508f, 1.033f, -0.0f, 0.33f, 0.508f, 0.508f, + 1.044f, -0.0f, 0.326f, 0.508f, 0.508f, 1.056f, -0.0f, 0.322f, 0.508f, 0.508f, 1.068f, -0.0f, 0.317f, 0.508f, 0.508f, 1.078f, -0.0f, 0.311f, 0.507f, 0.508f, + 1.089f, -0.0f, 0.304f, 0.506f, 0.508f, 1.099f, 0.0f, 0.294f, 0.503f, 0.506f, 1.107f, 0.0f, 0.287f, 0.498f, 0.506f, 1.113f, 0.0f, 0.28f, 0.49f, 0.505f, + 1.117f, 0.0f, 0.276f, 0.48f, 0.501f, 1.121f, 0.0f, 0.272f, 0.468f, 0.492f, 1.124f, 0.0f, 0.27f, 0.455f, 0.467f, 1.127f, 0.0f, 0.27f, 0.443f, 0.431f, + 1.129f, 0.0f, 0.271f, 0.431f, 0.4f, 1.13f, 0.0f, 0.274f, 0.422f, 0.399f, 1.13f, 0.0f, 0.278f, 0.414f, 0.399f, 1.13f, 0.0f, 0.286f, 0.408f, 0.399f, + 1.128f, 0.0f, 0.295f, 0.404f, 0.399f, 1.124f, 0.0f, 0.305f, 0.402f, 0.399f, 1.119f, 0.0f, 0.316f, 0.403f, 0.4f, 1.113f, -0.0f, 0.327f, 0.405f, 0.401f, + 1.107f, -0.0f, 0.337f, 0.408f, 0.411f, 1.1f, -0.0f, 0.345f, 0.412f, 0.412f, 1.094f, -0.0f, 0.352f, 0.416f, 0.413f, 1.087f, -0.0f, 0.357f, 0.421f, 0.422f, + 1.08f, -0.0f, 0.363f, 0.426f, 0.428f, 1.071f, -0.0f, 0.368f, 0.429f, 0.43f, 1.062f, -0.0f, 0.373f, 0.431f, 0.431f, 1.051f, -0.0f, 0.377f, 0.433f, 0.431f, + 1.039f, -0.0f, 0.381f, 0.436f, 0.437f, 1.026f, -0.0f, 0.383f, 0.438f, 0.44f, 1.013f, -0.0f, 0.384f, 0.44f, 0.44f, 1.0f, -0.0f, 0.385f, 0.441f, 0.443f, + 0.987f, -0.0f, 0.385f, 0.442f, 0.443f, 0.975f, -0.0f, 0.384f, 0.443f, 0.443f, 0.962f, -0.0f, 0.383f, 0.443f, 0.444f, 0.949f, -0.0f, 0.381f, 0.443f, 0.443f, + 0.936f, -0.0f, 0.38f, 0.443f, 0.444f, 0.923f, -0.0f, 0.378f, 0.443f, 0.444f, 0.909f, -0.0f, 0.375f, 0.443f, 0.444f, 0.897f, -0.0f, 0.371f, 0.443f, 0.444f, + 0.886f, -0.0f, 0.367f, 0.443f, 0.443f, 0.876f, -0.0f, 0.363f, 0.443f, 0.444f, 0.868f, -0.0f, 0.359f, 0.443f, 0.442f, 0.86f, -0.0f, 0.355f, 0.442f, 0.443f, + 0.852f, -0.0f, 0.35f, 0.441f, 0.443f, 0.844f, -0.0f, 0.347f, 0.433f, 0.443f, 0.837f, -0.0f, 0.343f, 0.409f, 0.443f, 0.83f, -0.0f, 0.338f, 0.344f, 0.443f, + 0.824f, -0.0f, 0.335f, 0.239f, 0.437f, 0.815f, -0.0f, 0.326f, 0.0f, 0.003f, +}; + +static const float data10[227 * GP_PRIM_DATABUF_SIZE] = { + -0.675f, 0.0f, 0.411f, 0.099f, 0.099f, -0.669f, 0.0f, 0.418f, 0.358f, 0.358f, -0.666f, 0.0f, 0.424f, 0.381f, 0.381f, -0.662f, 0.0f, 0.431f, 0.389f, 0.389f, + -0.658f, 0.0f, 0.438f, 0.393f, 0.393f, -0.649f, 0.0f, 0.448f, 0.404f, 0.404f, -0.641f, 0.0f, 0.458f, 0.419f, 0.419f, -0.632f, 0.0f, 0.468f, 0.431f, 0.434f, + -0.626f, 0.0f, 0.476f, 0.435f, 0.436f, -0.62f, 0.0f, 0.484f, 0.437f, 0.438f, -0.615f, 0.0f, 0.492f, 0.439f, 0.439f, -0.61f, 0.0f, 0.499f, 0.439f, 0.44f, + -0.605f, 0.0f, 0.506f, 0.44f, 0.44f, -0.6f, 0.0f, 0.512f, 0.44f, 0.44f, -0.595f, 0.0f, 0.519f, 0.44f, 0.44f, -0.59f, 0.0f, 0.526f, 0.441f, 0.441f, + -0.584f, 0.0f, 0.532f, 0.441f, 0.441f, -0.579f, 0.0f, 0.539f, 0.441f, 0.441f, -0.573f, 0.0f, 0.545f, 0.442f, 0.442f, -0.566f, 0.0f, 0.551f, 0.443f, 0.443f, + -0.559f, 0.0f, 0.557f, 0.443f, 0.443f, -0.552f, 0.0f, 0.563f, 0.444f, 0.444f, -0.545f, 0.0f, 0.569f, 0.445f, 0.445f, -0.538f, 0.0f, 0.576f, 0.447f, 0.447f, + -0.532f, 0.0f, 0.582f, 0.448f, 0.448f, -0.525f, 0.0f, 0.589f, 0.45f, 0.45f, -0.519f, 0.0f, 0.595f, 0.451f, 0.452f, -0.513f, 0.0f, 0.602f, 0.452f, 0.453f, + -0.506f, 0.0f, 0.608f, 0.453f, 0.453f, -0.5f, 0.0f, 0.613f, 0.453f, 0.454f, -0.493f, 0.0f, 0.619f, 0.453f, 0.454f, -0.486f, 0.0f, 0.625f, 0.453f, 0.454f, + -0.479f, 0.0f, 0.631f, 0.453f, 0.454f, -0.472f, 0.0f, 0.637f, 0.453f, 0.454f, -0.464f, 0.0f, 0.642f, 0.453f, 0.454f, -0.457f, 0.0f, 0.649f, 0.453f, 0.454f, + -0.45f, 0.0f, 0.655f, 0.453f, 0.453f, -0.443f, 0.0f, 0.661f, 0.453f, 0.453f, -0.435f, 0.0f, 0.667f, 0.453f, 0.454f, -0.427f, 0.0f, 0.672f, 0.453f, 0.454f, + -0.419f, 0.0f, 0.677f, 0.453f, 0.454f, -0.411f, 0.0f, 0.682f, 0.453f, 0.453f, -0.403f, 0.0f, 0.688f, 0.453f, 0.453f, -0.395f, 0.0f, 0.692f, 0.453f, 0.454f, + -0.387f, 0.0f, 0.697f, 0.453f, 0.454f, -0.379f, 0.0f, 0.702f, 0.453f, 0.454f, -0.372f, 0.0f, 0.707f, 0.454f, 0.454f, -0.364f, 0.0f, 0.712f, 0.454f, 0.454f, + -0.356f, 0.0f, 0.716f, 0.454f, 0.454f, -0.349f, 0.0f, 0.721f, 0.454f, 0.454f, -0.342f, 0.0f, 0.725f, 0.454f, 0.454f, -0.334f, 0.0f, 0.73f, 0.454f, 0.454f, + -0.326f, 0.0f, 0.733f, 0.454f, 0.454f, -0.318f, 0.0f, 0.737f, 0.454f, 0.454f, -0.31f, 0.0f, 0.74f, 0.454f, 0.454f, -0.301f, 0.0f, 0.743f, 0.454f, 0.454f, + -0.293f, 0.0f, 0.746f, 0.454f, 0.455f, -0.284f, 0.0f, 0.749f, 0.454f, 0.455f, -0.274f, 0.0f, 0.752f, 0.455f, 0.455f, -0.265f, 0.0f, 0.755f, 0.455f, 0.455f, + -0.255f, 0.0f, 0.757f, 0.455f, 0.455f, -0.245f, 0.0f, 0.76f, 0.456f, 0.455f, -0.234f, 0.0f, 0.762f, 0.457f, 0.456f, -0.223f, 0.0f, 0.764f, 0.458f, 0.458f, + -0.212f, 0.0f, 0.766f, 0.459f, 0.46f, -0.201f, 0.0f, 0.769f, 0.461f, 0.46f, -0.189f, 0.0f, 0.771f, 0.462f, 0.461f, -0.177f, 0.0f, 0.773f, 0.464f, 0.463f, + -0.166f, 0.0f, 0.775f, 0.465f, 0.465f, -0.153f, 0.0f, 0.777f, 0.467f, 0.467f, -0.141f, 0.0f, 0.779f, 0.469f, 0.469f, -0.128f, 0.0f, 0.781f, 0.472f, 0.472f, + -0.116f, 0.0f, 0.782f, 0.474f, 0.473f, -0.101f, 0.0f, 0.782f, 0.477f, 0.477f, -0.087f, 0.0f, 0.783f, 0.482f, 0.477f, -0.073f, 0.0f, 0.783f, 0.489f, 0.483f, + -0.059f, 0.0f, 0.783f, 0.497f, 0.5f, -0.046f, 0.0f, 0.784f, 0.503f, 0.509f, -0.033f, 0.0f, 0.784f, 0.508f, 0.51f, -0.022f, 0.0f, 0.784f, 0.51f, 0.512f, + -0.011f, 0.0f, 0.785f, 0.512f, 0.512f, -0.0f, 0.0f, 0.786f, 0.513f, 0.512f, 0.011f, 0.0f, 0.786f, 0.515f, 0.513f, 0.022f, 0.0f, 0.786f, 0.517f, 0.517f, + 0.032f, 0.0f, 0.786f, 0.52f, 0.519f, 0.044f, 0.0f, 0.786f, 0.522f, 0.524f, 0.055f, 0.0f, 0.785f, 0.525f, 0.525f, 0.066f, 0.0f, 0.785f, 0.527f, 0.525f, + 0.076f, 0.0f, 0.784f, 0.53f, 0.53f, 0.086f, 0.0f, 0.783f, 0.532f, 0.533f, 0.097f, 0.0f, 0.782f, 0.535f, 0.534f, 0.108f, 0.0f, 0.782f, 0.538f, 0.541f, + 0.119f, 0.0f, 0.781f, 0.54f, 0.542f, 0.13f, 0.0f, 0.781f, 0.543f, 0.543f, 0.141f, 0.0f, 0.78f, 0.545f, 0.545f, 0.154f, 0.0f, 0.779f, 0.547f, 0.547f, + 0.165f, 0.0f, 0.777f, 0.549f, 0.548f, 0.177f, 0.0f, 0.775f, 0.55f, 0.552f, 0.188f, 0.0f, 0.772f, 0.552f, 0.552f, 0.199f, 0.0f, 0.77f, 0.553f, 0.553f, + 0.209f, 0.0f, 0.767f, 0.554f, 0.554f, 0.218f, 0.0f, 0.765f, 0.555f, 0.556f, 0.226f, 0.0f, 0.763f, 0.556f, 0.557f, 0.235f, 0.0f, 0.761f, 0.557f, 0.557f, + 0.244f, 0.0f, 0.758f, 0.558f, 0.558f, 0.253f, 0.0f, 0.755f, 0.559f, 0.559f, 0.263f, 0.0f, 0.752f, 0.56f, 0.559f, 0.272f, 0.0f, 0.749f, 0.561f, 0.56f, + 0.285f, 0.0f, 0.745f, 0.562f, 0.56f, 0.299f, 0.0f, 0.741f, 0.563f, 0.563f, 0.316f, 0.0f, 0.736f, 0.564f, 0.564f, 0.331f, 0.0f, 0.728f, 0.565f, 0.567f, + 0.349f, 0.0f, 0.718f, 0.565f, 0.568f, 0.365f, 0.0f, 0.708f, 0.566f, 0.568f, 0.38f, 0.0f, 0.699f, 0.566f, 0.568f, 0.39f, 0.0f, 0.693f, 0.566f, 0.568f, + 0.397f, 0.0f, 0.687f, 0.566f, 0.569f, 0.4f, 0.0f, 0.683f, 0.566f, 0.569f, 0.401f, 0.0f, 0.681f, 0.565f, 0.57f, 0.4f, 0.0f, 0.679f, 0.565f, 0.57f, + 0.397f, 0.0f, 0.678f, 0.564f, 0.57f, 0.393f, 0.0f, 0.678f, 0.564f, 0.565f, 0.387f, 0.0f, 0.678f, 0.563f, 0.559f, 0.379f, 0.0f, 0.679f, 0.562f, 0.558f, + 0.37f, 0.0f, 0.681f, 0.561f, 0.557f, 0.357f, 0.0f, 0.684f, 0.561f, 0.557f, 0.342f, 0.0f, 0.689f, 0.56f, 0.557f, 0.324f, 0.0f, 0.694f, 0.56f, 0.557f, + 0.307f, 0.0f, 0.697f, 0.559f, 0.558f, 0.291f, 0.0f, 0.699f, 0.559f, 0.558f, 0.274f, 0.0f, 0.701f, 0.559f, 0.557f, 0.26f, 0.0f, 0.703f, 0.558f, 0.558f, + 0.246f, 0.0f, 0.705f, 0.558f, 0.558f, 0.235f, 0.0f, 0.707f, 0.558f, 0.558f, 0.224f, 0.0f, 0.709f, 0.558f, 0.558f, 0.214f, 0.0f, 0.711f, 0.558f, 0.558f, + 0.203f, 0.0f, 0.713f, 0.558f, 0.559f, 0.192f, 0.0f, 0.714f, 0.558f, 0.558f, 0.181f, 0.0f, 0.714f, 0.557f, 0.557f, 0.17f, 0.0f, 0.714f, 0.557f, 0.557f, + 0.16f, 0.0f, 0.715f, 0.557f, 0.556f, 0.149f, 0.0f, 0.715f, 0.557f, 0.556f, 0.139f, 0.0f, 0.716f, 0.557f, 0.556f, 0.129f, 0.0f, 0.716f, 0.558f, 0.556f, + 0.119f, 0.0f, 0.717f, 0.558f, 0.556f, 0.109f, 0.0f, 0.717f, 0.558f, 0.557f, 0.099f, 0.0f, 0.718f, 0.558f, 0.557f, 0.089f, 0.0f, 0.718f, 0.559f, 0.557f, + 0.079f, 0.0f, 0.718f, 0.559f, 0.558f, 0.068f, 0.0f, 0.719f, 0.559f, 0.559f, 0.057f, 0.0f, 0.719f, 0.56f, 0.56f, 0.046f, 0.0f, 0.718f, 0.56f, 0.561f, + 0.035f, 0.0f, 0.718f, 0.561f, 0.561f, 0.024f, 0.0f, 0.718f, 0.561f, 0.562f, 0.013f, 0.0f, 0.717f, 0.562f, 0.562f, 0.002f, 0.0f, 0.717f, 0.562f, 0.563f, + -0.01f, 0.0f, 0.717f, 0.563f, 0.564f, -0.021f, 0.0f, 0.717f, 0.563f, 0.564f, -0.032f, 0.0f, 0.716f, 0.563f, 0.564f, -0.044f, 0.0f, 0.715f, 0.564f, 0.564f, + -0.055f, 0.0f, 0.714f, 0.564f, 0.565f, -0.066f, 0.0f, 0.713f, 0.564f, 0.565f, -0.078f, 0.0f, 0.712f, 0.564f, 0.564f, -0.089f, 0.0f, 0.711f, 0.564f, 0.564f, + -0.101f, 0.0f, 0.709f, 0.565f, 0.564f, -0.112f, 0.0f, 0.708f, 0.565f, 0.564f, -0.124f, 0.0f, 0.707f, 0.565f, 0.564f, -0.135f, 0.0f, 0.705f, 0.565f, 0.564f, + -0.146f, 0.0f, 0.704f, 0.566f, 0.564f, -0.158f, 0.0f, 0.702f, 0.566f, 0.564f, -0.169f, 0.0f, 0.7f, 0.566f, 0.566f, -0.18f, 0.0f, 0.698f, 0.567f, 0.568f, + -0.191f, 0.0f, 0.696f, 0.567f, 0.568f, -0.203f, 0.0f, 0.693f, 0.567f, 0.568f, -0.215f, 0.0f, 0.69f, 0.567f, 0.568f, -0.227f, 0.0f, 0.687f, 0.567f, 0.568f, + -0.238f, 0.0f, 0.684f, 0.567f, 0.568f, -0.25f, 0.0f, 0.681f, 0.567f, 0.569f, -0.262f, 0.0f, 0.678f, 0.567f, 0.569f, -0.273f, 0.0f, 0.675f, 0.567f, 0.567f, + -0.284f, 0.0f, 0.673f, 0.567f, 0.566f, -0.295f, 0.0f, 0.671f, 0.567f, 0.567f, -0.305f, 0.0f, 0.669f, 0.566f, 0.567f, -0.316f, 0.0f, 0.666f, 0.566f, 0.567f, + -0.326f, 0.0f, 0.663f, 0.565f, 0.566f, -0.337f, 0.0f, 0.66f, 0.565f, 0.566f, -0.348f, 0.0f, 0.655f, 0.564f, 0.564f, -0.359f, 0.0f, 0.652f, 0.563f, 0.564f, + -0.369f, 0.0f, 0.648f, 0.562f, 0.563f, -0.379f, 0.0f, 0.644f, 0.561f, 0.56f, -0.389f, 0.0f, 0.64f, 0.561f, 0.559f, -0.399f, 0.0f, 0.636f, 0.56f, 0.559f, + -0.409f, 0.0f, 0.633f, 0.559f, 0.559f, -0.419f, 0.0f, 0.629f, 0.559f, 0.559f, -0.428f, 0.0f, 0.625f, 0.559f, 0.558f, -0.438f, 0.0f, 0.62f, 0.559f, 0.559f, + -0.447f, 0.0f, 0.615f, 0.559f, 0.559f, -0.457f, 0.0f, 0.61f, 0.559f, 0.559f, -0.466f, 0.0f, 0.605f, 0.559f, 0.559f, -0.474f, 0.0f, 0.6f, 0.559f, 0.559f, + -0.483f, 0.0f, 0.595f, 0.559f, 0.559f, -0.492f, 0.0f, 0.591f, 0.559f, 0.559f, -0.5f, 0.0f, 0.586f, 0.559f, 0.559f, -0.508f, 0.0f, 0.58f, 0.559f, 0.559f, + -0.515f, 0.0f, 0.574f, 0.559f, 0.559f, -0.523f, 0.0f, 0.568f, 0.559f, 0.559f, -0.531f, 0.0f, 0.562f, 0.559f, 0.558f, -0.54f, 0.0f, 0.556f, 0.559f, 0.558f, + -0.548f, 0.0f, 0.549f, 0.559f, 0.559f, -0.556f, 0.0f, 0.543f, 0.559f, 0.559f, -0.562f, 0.0f, 0.537f, 0.559f, 0.559f, -0.568f, 0.0f, 0.531f, 0.559f, 0.559f, + -0.574f, 0.0f, 0.524f, 0.559f, 0.559f, -0.58f, 0.0f, 0.518f, 0.558f, 0.559f, -0.586f, 0.0f, 0.512f, 0.557f, 0.558f, -0.591f, 0.0f, 0.506f, 0.555f, 0.557f, + -0.597f, 0.0f, 0.5f, 0.551f, 0.556f, -0.603f, 0.0f, 0.493f, 0.546f, 0.547f, -0.609f, 0.0f, 0.487f, 0.541f, 0.538f, -0.614f, 0.0f, 0.48f, 0.536f, 0.535f, + -0.621f, 0.0f, 0.473f, 0.534f, 0.534f, -0.628f, 0.0f, 0.467f, 0.534f, 0.534f, -0.637f, 0.0f, 0.459f, 0.534f, 0.534f, -0.642f, 0.0f, 0.452f, 0.532f, 0.532f, + -0.65f, 0.0f, 0.445f, 0.528f, 0.528f, -0.654f, 0.0f, 0.438f, 0.525f, 0.525f, -0.659f, 0.0f, 0.431f, 0.522f, 0.522f, +}; + +static const float data11[1 * GP_PRIM_DATABUF_SIZE] = { + -0.525f, 0.0f, 0.174f, 0.124f, 0.124f, +}; + +static const float data12[123 * GP_PRIM_DATABUF_SIZE] = { + -0.53f, 0.0f, 0.193f, 0.147f, 0.147f, -0.532f, 0.0f, 0.186f, 0.316f, 0.316f, -0.534f, 0.0f, 0.18f, 0.353f, 0.353f, -0.535f, 0.0f, 0.173f, 0.382f, 0.382f, + -0.537f, 0.0f, 0.165f, 0.384f, 0.384f, -0.538f, 0.0f, 0.155f, 0.387f, 0.387f, -0.539f, 0.0f, 0.145f, 0.393f, 0.393f, -0.54f, -0.0f, 0.134f, 0.399f, 0.399f, + -0.541f, -0.0f, 0.123f, 0.4f, 0.4f, -0.542f, -0.0f, 0.11f, 0.401f, 0.401f, -0.542f, 0.0f, 0.094f, 0.402f, 0.402f, -0.54f, 0.0f, 0.078f, 0.403f, 0.403f, + -0.538f, 0.0f, 0.061f, 0.404f, 0.404f, -0.535f, 0.0f, 0.045f, 0.404f, 0.404f, -0.531f, 0.0f, 0.031f, 0.404f, 0.404f, -0.526f, 0.0f, 0.018f, 0.404f, 0.404f, + -0.52f, -0.0f, 0.005f, 0.405f, 0.405f, -0.513f, -0.0f, -0.01f, 0.405f, 0.405f, -0.505f, -0.0f, -0.024f, 0.405f, 0.405f, -0.495f, -0.0f, -0.037f, 0.405f, 0.405f, + -0.485f, 0.0f, -0.051f, 0.405f, 0.405f, -0.474f, 0.0f, -0.064f, 0.406f, 0.406f, -0.462f, 0.0f, -0.076f, 0.405f, 0.405f, -0.451f, 0.0f, -0.086f, 0.406f, 0.406f, + -0.442f, 0.0f, -0.094f, 0.406f, 0.406f, -0.432f, 0.0f, -0.102f, 0.406f, 0.406f, -0.422f, 0.0f, -0.108f, 0.405f, 0.405f, -0.411f, 0.0f, -0.114f, 0.406f, 0.406f, + -0.4f, 0.0f, -0.119f, 0.405f, 0.405f, -0.389f, 0.0f, -0.122f, 0.406f, 0.406f, -0.378f, 0.0f, -0.125f, 0.407f, 0.407f, -0.365f, 0.0f, -0.127f, 0.412f, 0.412f, + -0.354f, 0.0f, -0.129f, 0.418f, 0.418f, -0.342f, 0.0f, -0.131f, 0.44f, 0.44f, -0.33f, 0.0f, -0.131f, 0.448f, 0.448f, -0.317f, 0.0f, -0.131f, 0.469f, 0.469f, + -0.305f, 0.0f, -0.13f, 0.477f, 0.477f, -0.293f, 0.0f, -0.128f, 0.482f, 0.482f, -0.278f, 0.0f, -0.125f, 0.494f, 0.494f, -0.266f, 0.0f, -0.121f, 0.5f, 0.5f, + -0.253f, 0.0f, -0.116f, 0.507f, 0.507f, -0.242f, 0.0f, -0.111f, 0.509f, 0.509f, -0.231f, 0.0f, -0.105f, 0.511f, 0.511f, -0.222f, 0.0f, -0.099f, 0.511f, 0.511f, + -0.213f, 0.0f, -0.092f, 0.512f, 0.512f, -0.206f, 0.0f, -0.084f, 0.513f, 0.513f, -0.199f, 0.0f, -0.076f, 0.514f, 0.514f, -0.192f, 0.0f, -0.067f, 0.515f, 0.515f, + -0.186f, -0.0f, -0.058f, 0.516f, 0.516f, -0.18f, -0.0f, -0.049f, 0.516f, 0.516f, -0.175f, -0.0f, -0.04f, 0.515f, 0.515f, -0.17f, -0.0f, -0.03f, 0.515f, 0.515f, + -0.166f, -0.0f, -0.02f, 0.516f, 0.516f, -0.163f, -0.0f, -0.01f, 0.504f, 0.504f, -0.159f, -0.0f, 0.002f, 0.502f, 0.502f, -0.155f, -0.0f, 0.014f, 0.501f, 0.501f, + -0.152f, -0.0f, 0.027f, 0.502f, 0.502f, -0.149f, -0.0f, 0.043f, 0.5f, 0.5f, -0.148f, -0.0f, 0.058f, 0.49f, 0.49f, -0.147f, -0.0f, 0.075f, 0.47f, 0.47f, + -0.146f, -0.0f, 0.09f, 0.463f, 0.463f, -0.146f, -0.0f, 0.105f, 0.454f, 0.454f, -0.146f, -0.0f, 0.12f, 0.427f, 0.427f, -0.148f, 0.0f, 0.133f, 0.413f, 0.413f, + -0.15f, 0.0f, 0.144f, 0.4f, 0.4f, -0.153f, 0.0f, 0.152f, 0.383f, 0.383f, -0.156f, 0.0f, 0.157f, 0.369f, 0.369f, -0.158f, 0.0f, 0.16f, 0.36f, 0.36f, + -0.16f, 0.0f, 0.158f, 0.349f, 0.349f, -0.162f, 0.0f, 0.154f, 0.364f, 0.364f, -0.164f, 0.0f, 0.147f, 0.37f, 0.37f, -0.166f, 0.0f, 0.139f, 0.378f, 0.378f, + -0.168f, 0.0f, 0.13f, 0.386f, 0.386f, -0.172f, 0.0f, 0.119f, 0.394f, 0.394f, -0.176f, -0.0f, 0.108f, 0.405f, 0.405f, -0.18f, -0.0f, 0.096f, 0.412f, 0.412f, + -0.185f, -0.0f, 0.084f, 0.417f, 0.417f, -0.191f, -0.0f, 0.073f, 0.425f, 0.425f, -0.196f, -0.0f, 0.063f, 0.431f, 0.431f, -0.202f, -0.0f, 0.053f, 0.441f, 0.441f, + -0.208f, -0.0f, 0.043f, 0.444f, 0.444f, -0.214f, -0.0f, 0.034f, 0.451f, 0.451f, -0.22f, 0.0f, 0.026f, 0.46f, 0.46f, -0.226f, 0.0f, 0.018f, 0.463f, 0.463f, + -0.232f, 0.0f, 0.01f, 0.474f, 0.474f, -0.239f, 0.0f, 0.004f, 0.477f, 0.477f, -0.247f, 0.0f, -0.003f, 0.48f, 0.48f, -0.255f, 0.0f, -0.008f, 0.483f, 0.483f, + -0.264f, 0.0f, -0.013f, 0.497f, 0.497f, -0.274f, 0.0f, -0.018f, 0.501f, 0.501f, -0.285f, 0.0f, -0.022f, 0.505f, 0.505f, -0.297f, 0.0f, -0.024f, 0.509f, 0.509f, + -0.311f, 0.0f, -0.025f, 0.51f, 0.51f, -0.325f, 0.0f, -0.024f, 0.512f, 0.512f, -0.339f, 0.0f, -0.023f, 0.512f, 0.512f, -0.354f, 0.0f, -0.022f, 0.513f, 0.513f, + -0.368f, 0.0f, -0.02f, 0.513f, 0.513f, -0.382f, 0.0f, -0.017f, 0.514f, 0.514f, -0.397f, 0.0f, -0.013f, 0.514f, 0.514f, -0.41f, 0.0f, -0.007f, 0.514f, 0.514f, + -0.422f, 0.0f, 0.001f, 0.513f, 0.513f, -0.434f, 0.0f, 0.009f, 0.514f, 0.514f, -0.446f, 0.0f, 0.018f, 0.514f, 0.514f, -0.458f, 0.0f, 0.028f, 0.514f, 0.514f, + -0.47f, -0.0f, 0.039f, 0.514f, 0.514f, -0.48f, 0.0f, 0.048f, 0.514f, 0.514f, -0.487f, 0.0f, 0.057f, 0.514f, 0.514f, -0.493f, 0.0f, 0.068f, 0.514f, 0.514f, + -0.498f, 0.0f, 0.08f, 0.514f, 0.514f, -0.502f, 0.0f, 0.092f, 0.514f, 0.514f, -0.506f, 0.0f, 0.104f, 0.514f, 0.514f, -0.509f, -0.0f, 0.116f, 0.515f, 0.515f, + -0.511f, -0.0f, 0.125f, 0.515f, 0.515f, -0.513f, -0.0f, 0.133f, 0.515f, 0.515f, -0.515f, -0.0f, 0.141f, 0.515f, 0.515f, -0.517f, 0.0f, 0.148f, 0.515f, 0.515f, + -0.519f, 0.0f, 0.155f, 0.514f, 0.514f, -0.52f, 0.0f, 0.161f, 0.514f, 0.514f, -0.522f, 0.0f, 0.168f, 0.514f, 0.514f, -0.523f, 0.0f, 0.174f, 0.514f, 0.514f, + -0.525f, 0.0f, 0.18f, 0.514f, 0.514f, -0.526f, 0.0f, 0.185f, 0.514f, 0.514f, -0.527f, 0.0f, 0.191f, 0.513f, 0.513f, +}; + +static const float data13[125 * GP_PRIM_DATABUF_SIZE] = { + 0.184f, 0.0f, 0.22f, 0.026f, 0.026f, 0.182f, 0.0f, 0.21f, 0.275f, 0.275f, 0.18f, 0.0f, 0.203f, 0.301f, 0.301f, 0.178f, 0.0f, 0.195f, 0.322f, 0.322f, + 0.176f, 0.0f, 0.186f, 0.343f, 0.343f, 0.173f, 0.0f, 0.176f, 0.36f, 0.36f, 0.17f, -0.0f, 0.166f, 0.367f, 0.367f, 0.168f, -0.0f, 0.156f, 0.38f, 0.38f, + 0.165f, -0.0f, 0.145f, 0.385f, 0.385f, 0.163f, -0.0f, 0.132f, 0.391f, 0.391f, 0.161f, -0.0f, 0.119f, 0.401f, 0.401f, 0.16f, -0.0f, 0.103f, 0.405f, 0.405f, + 0.161f, -0.0f, 0.086f, 0.405f, 0.405f, 0.163f, -0.0f, 0.068f, 0.407f, 0.407f, 0.165f, 0.0f, 0.051f, 0.409f, 0.409f, 0.168f, 0.0f, 0.034f, 0.409f, 0.409f, + 0.172f, 0.0f, 0.018f, 0.409f, 0.409f, 0.177f, 0.0f, 0.004f, 0.409f, 0.409f, 0.183f, 0.0f, -0.008f, 0.411f, 0.411f, 0.19f, 0.0f, -0.022f, 0.411f, 0.411f, + 0.196f, 0.0f, -0.034f, 0.411f, 0.411f, 0.203f, 0.0f, -0.045f, 0.411f, 0.411f, 0.211f, 0.0f, -0.055f, 0.411f, 0.411f, 0.219f, 0.0f, -0.064f, 0.411f, 0.411f, + 0.227f, 0.0f, -0.072f, 0.411f, 0.411f, 0.235f, 0.0f, -0.08f, 0.412f, 0.412f, 0.244f, 0.0f, -0.087f, 0.412f, 0.412f, 0.253f, 0.0f, -0.094f, 0.413f, 0.413f, + 0.262f, 0.0f, -0.1f, 0.413f, 0.413f, 0.273f, 0.0f, -0.105f, 0.413f, 0.413f, 0.284f, 0.0f, -0.11f, 0.413f, 0.413f, 0.295f, 0.0f, -0.114f, 0.419f, 0.419f, + 0.307f, 0.0f, -0.117f, 0.425f, 0.425f, 0.321f, -0.0f, -0.118f, 0.433f, 0.433f, 0.334f, -0.0f, -0.12f, 0.446f, 0.446f, 0.347f, -0.0f, -0.12f, 0.474f, 0.474f, + 0.36f, -0.0f, -0.12f, 0.481f, 0.481f, 0.374f, -0.0f, -0.119f, 0.491f, 0.491f, 0.387f, -0.0f, -0.118f, 0.494f, 0.494f, 0.401f, 0.0f, -0.116f, 0.5f, 0.5f, + 0.414f, 0.0f, -0.112f, 0.505f, 0.505f, 0.426f, -0.0f, -0.107f, 0.51f, 0.51f, 0.438f, -0.0f, -0.101f, 0.513f, 0.513f, 0.449f, -0.0f, -0.094f, 0.515f, 0.515f, + 0.46f, -0.0f, -0.086f, 0.517f, 0.517f, 0.47f, -0.0f, -0.078f, 0.519f, 0.519f, 0.478f, -0.0f, -0.07f, 0.52f, 0.52f, 0.486f, -0.0f, -0.061f, 0.522f, 0.522f, + 0.493f, -0.0f, -0.052f, 0.523f, 0.523f, 0.499f, -0.0f, -0.044f, 0.522f, 0.522f, 0.505f, -0.0f, -0.035f, 0.522f, 0.522f, 0.51f, -0.0f, -0.027f, 0.523f, 0.523f, + 0.514f, -0.0f, -0.018f, 0.523f, 0.523f, 0.517f, -0.0f, -0.009f, 0.523f, 0.523f, 0.52f, -0.0f, -0.001f, 0.524f, 0.524f, 0.522f, -0.0f, 0.008f, 0.523f, 0.523f, + 0.525f, -0.0f, 0.018f, 0.521f, 0.522f, 0.527f, -0.0f, 0.027f, 0.515f, 0.514f, 0.529f, -0.0f, 0.036f, 0.512f, 0.512f, 0.531f, -0.0f, 0.045f, 0.509f, 0.51f, + 0.533f, -0.0f, 0.053f, 0.506f, 0.505f, 0.535f, -0.0f, 0.062f, 0.503f, 0.503f, 0.536f, -0.0f, 0.071f, 0.5f, 0.5f, 0.538f, -0.0f, 0.08f, 0.496f, 0.497f, + 0.538f, -0.0f, 0.09f, 0.491f, 0.492f, 0.539f, -0.0f, 0.1f, 0.485f, 0.486f, 0.539f, 0.0f, 0.11f, 0.475f, 0.476f, 0.539f, 0.0f, 0.12f, 0.46f, 0.459f, + 0.539f, 0.0f, 0.13f, 0.444f, 0.448f, 0.538f, 0.0f, 0.139f, 0.406f, 0.405f, 0.537f, 0.0f, 0.144f, 0.399f, 0.399f, 0.536f, 0.0f, 0.146f, 0.395f, 0.395f, + 0.535f, 0.0f, 0.144f, 0.412f, 0.412f, 0.533f, 0.0f, 0.139f, 0.413f, 0.413f, 0.53f, 0.0f, 0.131f, 0.414f, 0.413f, 0.528f, 0.0f, 0.122f, 0.419f, 0.418f, + 0.525f, 0.0f, 0.112f, 0.425f, 0.424f, 0.521f, 0.0f, 0.102f, 0.444f, 0.444f, 0.518f, 0.0f, 0.094f, 0.451f, 0.452f, 0.514f, 0.0f, 0.085f, 0.457f, 0.457f, + 0.509f, 0.0f, 0.078f, 0.461f, 0.46f, 0.504f, 0.0f, 0.069f, 0.469f, 0.468f, 0.499f, 0.0f, 0.06f, 0.481f, 0.481f, 0.493f, 0.0f, 0.052f, 0.489f, 0.489f, + 0.487f, 0.0f, 0.044f, 0.492f, 0.492f, 0.481f, 0.0f, 0.037f, 0.501f, 0.5f, 0.474f, 0.0f, 0.029f, 0.513f, 0.513f, 0.467f, 0.0f, 0.022f, 0.521f, 0.521f, + 0.458f, 0.0f, 0.015f, 0.524f, 0.524f, 0.449f, 0.0f, 0.008f, 0.525f, 0.525f, 0.439f, 0.0f, 0.001f, 0.528f, 0.528f, 0.427f, 0.0f, -0.005f, 0.532f, 0.532f, + 0.416f, 0.0f, -0.011f, 0.533f, 0.533f, 0.401f, 0.0f, -0.015f, 0.537f, 0.537f, 0.386f, 0.0f, -0.018f, 0.539f, 0.539f, 0.371f, 0.0f, -0.02f, 0.538f, 0.538f, + 0.356f, 0.0f, -0.021f, 0.543f, 0.543f, 0.341f, 0.0f, -0.023f, 0.543f, 0.543f, 0.326f, 0.0f, -0.023f, 0.543f, 0.543f, 0.312f, 0.0f, -0.022f, 0.543f, 0.543f, + 0.298f, 0.0f, -0.018f, 0.543f, 0.543f, 0.286f, 0.0f, -0.014f, 0.543f, 0.543f, 0.273f, 0.0f, -0.006f, 0.543f, 0.543f, 0.26f, 0.0f, 0.004f, 0.543f, 0.543f, + 0.247f, 0.0f, 0.013f, 0.543f, 0.543f, 0.235f, 0.0f, 0.022f, 0.543f, 0.543f, 0.225f, 0.0f, 0.033f, 0.543f, 0.543f, 0.215f, 0.0f, 0.045f, 0.542f, 0.542f, + 0.206f, 0.0f, 0.061f, 0.54f, 0.54f, 0.199f, 0.0f, 0.078f, 0.542f, 0.542f, 0.193f, 0.0f, 0.094f, 0.542f, 0.542f, 0.189f, -0.0f, 0.109f, 0.541f, 0.541f, + 0.186f, -0.0f, 0.119f, 0.542f, 0.542f, 0.185f, -0.0f, 0.127f, 0.542f, 0.542f, 0.184f, -0.0f, 0.135f, 0.542f, 0.542f, 0.184f, -0.0f, 0.142f, 0.542f, 0.542f, + 0.183f, -0.0f, 0.149f, 0.541f, 0.541f, 0.183f, -0.0f, 0.156f, 0.538f, 0.538f, 0.183f, -0.0f, 0.163f, 0.539f, 0.539f, 0.183f, -0.0f, 0.17f, 0.54f, 0.54f, + 0.183f, 0.0f, 0.177f, 0.54f, 0.54f, 0.183f, 0.0f, 0.184f, 0.54f, 0.54f, 0.183f, 0.0f, 0.191f, 0.54f, 0.54f, 0.184f, 0.0f, 0.196f, 0.539f, 0.539f, + 0.184f, 0.0f, 0.204f, 0.518f, 0.518f, +}; + +static const float data14[45 * GP_PRIM_DATABUF_SIZE] = { + -0.096f, -0.0f, -0.305f, 0.01f, 0.01f, -0.09f, -0.0f, -0.313f, 0.121f, 0.362f, -0.086f, -0.0f, -0.318f, 0.179f, 0.368f, -0.081f, -0.0f, -0.325f, 0.234f, 0.37f, + -0.075f, -0.0f, -0.331f, 0.272f, 0.37f, -0.068f, -0.0f, -0.338f, 0.302f, 0.371f, -0.061f, -0.0f, -0.345f, 0.324f, 0.374f, -0.053f, -0.0f, -0.352f, 0.34f, 0.377f, + -0.044f, -0.0f, -0.358f, 0.352f, 0.378f, -0.035f, -0.0f, -0.362f, 0.362f, 0.377f, -0.026f, -0.0f, -0.366f, 0.37f, 0.378f, -0.018f, -0.0f, -0.368f, 0.377f, 0.378f, + -0.009f, -0.0f, -0.369f, 0.383f, 0.376f, -0.001f, -0.0f, -0.369f, 0.389f, 0.369f, 0.007f, -0.0f, -0.368f, 0.395f, 0.364f, 0.015f, -0.0f, -0.367f, 0.4f, 0.388f, + 0.023f, -0.0f, -0.365f, 0.405f, 0.41f, 0.03f, -0.0f, -0.363f, 0.41f, 0.429f, 0.038f, -0.0f, -0.36f, 0.414f, 0.438f, 0.044f, -0.0f, -0.357f, 0.417f, 0.441f, + 0.05f, -0.0f, -0.355f, 0.419f, 0.444f, 0.055f, -0.0f, -0.352f, 0.42f, 0.441f, 0.06f, -0.0f, -0.349f, 0.421f, 0.445f, 0.063f, -0.0f, -0.347f, 0.421f, 0.446f, + 0.065f, -0.0f, -0.344f, 0.42f, 0.443f, 0.065f, -0.0f, -0.342f, 0.42f, 0.437f, 0.065f, -0.0f, -0.341f, 0.419f, 0.413f, 0.063f, -0.0f, -0.339f, 0.418f, 0.404f, + 0.061f, -0.0f, -0.338f, 0.418f, 0.403f, 0.057f, -0.0f, -0.337f, 0.418f, 0.402f, 0.052f, -0.0f, -0.337f, 0.418f, 0.407f, 0.046f, -0.0f, -0.337f, 0.419f, 0.411f, + 0.04f, 0.0f, -0.336f, 0.42f, 0.416f, 0.032f, 0.0f, -0.337f, 0.422f, 0.421f, 0.023f, 0.0f, -0.339f, 0.424f, 0.425f, 0.014f, 0.0f, -0.34f, 0.426f, 0.427f, + 0.003f, 0.0f, -0.341f, 0.428f, 0.427f, -0.007f, 0.0f, -0.341f, 0.43f, 0.433f, -0.018f, 0.0f, -0.339f, 0.432f, 0.437f, -0.027f, 0.0f, -0.335f, 0.434f, 0.438f, + -0.037f, 0.0f, -0.33f, 0.435f, 0.437f, -0.046f, -0.0f, -0.326f, 0.436f, 0.438f, -0.055f, -0.0f, -0.321f, 0.436f, 0.44f, -0.062f, -0.0f, -0.316f, 0.437f, 0.439f, + -0.073f, -0.0f, -0.31f, 0.437f, 0.437f, +}; + +static const float data16[84 * GP_PRIM_DATABUF_SIZE] = { + 0.737f, 0.0f, 0.177f, 0.148f, 0.148f, 0.735f, 0.0f, 0.164f, 0.214f, 0.39f, 0.734f, 0.0f, 0.155f, 0.254f, 0.402f, 0.732f, 0.0f, 0.143f, 0.295f, 0.413f, + 0.73f, 0.0f, 0.132f, 0.328f, 0.415f, 0.728f, 0.0f, 0.121f, 0.355f, 0.415f, 0.726f, 0.0f, 0.109f, 0.375f, 0.416f, 0.724f, 0.0f, 0.097f, 0.39f, 0.417f, + 0.721f, 0.0f, 0.086f, 0.401f, 0.418f, 0.719f, 0.0f, 0.074f, 0.408f, 0.419f, 0.716f, 0.0f, 0.062f, 0.413f, 0.42f, 0.713f, 0.0f, 0.05f, 0.416f, 0.42f, + 0.71f, 0.0f, 0.039f, 0.418f, 0.421f, 0.707f, 0.0f, 0.028f, 0.42f, 0.421f, 0.703f, 0.0f, 0.017f, 0.421f, 0.422f, 0.7f, 0.0f, 0.006f, 0.421f, 0.422f, + 0.696f, 0.0f, -0.005f, 0.422f, 0.422f, 0.693f, 0.0f, -0.015f, 0.422f, 0.422f, 0.689f, 0.0f, -0.025f, 0.423f, 0.423f, 0.685f, 0.0f, -0.034f, 0.423f, 0.423f, + 0.681f, 0.0f, -0.044f, 0.423f, 0.423f, 0.677f, 0.0f, -0.053f, 0.423f, 0.423f, 0.672f, 0.0f, -0.062f, 0.423f, 0.423f, 0.668f, 0.0f, -0.071f, 0.422f, 0.424f, + 0.662f, 0.0f, -0.08f, 0.422f, 0.424f, 0.657f, 0.0f, -0.088f, 0.422f, 0.422f, 0.651f, 0.0f, -0.095f, 0.421f, 0.419f, 0.645f, 0.0f, -0.103f, 0.42f, 0.419f, + 0.638f, 0.0f, -0.109f, 0.42f, 0.419f, 0.631f, 0.0f, -0.115f, 0.419f, 0.419f, 0.624f, 0.0f, -0.12f, 0.419f, 0.419f, 0.617f, 0.0f, -0.125f, 0.419f, 0.419f, + 0.61f, 0.0f, -0.129f, 0.418f, 0.418f, 0.602f, 0.0f, -0.133f, 0.418f, 0.416f, 0.594f, 0.0f, -0.137f, 0.417f, 0.416f, 0.587f, 0.0f, -0.14f, 0.417f, 0.415f, + 0.579f, 0.0f, -0.142f, 0.417f, 0.416f, 0.571f, 0.0f, -0.144f, 0.417f, 0.415f, 0.564f, 0.0f, -0.145f, 0.417f, 0.416f, 0.556f, 0.0f, -0.146f, 0.417f, 0.415f, + 0.549f, 0.0f, -0.146f, 0.417f, 0.415f, 0.541f, 0.0f, -0.146f, 0.417f, 0.415f, 0.535f, 0.0f, -0.145f, 0.417f, 0.416f, 0.53f, 0.0f, -0.143f, 0.418f, 0.418f, + 0.526f, 0.0f, -0.14f, 0.418f, 0.418f, 0.524f, 0.0f, -0.136f, 0.42f, 0.418f, 0.524f, 0.0f, -0.132f, 0.422f, 0.416f, 0.527f, 0.0f, -0.126f, 0.424f, 0.424f, + 0.531f, 0.0f, -0.121f, 0.427f, 0.428f, 0.536f, 0.0f, -0.115f, 0.43f, 0.433f, 0.542f, 0.0f, -0.109f, 0.433f, 0.436f, 0.548f, 0.0f, -0.102f, 0.435f, 0.436f, + 0.555f, 0.0f, -0.095f, 0.436f, 0.437f, 0.562f, 0.0f, -0.088f, 0.437f, 0.438f, 0.568f, 0.0f, -0.081f, 0.437f, 0.438f, 0.575f, 0.0f, -0.073f, 0.438f, 0.438f, + 0.581f, 0.0f, -0.065f, 0.438f, 0.438f, 0.587f, 0.0f, -0.058f, 0.438f, 0.438f, 0.593f, 0.0f, -0.05f, 0.438f, 0.438f, 0.599f, 0.0f, -0.041f, 0.438f, 0.438f, + 0.605f, 0.0f, -0.033f, 0.438f, 0.438f, 0.61f, 0.0f, -0.024f, 0.438f, 0.438f, 0.615f, 0.0f, -0.015f, 0.438f, 0.438f, 0.621f, 0.0f, -0.006f, 0.438f, 0.438f, + 0.626f, 0.0f, 0.004f, 0.438f, 0.438f, 0.631f, 0.0f, 0.013f, 0.437f, 0.438f, 0.636f, 0.0f, 0.023f, 0.436f, 0.438f, 0.641f, 0.0f, 0.032f, 0.434f, 0.438f, + 0.647f, 0.0f, 0.042f, 0.432f, 0.437f, 0.652f, 0.0f, 0.051f, 0.431f, 0.429f, 0.657f, 0.0f, 0.06f, 0.429f, 0.426f, 0.662f, 0.0f, 0.069f, 0.427f, 0.425f, + 0.668f, 0.0f, 0.078f, 0.425f, 0.425f, 0.673f, 0.0f, 0.087f, 0.423f, 0.424f, 0.678f, 0.0f, 0.095f, 0.42f, 0.422f, 0.683f, 0.0f, 0.104f, 0.416f, 0.42f, + 0.688f, 0.0f, 0.112f, 0.411f, 0.421f, 0.693f, 0.0f, 0.12f, 0.403f, 0.417f, 0.698f, 0.0f, 0.128f, 0.394f, 0.411f, 0.702f, 0.0f, 0.135f, 0.382f, 0.404f, + 0.707f, 0.0f, 0.143f, 0.369f, 0.388f, 0.711f, 0.0f, 0.15f, 0.352f, 0.371f, 0.714f, 0.0f, 0.155f, 0.338f, 0.352f, 0.719f, 0.0f, 0.164f, 0.315f, 0.315f, +}; + +static const float data15[44 * GP_PRIM_DATABUF_SIZE] = { + -0.085f, 0.0f, -0.816f, 0.138f, 0.138f, -0.079f, 0.0f, -0.825f, 0.246f, 0.309f, -0.074f, 0.0f, -0.832f, 0.302f, 0.34f, -0.067f, 0.0f, -0.84f, 0.335f, 0.352f, + -0.059f, 0.0f, -0.848f, 0.357f, 0.374f, -0.05f, 0.0f, -0.855f, 0.371f, 0.378f, -0.041f, 0.0f, -0.861f, 0.382f, 0.383f, -0.031f, 0.0f, -0.866f, 0.391f, 0.396f, + -0.021f, 0.0f, -0.871f, 0.398f, 0.401f, -0.011f, 0.0f, -0.874f, 0.404f, 0.407f, -0.001f, 0.0f, -0.877f, 0.409f, 0.411f, 0.01f, 0.0f, -0.878f, 0.415f, 0.412f, + 0.02f, 0.0f, -0.878f, 0.422f, 0.417f, 0.031f, 0.0f, -0.878f, 0.43f, 0.421f, 0.042f, 0.0f, -0.876f, 0.438f, 0.437f, 0.052f, 0.0f, -0.873f, 0.445f, 0.451f, + 0.062f, 0.0f, -0.868f, 0.451f, 0.459f, 0.071f, 0.0f, -0.863f, 0.456f, 0.463f, 0.08f, 0.0f, -0.857f, 0.46f, 0.465f, 0.087f, 0.0f, -0.85f, 0.462f, 0.465f, + 0.094f, 0.0f, -0.842f, 0.461f, 0.465f, 0.098f, 0.0f, -0.835f, 0.458f, 0.467f, 0.101f, 0.0f, -0.827f, 0.451f, 0.457f, 0.103f, 0.0f, -0.82f, 0.436f, 0.451f, + 0.102f, 0.0f, -0.815f, 0.422f, 0.418f, 0.1f, 0.0f, -0.811f, 0.419f, 0.378f, 0.096f, 0.0f, -0.814f, 0.436f, 0.447f, 0.089f, 0.0f, -0.817f, 0.454f, 0.465f, + 0.082f, 0.0f, -0.821f, 0.465f, 0.47f, 0.072f, 0.0f, -0.825f, 0.473f, 0.477f, 0.061f, 0.0f, -0.828f, 0.477f, 0.479f, 0.049f, 0.0f, -0.832f, 0.48f, 0.485f, + 0.036f, 0.0f, -0.834f, 0.483f, 0.48f, 0.023f, 0.0f, -0.836f, 0.484f, 0.485f, 0.01f, 0.0f, -0.838f, 0.486f, 0.487f, -0.003f, 0.0f, -0.84f, 0.486f, 0.488f, + -0.016f, 0.0f, -0.84f, 0.486f, 0.489f, -0.027f, 0.0f, -0.84f, 0.485f, 0.485f, -0.039f, 0.0f, -0.839f, 0.484f, 0.484f, -0.049f, 0.0f, -0.837f, 0.483f, 0.485f, + -0.058f, 0.0f, -0.834f, 0.48f, 0.481f, -0.066f, 0.0f, -0.83f, 0.473f, 0.479f, -0.072f, 0.0f, -0.827f, 0.462f, 0.472f, -0.081f, 0.0f, -0.823f, 0.442f, 0.442f, +}; + +static const float data17[56 * GP_PRIM_DATABUF_SIZE] = { + -1.007f, -0.0f, 0.183f, 0.022f, 0.022f, -1.003f, -0.0f, 0.181f, 0.192f, 0.436f, -0.998f, -0.0f, 0.18f, 0.28f, 0.451f, -0.99f, -0.0f, 0.178f, 0.355f, 0.459f, + -0.98f, -0.0f, 0.175f, 0.402f, 0.464f, -0.967f, -0.0f, 0.169f, 0.432f, 0.467f, -0.952f, -0.0f, 0.152f, 0.449f, 0.468f, -0.943f, 0.0f, 0.138f, 0.459f, 0.469f, + -0.939f, 0.0f, 0.128f, 0.464f, 0.469f, -0.934f, 0.0f, 0.119f, 0.467f, 0.47f, -0.929f, 0.0f, 0.11f, 0.469f, 0.47f, -0.924f, 0.0f, 0.101f, 0.47f, 0.47f, + -0.919f, 0.0f, 0.092f, 0.47f, 0.471f, -0.913f, 0.0f, 0.082f, 0.471f, 0.471f, -0.908f, 0.0f, 0.072f, 0.471f, 0.471f, -0.903f, 0.0f, 0.063f, 0.472f, 0.472f, + -0.897f, 0.0f, 0.053f, 0.472f, 0.472f, -0.892f, 0.0f, 0.044f, 0.473f, 0.473f, -0.886f, 0.0f, 0.035f, 0.473f, 0.473f, -0.881f, 0.0f, 0.026f, 0.473f, 0.473f, + -0.876f, 0.0f, 0.018f, 0.473f, 0.473f, -0.87f, 0.0f, 0.012f, 0.472f, 0.473f, -0.865f, 0.0f, 0.006f, 0.47f, 0.473f, -0.86f, 0.0f, 0.003f, 0.468f, 0.473f, + -0.855f, 0.0f, 0.001f, 0.466f, 0.469f, -0.85f, 0.0f, 0.001f, 0.463f, 0.469f, -0.846f, 0.0f, 0.003f, 0.46f, 0.45f, -0.843f, 0.0f, 0.008f, 0.458f, 0.454f, + -0.84f, 0.0f, 0.014f, 0.456f, 0.454f, -0.838f, 0.0f, 0.021f, 0.455f, 0.454f, -0.836f, 0.0f, 0.03f, 0.453f, 0.455f, -0.835f, 0.0f, 0.039f, 0.451f, 0.455f, + -0.835f, 0.0f, 0.049f, 0.449f, 0.453f, -0.836f, 0.0f, 0.059f, 0.447f, 0.445f, -0.837f, 0.0f, 0.068f, 0.445f, 0.441f, -0.84f, 0.0f, 0.078f, 0.443f, 0.44f, + -0.843f, 0.0f, 0.087f, 0.442f, 0.44f, -0.846f, 0.0f, 0.095f, 0.442f, 0.44f, -0.851f, -0.0f, 0.103f, 0.441f, 0.441f, -0.855f, -0.0f, 0.111f, 0.441f, 0.44f, + -0.86f, -0.0f, 0.119f, 0.441f, 0.441f, -0.865f, -0.0f, 0.127f, 0.441f, 0.441f, -0.871f, -0.0f, 0.134f, 0.441f, 0.441f, -0.877f, -0.0f, 0.141f, 0.441f, 0.441f, + -0.883f, -0.0f, 0.149f, 0.441f, 0.442f, -0.889f, -0.0f, 0.156f, 0.441f, 0.441f, -0.896f, -0.0f, 0.163f, 0.441f, 0.442f, -0.904f, -0.0f, 0.169f, 0.442f, 0.441f, + -0.913f, -0.0f, 0.176f, 0.442f, 0.441f, -0.925f, -0.0f, 0.183f, 0.443f, 0.441f, -0.941f, -0.0f, 0.19f, 0.444f, 0.442f, -0.956f, -0.0f, 0.195f, 0.446f, 0.443f, + -0.971f, -0.0f, 0.198f, 0.448f, 0.443f, -0.983f, -0.0f, 0.198f, 0.451f, 0.452f, -0.992f, -0.0f, 0.198f, 0.454f, 0.456f, -1.001f, 0.0f, 0.196f, 0.457f, 0.457f, +}; + +static const float data18[59 * GP_PRIM_DATABUF_SIZE] = { + 0.782f, 0.0f, 0.099f, 0.04f, 0.04f, 0.779f, 0.0f, 0.088f, 0.108f, 0.34f, 0.777f, 0.0f, 0.08f, 0.149f, 0.35f, 0.774f, 0.0f, 0.071f, 0.194f, 0.352f, + 0.772f, 0.0f, 0.062f, 0.231f, 0.352f, 0.771f, 0.0f, 0.053f, 0.263f, 0.353f, 0.769f, 0.0f, 0.044f, 0.289f, 0.353f, 0.768f, 0.0f, 0.036f, 0.31f, 0.353f, + 0.767f, 0.0f, 0.029f, 0.327f, 0.353f, 0.767f, 0.0f, 0.023f, 0.341f, 0.353f, 0.767f, 0.0f, 0.017f, 0.353f, 0.353f, 0.768f, 0.0f, 0.013f, 0.363f, 0.353f, + 0.769f, 0.0f, 0.01f, 0.373f, 0.353f, 0.771f, 0.0f, 0.009f, 0.382f, 0.351f, 0.773f, 0.0f, 0.008f, 0.39f, 0.393f, 0.776f, 0.0f, 0.009f, 0.399f, 0.41f, + 0.779f, 0.0f, 0.011f, 0.407f, 0.425f, 0.783f, 0.0f, 0.015f, 0.415f, 0.434f, 0.787f, 0.0f, 0.019f, 0.423f, 0.44f, 0.792f, 0.0f, 0.024f, 0.429f, 0.441f, + 0.797f, 0.0f, 0.03f, 0.435f, 0.444f, 0.802f, 0.0f, 0.037f, 0.441f, 0.447f, 0.807f, 0.0f, 0.044f, 0.445f, 0.453f, 0.813f, 0.0f, 0.051f, 0.449f, 0.457f, + 0.819f, 0.0f, 0.058f, 0.452f, 0.458f, 0.825f, 0.0f, 0.066f, 0.455f, 0.46f, 0.831f, 0.0f, 0.074f, 0.457f, 0.462f, 0.838f, 0.0f, 0.082f, 0.459f, 0.462f, + 0.845f, 0.0f, 0.09f, 0.461f, 0.462f, 0.852f, 0.0f, 0.098f, 0.462f, 0.463f, 0.859f, 0.0f, 0.106f, 0.463f, 0.464f, 0.867f, 0.0f, 0.113f, 0.464f, 0.464f, + 0.874f, 0.0f, 0.121f, 0.465f, 0.465f, 0.882f, 0.0f, 0.129f, 0.465f, 0.465f, 0.889f, 0.0f, 0.136f, 0.466f, 0.466f, 0.897f, 0.0f, 0.143f, 0.466f, 0.467f, + 0.904f, 0.0f, 0.15f, 0.467f, 0.466f, 0.911f, 0.0f, 0.157f, 0.467f, 0.467f, 0.916f, 0.0f, 0.163f, 0.468f, 0.468f, 0.921f, 0.0f, 0.169f, 0.468f, 0.469f, + 0.924f, 0.0f, 0.173f, 0.468f, 0.469f, 0.926f, 0.0f, 0.177f, 0.469f, 0.468f, 0.925f, 0.0f, 0.18f, 0.469f, 0.468f, 0.922f, 0.0f, 0.181f, 0.469f, 0.469f, + 0.918f, 0.0f, 0.181f, 0.469f, 0.469f, 0.912f, 0.0f, 0.18f, 0.469f, 0.469f, 0.905f, 0.0f, 0.178f, 0.468f, 0.47f, 0.898f, 0.0f, 0.175f, 0.466f, 0.471f, + 0.89f, 0.0f, 0.172f, 0.462f, 0.469f, 0.882f, 0.0f, 0.168f, 0.454f, 0.468f, 0.874f, 0.0f, 0.164f, 0.442f, 0.467f, 0.866f, 0.0f, 0.159f, 0.423f, 0.467f, + 0.858f, 0.0f, 0.154f, 0.398f, 0.468f, 0.851f, 0.0f, 0.149f, 0.366f, 0.468f, 0.844f, 0.0f, 0.144f, 0.326f, 0.469f, 0.837f, 0.0f, 0.139f, 0.282f, 0.469f, + 0.83f, 0.0f, 0.134f, 0.231f, 0.467f, 0.824f, 0.0f, 0.13f, 0.184f, 0.415f, 0.816f, 0.0f, 0.124f, 0.111f, 0.111f, +}; + +static const float data19[100 * GP_PRIM_DATABUF_SIZE] = { + -0.279f, 0.0f, 0.568f, 0.154f, 0.154f, -0.266f, 0.0f, 0.569f, 0.249f, 0.318f, -0.258f, 0.0f, 0.57f, 0.296f, 0.357f, -0.248f, 0.0f, 0.571f, 0.337f, 0.383f, + -0.238f, 0.0f, 0.571f, 0.363f, 0.396f, -0.229f, 0.0f, 0.571f, 0.381f, 0.403f, -0.219f, 0.0f, 0.57f, 0.392f, 0.407f, -0.209f, 0.0f, 0.568f, 0.399f, 0.407f, + -0.2f, 0.0f, 0.566f, 0.403f, 0.408f, -0.19f, 0.0f, 0.563f, 0.406f, 0.41f, -0.181f, 0.0f, 0.559f, 0.407f, 0.41f, -0.171f, 0.0f, 0.555f, 0.409f, 0.41f, + -0.161f, 0.0f, 0.551f, 0.409f, 0.411f, -0.152f, 0.0f, 0.546f, 0.41f, 0.411f, -0.142f, 0.0f, 0.542f, 0.41f, 0.412f, -0.132f, 0.0f, 0.537f, 0.411f, 0.411f, + -0.122f, 0.0f, 0.533f, 0.411f, 0.411f, -0.112f, 0.0f, 0.528f, 0.411f, 0.412f, -0.102f, 0.0f, 0.524f, 0.411f, 0.412f, -0.092f, 0.0f, 0.519f, 0.41f, 0.412f, + -0.081f, 0.0f, 0.515f, 0.407f, 0.411f, -0.071f, 0.0f, 0.511f, 0.403f, 0.408f, -0.061f, 0.0f, 0.507f, 0.399f, 0.401f, -0.051f, 0.0f, 0.503f, 0.394f, 0.395f, + -0.041f, 0.0f, 0.499f, 0.39f, 0.388f, -0.031f, 0.0f, 0.495f, 0.386f, 0.383f, -0.021f, 0.0f, 0.491f, 0.383f, 0.38f, -0.011f, 0.0f, 0.488f, 0.381f, 0.378f, + -0.001f, 0.0f, 0.486f, 0.379f, 0.377f, 0.009f, 0.0f, 0.484f, 0.378f, 0.377f, 0.019f, 0.0f, 0.483f, 0.377f, 0.375f, 0.03f, 0.0f, 0.482f, 0.377f, 0.375f, + 0.041f, 0.0f, 0.482f, 0.378f, 0.376f, 0.051f, 0.0f, 0.483f, 0.379f, 0.376f, 0.062f, 0.0f, 0.484f, 0.381f, 0.376f, 0.073f, 0.0f, 0.486f, 0.385f, 0.379f, + 0.085f, 0.0f, 0.488f, 0.389f, 0.382f, 0.096f, 0.0f, 0.491f, 0.395f, 0.392f, 0.108f, 0.0f, 0.494f, 0.402f, 0.4f, 0.12f, 0.0f, 0.497f, 0.409f, 0.409f, + 0.132f, 0.0f, 0.501f, 0.415f, 0.416f, 0.144f, 0.0f, 0.505f, 0.421f, 0.427f, 0.157f, 0.0f, 0.509f, 0.425f, 0.43f, 0.17f, 0.0f, 0.513f, 0.429f, 0.433f, + 0.181f, 0.0f, 0.517f, 0.431f, 0.433f, 0.192f, 0.0f, 0.52f, 0.433f, 0.434f, 0.201f, 0.0f, 0.522f, 0.433f, 0.435f, 0.208f, 0.0f, 0.524f, 0.433f, 0.435f, + 0.213f, 0.0f, 0.524f, 0.432f, 0.436f, 0.216f, 0.0f, 0.523f, 0.431f, 0.435f, 0.217f, 0.0f, 0.521f, 0.43f, 0.426f, 0.215f, 0.0f, 0.518f, 0.429f, 0.427f, + 0.213f, 0.0f, 0.515f, 0.428f, 0.427f, 0.208f, 0.0f, 0.511f, 0.428f, 0.427f, 0.203f, 0.0f, 0.506f, 0.428f, 0.427f, 0.196f, 0.0f, 0.502f, 0.428f, 0.427f, + 0.189f, 0.0f, 0.497f, 0.428f, 0.427f, 0.181f, 0.0f, 0.492f, 0.428f, 0.427f, 0.173f, 0.0f, 0.487f, 0.428f, 0.428f, 0.163f, 0.0f, 0.482f, 0.429f, 0.428f, + 0.154f, 0.0f, 0.477f, 0.429f, 0.429f, 0.145f, 0.0f, 0.472f, 0.43f, 0.43f, 0.135f, 0.0f, 0.467f, 0.431f, 0.431f, 0.125f, 0.0f, 0.462f, 0.432f, 0.43f, + 0.116f, 0.0f, 0.457f, 0.433f, 0.431f, 0.106f, 0.0f, 0.453f, 0.435f, 0.434f, 0.096f, 0.0f, 0.448f, 0.436f, 0.436f, 0.086f, 0.0f, 0.444f, 0.437f, 0.438f, + 0.076f, 0.0f, 0.44f, 0.438f, 0.44f, 0.065f, 0.0f, 0.436f, 0.439f, 0.441f, 0.055f, 0.0f, 0.433f, 0.44f, 0.441f, 0.044f, 0.0f, 0.431f, 0.441f, 0.442f, + 0.033f, 0.0f, 0.429f, 0.441f, 0.442f, 0.022f, 0.0f, 0.427f, 0.441f, 0.442f, 0.011f, 0.0f, 0.426f, 0.442f, 0.443f, -0.0f, 0.0f, 0.426f, 0.442f, 0.442f, + -0.011f, 0.0f, 0.426f, 0.442f, 0.442f, -0.022f, 0.0f, 0.427f, 0.442f, 0.442f, -0.033f, 0.0f, 0.429f, 0.442f, 0.442f, -0.042f, 0.0f, 0.432f, 0.441f, 0.442f, + -0.052f, 0.0f, 0.435f, 0.441f, 0.441f, -0.061f, 0.0f, 0.439f, 0.441f, 0.441f, -0.07f, 0.0f, 0.443f, 0.441f, 0.441f, -0.078f, 0.0f, 0.448f, 0.441f, 0.441f, + -0.087f, 0.0f, 0.453f, 0.441f, 0.442f, -0.095f, 0.0f, 0.458f, 0.441f, 0.441f, -0.104f, 0.0f, 0.463f, 0.44f, 0.44f, -0.113f, 0.0f, 0.468f, 0.44f, 0.44f, + -0.122f, 0.0f, 0.473f, 0.44f, 0.44f, -0.132f, 0.0f, 0.479f, 0.44f, 0.44f, -0.143f, 0.0f, 0.485f, 0.44f, 0.44f, -0.154f, 0.0f, 0.491f, 0.44f, 0.44f, + -0.165f, 0.0f, 0.498f, 0.44f, 0.44f, -0.176f, 0.0f, 0.504f, 0.439f, 0.439f, -0.187f, 0.0f, 0.51f, 0.435f, 0.44f, -0.198f, 0.0f, 0.516f, 0.424f, 0.44f, + -0.209f, 0.0f, 0.522f, 0.393f, 0.44f, -0.219f, 0.0f, 0.527f, 0.324f, 0.44f, -0.228f, 0.0f, 0.532f, 0.222f, 0.404f, -0.241f, 0.0f, 0.538f, 0.037f, 0.037f, +}; + +static const float data20[136 * GP_PRIM_DATABUF_SIZE] = { + 0.331f, 0.0f, -0.036f, 0.065f, 0.065f, 0.322f, 0.0f, -0.034f, 0.239f, 0.293f, 0.317f, 0.0f, -0.032f, 0.316f, 0.339f, 0.31f, 0.0f, -0.029f, 0.348f, 0.355f, + 0.302f, 0.0f, -0.027f, 0.364f, 0.368f, 0.295f, 0.0f, -0.023f, 0.373f, 0.374f, 0.287f, 0.0f, -0.02f, 0.381f, 0.381f, 0.279f, 0.0f, -0.015f, 0.388f, 0.391f, + 0.271f, 0.0f, -0.01f, 0.392f, 0.394f, 0.263f, 0.0f, -0.004f, 0.395f, 0.396f, 0.255f, 0.0f, 0.002f, 0.397f, 0.397f, 0.247f, 0.0f, 0.008f, 0.399f, 0.4f, + 0.239f, 0.0f, 0.016f, 0.401f, 0.401f, 0.232f, -0.0f, 0.024f, 0.404f, 0.404f, 0.226f, 0.0f, 0.031f, 0.406f, 0.407f, 0.221f, 0.0f, 0.038f, 0.409f, 0.409f, + 0.215f, 0.0f, 0.045f, 0.412f, 0.412f, 0.21f, 0.0f, 0.054f, 0.415f, 0.415f, 0.205f, 0.0f, 0.063f, 0.417f, 0.417f, 0.201f, 0.0f, 0.073f, 0.42f, 0.421f, + 0.197f, 0.0f, 0.083f, 0.421f, 0.421f, 0.193f, -0.0f, 0.094f, 0.423f, 0.423f, 0.19f, -0.0f, 0.104f, 0.424f, 0.424f, 0.187f, -0.0f, 0.114f, 0.424f, 0.425f, + 0.185f, -0.0f, 0.125f, 0.425f, 0.425f, 0.183f, -0.0f, 0.135f, 0.425f, 0.425f, 0.182f, -0.0f, 0.146f, 0.426f, 0.425f, 0.181f, -0.0f, 0.157f, 0.426f, 0.425f, + 0.18f, -0.0f, 0.168f, 0.426f, 0.426f, 0.18f, -0.0f, 0.179f, 0.427f, 0.427f, 0.181f, -0.0f, 0.189f, 0.427f, 0.427f, 0.182f, -0.0f, 0.199f, 0.427f, 0.427f, + 0.183f, -0.0f, 0.208f, 0.427f, 0.428f, 0.185f, -0.0f, 0.218f, 0.428f, 0.427f, 0.187f, -0.0f, 0.226f, 0.428f, 0.427f, 0.19f, -0.0f, 0.235f, 0.429f, 0.427f, + 0.192f, -0.0f, 0.243f, 0.43f, 0.428f, 0.196f, -0.0f, 0.252f, 0.431f, 0.431f, 0.199f, -0.0f, 0.26f, 0.431f, 0.432f, 0.203f, -0.0f, 0.268f, 0.432f, 0.433f, + 0.207f, -0.0f, 0.276f, 0.433f, 0.433f, 0.212f, -0.0f, 0.283f, 0.434f, 0.434f, 0.216f, -0.0f, 0.291f, 0.434f, 0.435f, 0.221f, -0.0f, 0.298f, 0.435f, 0.436f, + 0.227f, -0.0f, 0.305f, 0.435f, 0.435f, 0.232f, -0.0f, 0.311f, 0.436f, 0.436f, 0.238f, -0.0f, 0.317f, 0.436f, 0.436f, 0.243f, -0.0f, 0.323f, 0.436f, 0.436f, + 0.249f, -0.0f, 0.329f, 0.437f, 0.436f, 0.255f, -0.0f, 0.334f, 0.438f, 0.437f, 0.262f, -0.0f, 0.339f, 0.44f, 0.437f, 0.268f, -0.0f, 0.344f, 0.442f, 0.441f, + 0.274f, 0.0f, 0.348f, 0.444f, 0.446f, 0.281f, 0.0f, 0.352f, 0.445f, 0.447f, 0.287f, 0.0f, 0.355f, 0.446f, 0.447f, 0.293f, 0.0f, 0.358f, 0.446f, 0.447f, + 0.299f, 0.0f, 0.361f, 0.447f, 0.447f, 0.306f, 0.0f, 0.363f, 0.447f, 0.448f, 0.312f, 0.0f, 0.366f, 0.447f, 0.448f, 0.318f, 0.0f, 0.368f, 0.448f, 0.448f, + 0.325f, 0.0f, 0.369f, 0.448f, 0.448f, 0.331f, 0.0f, 0.371f, 0.448f, 0.448f, 0.338f, 0.0f, 0.372f, 0.448f, 0.448f, 0.345f, 0.0f, 0.372f, 0.448f, 0.448f, + 0.352f, 0.0f, 0.372f, 0.448f, 0.448f, 0.359f, 0.0f, 0.372f, 0.448f, 0.449f, 0.366f, 0.0f, 0.371f, 0.448f, 0.448f, 0.373f, 0.0f, 0.37f, 0.448f, 0.449f, + 0.38f, 0.0f, 0.369f, 0.449f, 0.449f, 0.387f, 0.0f, 0.367f, 0.449f, 0.449f, 0.393f, 0.0f, 0.365f, 0.449f, 0.449f, 0.4f, 0.0f, 0.363f, 0.449f, 0.45f, + 0.406f, 0.0f, 0.36f, 0.45f, 0.45f, 0.412f, -0.0f, 0.357f, 0.45f, 0.45f, 0.418f, -0.0f, 0.354f, 0.45f, 0.451f, 0.424f, -0.0f, 0.351f, 0.45f, 0.451f, + 0.43f, -0.0f, 0.347f, 0.45f, 0.451f, 0.436f, -0.0f, 0.343f, 0.45f, 0.451f, 0.443f, -0.0f, 0.339f, 0.45f, 0.45f, 0.449f, -0.0f, 0.334f, 0.45f, 0.451f, + 0.455f, -0.0f, 0.329f, 0.451f, 0.451f, 0.46f, -0.0f, 0.323f, 0.451f, 0.451f, 0.466f, -0.0f, 0.318f, 0.451f, 0.451f, 0.472f, -0.0f, 0.311f, 0.452f, 0.452f, + 0.477f, -0.0f, 0.305f, 0.452f, 0.453f, 0.482f, -0.0f, 0.298f, 0.452f, 0.453f, 0.487f, -0.0f, 0.291f, 0.453f, 0.453f, 0.492f, -0.0f, 0.284f, 0.453f, 0.453f, + 0.496f, -0.0f, 0.277f, 0.453f, 0.453f, 0.5f, -0.0f, 0.269f, 0.453f, 0.454f, 0.504f, -0.0f, 0.261f, 0.453f, 0.454f, 0.508f, -0.0f, 0.252f, 0.454f, 0.454f, + 0.511f, -0.0f, 0.244f, 0.454f, 0.454f, 0.514f, -0.0f, 0.235f, 0.454f, 0.455f, 0.517f, -0.0f, 0.225f, 0.454f, 0.455f, 0.519f, -0.0f, 0.216f, 0.454f, 0.455f, + 0.521f, -0.0f, 0.205f, 0.455f, 0.455f, 0.523f, -0.0f, 0.194f, 0.455f, 0.455f, 0.524f, -0.0f, 0.182f, 0.455f, 0.455f, 0.524f, -0.0f, 0.169f, 0.455f, 0.456f, + 0.524f, -0.0f, 0.157f, 0.455f, 0.456f, 0.523f, -0.0f, 0.145f, 0.455f, 0.456f, 0.522f, -0.0f, 0.133f, 0.455f, 0.456f, 0.52f, -0.0f, 0.122f, 0.456f, 0.456f, + 0.518f, -0.0f, 0.11f, 0.456f, 0.456f, 0.515f, -0.0f, 0.1f, 0.456f, 0.456f, 0.513f, -0.0f, 0.09f, 0.456f, 0.457f, 0.509f, -0.0f, 0.081f, 0.456f, 0.457f, + 0.506f, -0.0f, 0.072f, 0.457f, 0.457f, 0.502f, -0.0f, 0.064f, 0.457f, 0.457f, 0.498f, -0.0f, 0.056f, 0.457f, 0.457f, 0.494f, -0.0f, 0.049f, 0.457f, 0.457f, + 0.49f, -0.0f, 0.041f, 0.458f, 0.457f, 0.485f, -0.0f, 0.034f, 0.458f, 0.457f, 0.48f, -0.0f, 0.028f, 0.458f, 0.458f, 0.475f, -0.0f, 0.022f, 0.458f, 0.458f, + 0.47f, -0.0f, 0.016f, 0.458f, 0.458f, 0.465f, -0.0f, 0.011f, 0.459f, 0.458f, 0.46f, -0.0f, 0.006f, 0.459f, 0.458f, 0.454f, -0.0f, 0.001f, 0.46f, 0.459f, + 0.449f, 0.0f, -0.003f, 0.464f, 0.463f, 0.443f, 0.0f, -0.007f, 0.467f, 0.468f, 0.438f, 0.0f, -0.011f, 0.469f, 0.469f, 0.432f, 0.0f, -0.015f, 0.471f, 0.47f, + 0.426f, 0.0f, -0.018f, 0.477f, 0.478f, 0.42f, 0.0f, -0.021f, 0.478f, 0.478f, 0.414f, 0.0f, -0.024f, 0.478f, 0.478f, 0.408f, 0.0f, -0.027f, 0.479f, 0.479f, + 0.402f, 0.0f, -0.029f, 0.48f, 0.48f, 0.395f, 0.0f, -0.031f, 0.48f, 0.48f, 0.389f, 0.0f, -0.033f, 0.482f, 0.482f, 0.382f, 0.0f, -0.035f, 0.482f, 0.482f, + 0.376f, 0.0f, -0.036f, 0.482f, 0.482f, 0.369f, 0.0f, -0.037f, 0.48f, 0.482f, 0.364f, 0.0f, -0.037f, 0.457f, 0.485f, 0.356f, 0.0f, -0.038f, 0.32f, 0.32f, +}; + +static const float data21[353 * GP_PRIM_DATABUF_SIZE] = { + -0.382f, 0.0f, 0.397f, 0.0f, 1.0f, -0.386f, 0.0f, 0.394f, 0.0f, 1.0f, -0.389f, 0.0f, 0.392f, 0.0f, 1.0f, -0.392f, 0.0f, 0.39f, 0.0f, 1.0f, + -0.395f, 0.0f, 0.388f, 0.0f, 1.0f, -0.399f, 0.0f, 0.385f, 0.0f, 1.0f, -0.402f, 0.0f, 0.383f, 0.0f, 1.0f, -0.405f, 0.0f, 0.381f, 0.0f, 1.0f, + -0.408f, 0.0f, 0.379f, 0.0f, 1.0f, -0.411f, 0.0f, 0.377f, 0.0f, 1.0f, -0.414f, 0.0f, 0.375f, 0.0f, 1.0f, -0.417f, 0.0f, 0.372f, 0.0f, 1.0f, + -0.42f, 0.0f, 0.37f, 0.0f, 1.0f, -0.423f, 0.0f, 0.368f, 0.0f, 1.0f, -0.425f, 0.0f, 0.366f, 0.0f, 1.0f, -0.428f, 0.0f, 0.364f, 0.0f, 1.0f, + -0.431f, 0.0f, 0.362f, 0.0f, 1.0f, -0.433f, 0.0f, 0.359f, 0.0f, 1.0f, -0.436f, 0.0f, 0.357f, 0.0f, 1.0f, -0.438f, 0.0f, 0.355f, 0.0f, 1.0f, + -0.441f, 0.0f, 0.353f, 0.0f, 1.0f, -0.443f, 0.0f, 0.351f, 0.0f, 1.0f, -0.445f, 0.0f, 0.349f, 0.0f, 1.0f, -0.447f, 0.0f, 0.346f, 0.0f, 1.0f, + -0.45f, 0.0f, 0.344f, 0.0f, 1.0f, -0.452f, 0.0f, 0.342f, 0.0f, 1.0f, -0.454f, 0.0f, 0.34f, 0.0f, 1.0f, -0.456f, 0.0f, 0.337f, 0.0f, 1.0f, + -0.458f, 0.0f, 0.335f, 0.0f, 1.0f, -0.46f, 0.0f, 0.333f, 0.0f, 1.0f, -0.462f, 0.0f, 0.33f, 0.0f, 1.0f, -0.464f, 0.0f, 0.328f, 0.0f, 1.0f, + -0.466f, 0.0f, 0.326f, 0.0f, 1.0f, -0.468f, 0.0f, 0.323f, 0.0f, 1.0f, -0.47f, 0.0f, 0.321f, 0.0f, 1.0f, -0.472f, 0.0f, 0.319f, 0.0f, 1.0f, + -0.474f, 0.0f, 0.316f, 0.0f, 1.0f, -0.475f, 0.0f, 0.314f, 0.0f, 1.0f, -0.477f, 0.0f, 0.311f, 0.0f, 1.0f, -0.479f, 0.0f, 0.309f, 0.0f, 1.0f, + -0.481f, 0.0f, 0.307f, 0.0f, 1.0f, -0.482f, 0.0f, 0.304f, 0.0f, 1.0f, -0.484f, 0.0f, 0.302f, 0.0f, 1.0f, -0.486f, 0.0f, 0.299f, 0.0f, 1.0f, + -0.487f, 0.0f, 0.297f, 0.0f, 1.0f, -0.489f, 0.0f, 0.294f, 0.0f, 1.0f, -0.49f, 0.0f, 0.292f, 0.0f, 1.0f, -0.492f, 0.0f, 0.289f, 0.0f, 1.0f, + -0.494f, 0.0f, 0.286f, 0.0f, 1.0f, -0.495f, 0.0f, 0.284f, 0.0f, 1.0f, -0.497f, 0.0f, 0.281f, 0.0f, 1.0f, -0.498f, 0.0f, 0.279f, 0.001f, 1.0f, + -0.499f, 0.0f, 0.276f, 0.001f, 1.0f, -0.501f, 0.0f, 0.273f, 0.002f, 1.0f, -0.502f, 0.0f, 0.271f, 0.003f, 1.0f, -0.504f, 0.0f, 0.268f, 0.005f, 1.0f, + -0.505f, 0.0f, 0.265f, 0.008f, 1.0f, -0.506f, 0.0f, 0.262f, 0.011f, 1.0f, -0.508f, 0.0f, 0.259f, 0.016f, 1.0f, -0.509f, 0.0f, 0.256f, 0.021f, 1.0f, + -0.51f, 0.0f, 0.254f, 0.027f, 1.0f, -0.512f, 0.0f, 0.251f, 0.035f, 1.0f, -0.513f, 0.0f, 0.248f, 0.043f, 1.0f, -0.514f, 0.0f, 0.245f, 0.053f, 1.0f, + -0.515f, 0.0f, 0.242f, 0.064f, 1.0f, -0.516f, 0.0f, 0.239f, 0.076f, 1.0f, -0.517f, 0.0f, 0.235f, 0.09f, 1.0f, -0.519f, 0.0f, 0.232f, 0.105f, 1.0f, + -0.52f, 0.0f, 0.229f, 0.122f, 1.0f, -0.521f, 0.0f, 0.226f, 0.14f, 1.0f, -0.521f, 0.0f, 0.222f, 0.159f, 1.0f, -0.522f, 0.0f, 0.219f, 0.179f, 1.0f, + -0.523f, 0.0f, 0.216f, 0.2f, 1.0f, -0.524f, 0.0f, 0.212f, 0.221f, 1.0f, -0.525f, 0.0f, 0.209f, 0.243f, 1.0f, -0.526f, 0.0f, 0.205f, 0.265f, 1.0f, + -0.526f, 0.0f, 0.202f, 0.286f, 1.0f, -0.527f, 0.0f, 0.198f, 0.306f, 1.0f, -0.527f, 0.0f, 0.195f, 0.326f, 1.0f, -0.528f, 0.0f, 0.191f, 0.345f, 1.0f, + -0.528f, 0.0f, 0.187f, 0.363f, 1.0f, -0.529f, 0.0f, 0.184f, 0.38f, 1.0f, -0.529f, 0.0f, 0.18f, 0.395f, 1.0f, -0.529f, 0.0f, 0.176f, 0.41f, 1.0f, + -0.53f, 0.0f, 0.173f, 0.424f, 1.0f, -0.53f, 0.0f, 0.169f, 0.438f, 1.0f, -0.53f, 0.0f, 0.165f, 0.452f, 1.0f, -0.53f, 0.0f, 0.161f, 0.465f, 1.0f, + -0.53f, 0.0f, 0.157f, 0.478f, 1.0f, -0.53f, 0.0f, 0.154f, 0.492f, 1.0f, -0.53f, 0.0f, 0.15f, 0.505f, 1.0f, -0.53f, 0.0f, 0.146f, 0.517f, 1.0f, + -0.53f, 0.0f, 0.142f, 0.53f, 1.0f, -0.529f, 0.0f, 0.138f, 0.542f, 1.0f, -0.529f, 0.0f, 0.134f, 0.553f, 1.0f, -0.528f, 0.0f, 0.13f, 0.564f, 1.0f, + -0.528f, 0.0f, 0.127f, 0.574f, 1.0f, -0.527f, 0.0f, 0.123f, 0.583f, 1.0f, -0.527f, 0.0f, 0.119f, 0.592f, 1.0f, -0.526f, 0.0f, 0.115f, 0.6f, 1.0f, + -0.526f, 0.0f, 0.111f, 0.608f, 1.0f, -0.525f, 0.0f, 0.108f, 0.615f, 1.0f, -0.524f, 0.0f, 0.104f, 0.622f, 1.0f, -0.523f, 0.0f, 0.1f, 0.628f, 1.0f, + -0.522f, 0.0f, 0.097f, 0.635f, 1.0f, -0.521f, 0.0f, 0.093f, 0.641f, 1.0f, -0.52f, 0.0f, 0.089f, 0.647f, 1.0f, -0.519f, 0.0f, 0.086f, 0.653f, 1.0f, + -0.518f, 0.0f, 0.082f, 0.659f, 1.0f, -0.517f, 0.0f, 0.079f, 0.664f, 1.0f, -0.515f, 0.0f, 0.075f, 0.67f, 1.0f, -0.514f, 0.0f, 0.072f, 0.675f, 1.0f, + -0.513f, 0.0f, 0.069f, 0.68f, 1.0f, -0.511f, 0.0f, 0.065f, 0.685f, 1.0f, -0.51f, 0.0f, 0.062f, 0.69f, 1.0f, -0.509f, 0.0f, 0.059f, 0.695f, 1.0f, + -0.507f, 0.0f, 0.056f, 0.7f, 1.0f, -0.505f, 0.0f, 0.053f, 0.704f, 1.0f, -0.504f, 0.0f, 0.049f, 0.709f, 1.0f, -0.502f, 0.0f, 0.046f, 0.714f, 1.0f, + -0.5f, 0.0f, 0.043f, 0.719f, 1.0f, -0.499f, 0.0f, 0.04f, 0.724f, 1.0f, -0.497f, 0.0f, 0.038f, 0.73f, 1.0f, -0.495f, 0.0f, 0.035f, 0.735f, 1.0f, + -0.493f, 0.0f, 0.032f, 0.741f, 1.0f, -0.491f, 0.0f, 0.029f, 0.748f, 1.0f, -0.489f, 0.0f, 0.026f, 0.754f, 1.0f, -0.488f, -0.0f, 0.024f, 0.76f, 1.0f, + -0.486f, -0.0f, 0.022f, 0.767f, 1.0f, -0.485f, -0.0f, 0.019f, 0.773f, 1.0f, -0.483f, -0.0f, 0.017f, 0.779f, 1.0f, -0.482f, -0.0f, 0.015f, 0.785f, 1.0f, + -0.48f, -0.0f, 0.013f, 0.79f, 1.0f, -0.478f, -0.0f, 0.01f, 0.795f, 1.0f, -0.476f, -0.0f, 0.008f, 0.8f, 1.0f, -0.474f, -0.0f, 0.006f, 0.804f, 1.0f, + -0.472f, -0.0f, 0.004f, 0.808f, 1.0f, -0.47f, -0.0f, 0.002f, 0.811f, 1.0f, -0.468f, -0.0f, -0.0f, 0.814f, 1.0f, -0.466f, -0.0f, -0.002f, 0.816f, 1.0f, + -0.464f, -0.0f, -0.004f, 0.818f, 1.0f, -0.461f, -0.0f, -0.006f, 0.82f, 1.0f, -0.459f, -0.0f, -0.008f, 0.822f, 1.0f, -0.456f, -0.0f, -0.01f, 0.823f, 1.0f, + -0.454f, -0.0f, -0.012f, 0.825f, 1.0f, -0.451f, -0.0f, -0.014f, 0.826f, 1.0f, -0.448f, -0.0f, -0.016f, 0.827f, 1.0f, -0.445f, -0.0f, -0.018f, 0.828f, 1.0f, + -0.442f, -0.0f, -0.02f, 0.829f, 1.0f, -0.439f, -0.0f, -0.022f, 0.829f, 1.0f, -0.436f, -0.0f, -0.024f, 0.83f, 1.0f, -0.433f, -0.0f, -0.026f, 0.83f, 1.0f, + -0.43f, -0.0f, -0.027f, 0.83f, 1.0f, -0.426f, -0.0f, -0.029f, 0.83f, 1.0f, -0.423f, 0.0f, -0.031f, 0.83f, 1.0f, -0.42f, 0.0f, -0.032f, 0.83f, 1.0f, + -0.417f, 0.0f, -0.033f, 0.831f, 1.0f, -0.414f, 0.0f, -0.034f, 0.831f, 1.0f, -0.411f, 0.0f, -0.035f, 0.831f, 1.0f, -0.408f, 0.0f, -0.037f, 0.831f, 1.0f, + -0.405f, 0.0f, -0.038f, 0.831f, 1.0f, -0.402f, 0.0f, -0.039f, 0.831f, 1.0f, -0.399f, 0.0f, -0.039f, 0.831f, 1.0f, -0.396f, 0.0f, -0.04f, 0.832f, 1.0f, + -0.393f, 0.0f, -0.041f, 0.832f, 1.0f, -0.389f, 0.0f, -0.042f, 0.832f, 1.0f, -0.386f, 0.0f, -0.043f, 0.832f, 1.0f, -0.383f, 0.0f, -0.044f, 0.832f, 1.0f, + -0.379f, 0.0f, -0.044f, 0.832f, 1.0f, -0.376f, 0.0f, -0.045f, 0.832f, 1.0f, -0.372f, 0.0f, -0.045f, 0.832f, 1.0f, -0.369f, 0.0f, -0.046f, 0.832f, 1.0f, + -0.366f, 0.0f, -0.047f, 0.832f, 1.0f, -0.362f, 0.0f, -0.047f, 0.832f, 1.0f, -0.359f, 0.0f, -0.047f, 0.831f, 1.0f, -0.355f, 0.0f, -0.048f, 0.831f, 1.0f, + -0.352f, 0.0f, -0.048f, 0.83f, 1.0f, -0.348f, 0.0f, -0.048f, 0.83f, 1.0f, -0.345f, 0.0f, -0.049f, 0.829f, 1.0f, -0.341f, 0.0f, -0.049f, 0.828f, 1.0f, + -0.338f, 0.0f, -0.049f, 0.827f, 1.0f, -0.334f, 0.0f, -0.049f, 0.826f, 1.0f, -0.331f, 0.0f, -0.049f, 0.823f, 1.0f, -0.327f, 0.0f, -0.049f, 0.82f, 1.0f, + -0.323f, 0.0f, -0.048f, 0.816f, 1.0f, -0.32f, 0.0f, -0.048f, 0.811f, 1.0f, -0.316f, 0.0f, -0.048f, 0.804f, 1.0f, -0.313f, 0.0f, -0.048f, 0.797f, 1.0f, + -0.309f, 0.0f, -0.047f, 0.79f, 1.0f, -0.306f, 0.0f, -0.047f, 0.782f, 1.0f, -0.302f, 0.0f, -0.046f, 0.774f, 1.0f, -0.299f, 0.0f, -0.045f, 0.767f, 1.0f, + -0.295f, 0.0f, -0.044f, 0.76f, 1.0f, -0.292f, 0.0f, -0.044f, 0.753f, 1.0f, -0.288f, 0.0f, -0.043f, 0.748f, 1.0f, -0.285f, 0.0f, -0.042f, 0.742f, 1.0f, + -0.282f, 0.0f, -0.041f, 0.738f, 1.0f, -0.278f, 0.0f, -0.04f, 0.734f, 1.0f, -0.275f, 0.0f, -0.039f, 0.73f, 1.0f, -0.272f, 0.0f, -0.037f, 0.726f, 1.0f, + -0.269f, 0.0f, -0.036f, 0.723f, 1.0f, -0.266f, 0.0f, -0.035f, 0.72f, 1.0f, -0.263f, 0.0f, -0.034f, 0.717f, 1.0f, -0.26f, 0.0f, -0.032f, 0.713f, 1.0f, + -0.257f, 0.0f, -0.031f, 0.71f, 1.0f, -0.255f, 0.0f, -0.029f, 0.706f, 1.0f, -0.252f, 0.0f, -0.028f, 0.702f, 1.0f, -0.249f, 0.0f, -0.026f, 0.698f, 1.0f, + -0.247f, 0.0f, -0.025f, 0.693f, 1.0f, -0.244f, 0.0f, -0.023f, 0.688f, 1.0f, -0.242f, 0.0f, -0.021f, 0.684f, 1.0f, -0.239f, 0.0f, -0.02f, 0.679f, 1.0f, + -0.237f, 0.0f, -0.018f, 0.675f, 1.0f, -0.234f, 0.0f, -0.016f, 0.671f, 1.0f, -0.232f, 0.0f, -0.014f, 0.667f, 1.0f, -0.23f, 0.0f, -0.013f, 0.663f, 1.0f, + -0.228f, 0.0f, -0.011f, 0.66f, 1.0f, -0.225f, 0.0f, -0.009f, 0.657f, 1.0f, -0.223f, 0.0f, -0.007f, 0.654f, 1.0f, -0.221f, 0.0f, -0.005f, 0.651f, 1.0f, + -0.219f, 0.0f, -0.003f, 0.649f, 1.0f, -0.217f, 0.0f, -0.001f, 0.645f, 1.0f, -0.215f, 0.0f, 0.002f, 0.642f, 1.0f, -0.213f, 0.0f, 0.004f, 0.639f, 1.0f, + -0.211f, 0.0f, 0.006f, 0.635f, 1.0f, -0.209f, 0.0f, 0.008f, 0.631f, 1.0f, -0.207f, 0.0f, 0.011f, 0.627f, 1.0f, -0.206f, 0.0f, 0.013f, 0.623f, 1.0f, + -0.204f, 0.0f, 0.016f, 0.619f, 1.0f, -0.202f, 0.0f, 0.018f, 0.615f, 1.0f, -0.2f, 0.0f, 0.021f, 0.61f, 1.0f, -0.199f, 0.0f, 0.023f, 0.606f, 1.0f, + -0.197f, 0.0f, 0.026f, 0.602f, 1.0f, -0.195f, 0.0f, 0.029f, 0.598f, 1.0f, -0.194f, 0.0f, 0.032f, 0.595f, 1.0f, -0.192f, 0.0f, 0.034f, 0.592f, 1.0f, + -0.191f, 0.0f, 0.037f, 0.589f, 1.0f, -0.19f, 0.0f, 0.04f, 0.587f, 1.0f, -0.188f, 0.0f, 0.043f, 0.585f, 1.0f, -0.187f, 0.0f, 0.046f, 0.584f, 1.0f, + -0.186f, 0.0f, 0.05f, 0.583f, 1.0f, -0.185f, 0.0f, 0.053f, 0.582f, 1.0f, -0.183f, 0.0f, 0.056f, 0.581f, 1.0f, -0.182f, 0.0f, 0.059f, 0.581f, 1.0f, + -0.181f, 0.0f, 0.062f, 0.581f, 1.0f, -0.18f, 0.0f, 0.066f, 0.581f, 1.0f, -0.179f, 0.0f, 0.069f, 0.58f, 1.0f, -0.178f, 0.0f, 0.072f, 0.58f, 1.0f, + -0.177f, 0.0f, 0.076f, 0.58f, 1.0f, -0.177f, 0.0f, 0.079f, 0.58f, 1.0f, -0.176f, 0.0f, 0.083f, 0.58f, 1.0f, -0.175f, 0.0f, 0.086f, 0.58f, 1.0f, + -0.174f, 0.0f, 0.09f, 0.58f, 1.0f, -0.174f, 0.0f, 0.093f, 0.58f, 1.0f, -0.173f, 0.0f, 0.097f, 0.58f, 1.0f, -0.172f, 0.0f, 0.1f, 0.58f, 1.0f, + -0.172f, 0.0f, 0.104f, 0.58f, 1.0f, -0.171f, 0.0f, 0.108f, 0.579f, 1.0f, -0.171f, 0.0f, 0.111f, 0.579f, 1.0f, -0.17f, 0.0f, 0.115f, 0.578f, 1.0f, + -0.17f, 0.0f, 0.119f, 0.578f, 1.0f, -0.17f, 0.0f, 0.122f, 0.577f, 1.0f, -0.169f, 0.0f, 0.126f, 0.577f, 1.0f, -0.169f, 0.0f, 0.13f, 0.576f, 1.0f, + -0.169f, 0.0f, 0.134f, 0.576f, 1.0f, -0.169f, 0.0f, 0.137f, 0.575f, 1.0f, -0.169f, 0.0f, 0.141f, 0.575f, 1.0f, -0.169f, 0.0f, 0.145f, 0.574f, 1.0f, + -0.169f, 0.0f, 0.149f, 0.572f, 1.0f, -0.169f, 0.0f, 0.153f, 0.571f, 1.0f, -0.169f, 0.0f, 0.157f, 0.569f, 1.0f, -0.169f, 0.0f, 0.16f, 0.566f, 1.0f, + -0.169f, 0.0f, 0.164f, 0.562f, 1.0f, -0.17f, 0.0f, 0.168f, 0.558f, 1.0f, -0.17f, 0.0f, 0.172f, 0.553f, 1.0f, -0.17f, 0.0f, 0.176f, 0.547f, 1.0f, + -0.171f, 0.0f, 0.18f, 0.539f, 1.0f, -0.171f, 0.0f, 0.183f, 0.531f, 1.0f, -0.172f, 0.0f, 0.187f, 0.522f, 1.0f, -0.172f, 0.0f, 0.191f, 0.513f, 1.0f, + -0.173f, 0.0f, 0.194f, 0.503f, 1.0f, -0.173f, 0.0f, 0.198f, 0.493f, 1.0f, -0.174f, 0.0f, 0.202f, 0.483f, 1.0f, -0.175f, 0.0f, 0.205f, 0.473f, 1.0f, + -0.176f, 0.0f, 0.209f, 0.464f, 1.0f, -0.177f, 0.0f, 0.212f, 0.455f, 1.0f, -0.178f, 0.0f, 0.215f, 0.446f, 1.0f, -0.178f, 0.0f, 0.219f, 0.438f, 1.0f, + -0.179f, 0.0f, 0.222f, 0.428f, 1.0f, -0.18f, 0.0f, 0.226f, 0.418f, 1.0f, -0.182f, 0.0f, 0.229f, 0.407f, 1.0f, -0.183f, 0.0f, 0.232f, 0.394f, 1.0f, + -0.184f, 0.0f, 0.236f, 0.38f, 1.0f, -0.185f, 0.0f, 0.239f, 0.364f, 1.0f, -0.186f, 0.0f, 0.242f, 0.348f, 1.0f, -0.187f, 0.0f, 0.245f, 0.33f, 1.0f, + -0.188f, 0.0f, 0.249f, 0.311f, 1.0f, -0.19f, 0.0f, 0.252f, 0.293f, 1.0f, -0.191f, 0.0f, 0.255f, 0.275f, 1.0f, -0.192f, 0.0f, 0.258f, 0.258f, 1.0f, + -0.194f, 0.0f, 0.261f, 0.242f, 1.0f, -0.195f, 0.0f, 0.264f, 0.228f, 1.0f, -0.196f, 0.0f, 0.267f, 0.214f, 1.0f, -0.198f, 0.0f, 0.27f, 0.202f, 1.0f, + -0.199f, 0.0f, 0.273f, 0.191f, 1.0f, -0.201f, 0.0f, 0.276f, 0.181f, 1.0f, -0.202f, 0.0f, 0.279f, 0.171f, 1.0f, -0.204f, 0.0f, 0.282f, 0.162f, 1.0f, + -0.205f, 0.0f, 0.285f, 0.152f, 1.0f, -0.206f, 0.0f, 0.287f, 0.143f, 1.0f, -0.208f, 0.0f, 0.29f, 0.134f, 1.0f, -0.21f, 0.0f, 0.293f, 0.126f, 1.0f, + -0.211f, 0.0f, 0.295f, 0.117f, 1.0f, -0.213f, 0.0f, 0.298f, 0.109f, 1.0f, -0.214f, 0.0f, 0.301f, 0.101f, 1.0f, -0.216f, 0.0f, 0.303f, 0.094f, 1.0f, + -0.217f, 0.0f, 0.306f, 0.087f, 1.0f, -0.219f, 0.0f, 0.308f, 0.081f, 1.0f, -0.221f, 0.0f, 0.311f, 0.076f, 1.0f, -0.223f, 0.0f, 0.313f, 0.071f, 1.0f, + -0.224f, 0.0f, 0.316f, 0.067f, 1.0f, -0.226f, 0.0f, 0.318f, 0.065f, 1.0f, -0.228f, 0.0f, 0.321f, 0.062f, 1.0f, -0.23f, 0.0f, 0.323f, 0.061f, 1.0f, + -0.232f, 0.0f, 0.326f, 0.06f, 1.0f, -0.233f, 0.0f, 0.328f, 0.06f, 1.0f, -0.235f, 0.0f, 0.331f, 0.061f, 1.0f, -0.237f, 0.0f, 0.334f, 0.061f, 1.0f, + -0.239f, 0.0f, 0.336f, 0.062f, 1.0f, -0.241f, 0.0f, 0.339f, 0.063f, 1.0f, -0.243f, 0.0f, 0.341f, 0.064f, 1.0f, -0.245f, 0.0f, 0.344f, 0.065f, 1.0f, + -0.248f, 0.0f, 0.346f, 0.065f, 1.0f, -0.25f, 0.0f, 0.349f, 0.065f, 1.0f, -0.252f, 0.0f, 0.351f, 0.064f, 1.0f, -0.254f, 0.0f, 0.354f, 0.062f, 1.0f, + -0.256f, 0.0f, 0.356f, 0.06f, 1.0f, -0.258f, 0.0f, 0.359f, 0.058f, 1.0f, -0.261f, 0.0f, 0.361f, 0.055f, 1.0f, -0.263f, 0.0f, 0.364f, 0.051f, 1.0f, + -0.265f, 0.0f, 0.366f, 0.046f, 1.0f, -0.267f, 0.0f, 0.368f, 0.04f, 1.0f, -0.269f, 0.0f, 0.37f, 0.034f, 1.0f, -0.272f, 0.0f, 0.373f, 0.027f, 1.0f, + -0.274f, 0.0f, 0.375f, 0.019f, 1.0f, -0.276f, 0.0f, 0.377f, 0.012f, 1.0f, -0.278f, 0.0f, 0.379f, 0.007f, 1.0f, -0.28f, 0.0f, 0.381f, 0.003f, 1.0f, + -0.282f, 0.0f, 0.383f, 0.001f, 1.0f, -0.284f, 0.0f, 0.385f, 0.0f, 1.0f, -0.286f, 0.0f, 0.387f, 0.0f, 1.0f, -0.287f, 0.0f, 0.388f, 0.0f, 1.0f, + -0.289f, 0.0f, 0.39f, 0.0f, 1.0f, +}; + +static const float data22[309 * GP_PRIM_DATABUF_SIZE] = { + 0.294f, 0.0f, 0.372f, 0.0f, 1.0f, 0.291f, 0.0f, 0.37f, 0.001f, 1.0f, 0.289f, 0.0f, 0.368f, 0.002f, 1.0f, 0.286f, 0.0f, 0.366f, 0.003f, 1.0f, + 0.284f, 0.0f, 0.364f, 0.006f, 1.0f, 0.282f, 0.0f, 0.362f, 0.01f, 1.0f, 0.279f, 0.0f, 0.36f, 0.015f, 1.0f, 0.277f, 0.0f, 0.358f, 0.022f, 1.0f, + 0.274f, 0.0f, 0.356f, 0.03f, 1.0f, 0.272f, 0.0f, 0.353f, 0.04f, 1.0f, 0.269f, 0.0f, 0.351f, 0.051f, 1.0f, 0.267f, 0.0f, 0.349f, 0.062f, 1.0f, + 0.265f, 0.0f, 0.347f, 0.074f, 1.0f, 0.262f, 0.0f, 0.344f, 0.086f, 1.0f, 0.26f, 0.0f, 0.342f, 0.097f, 1.0f, 0.258f, 0.0f, 0.34f, 0.108f, 1.0f, + 0.256f, 0.0f, 0.337f, 0.119f, 1.0f, 0.253f, 0.0f, 0.335f, 0.128f, 1.0f, 0.251f, 0.0f, 0.333f, 0.137f, 1.0f, 0.249f, 0.0f, 0.33f, 0.145f, 1.0f, + 0.247f, 0.0f, 0.328f, 0.153f, 1.0f, 0.246f, 0.0f, 0.325f, 0.161f, 1.0f, 0.244f, 0.0f, 0.323f, 0.168f, 1.0f, 0.242f, 0.0f, 0.321f, 0.176f, 1.0f, + 0.24f, 0.0f, 0.318f, 0.183f, 1.0f, 0.239f, 0.0f, 0.316f, 0.191f, 1.0f, 0.237f, 0.0f, 0.314f, 0.198f, 1.0f, 0.235f, 0.0f, 0.311f, 0.206f, 1.0f, + 0.233f, 0.0f, 0.309f, 0.214f, 1.0f, 0.231f, 0.0f, 0.306f, 0.223f, 1.0f, 0.23f, 0.0f, 0.304f, 0.231f, 1.0f, 0.228f, 0.0f, 0.301f, 0.24f, 1.0f, + 0.226f, 0.0f, 0.299f, 0.248f, 1.0f, 0.224f, 0.0f, 0.296f, 0.256f, 1.0f, 0.223f, 0.0f, 0.294f, 0.264f, 1.0f, 0.221f, 0.0f, 0.291f, 0.272f, 1.0f, + 0.219f, 0.0f, 0.288f, 0.28f, 1.0f, 0.218f, 0.0f, 0.286f, 0.287f, 1.0f, 0.216f, 0.0f, 0.283f, 0.294f, 1.0f, 0.214f, 0.0f, 0.281f, 0.301f, 1.0f, + 0.213f, 0.0f, 0.278f, 0.307f, 1.0f, 0.211f, 0.0f, 0.275f, 0.314f, 1.0f, 0.21f, 0.0f, 0.273f, 0.32f, 1.0f, 0.208f, 0.0f, 0.27f, 0.327f, 1.0f, + 0.206f, 0.0f, 0.267f, 0.333f, 1.0f, 0.205f, 0.0f, 0.265f, 0.339f, 1.0f, 0.204f, 0.0f, 0.262f, 0.345f, 1.0f, 0.202f, 0.0f, 0.259f, 0.351f, 1.0f, + 0.201f, 0.0f, 0.256f, 0.357f, 1.0f, 0.199f, 0.0f, 0.253f, 0.362f, 1.0f, 0.198f, 0.0f, 0.25f, 0.368f, 1.0f, 0.197f, 0.0f, 0.247f, 0.373f, 1.0f, + 0.195f, 0.0f, 0.244f, 0.379f, 1.0f, 0.194f, 0.0f, 0.241f, 0.383f, 1.0f, 0.193f, 0.0f, 0.238f, 0.388f, 1.0f, 0.192f, 0.0f, 0.235f, 0.392f, 1.0f, + 0.191f, 0.0f, 0.232f, 0.396f, 1.0f, 0.19f, 0.0f, 0.229f, 0.399f, 1.0f, 0.189f, 0.0f, 0.226f, 0.402f, 1.0f, 0.188f, 0.0f, 0.222f, 0.405f, 1.0f, + 0.187f, 0.0f, 0.219f, 0.407f, 1.0f, 0.186f, 0.0f, 0.216f, 0.409f, 1.0f, 0.185f, 0.0f, 0.213f, 0.411f, 1.0f, 0.184f, 0.0f, 0.209f, 0.412f, 1.0f, + 0.183f, 0.0f, 0.206f, 0.413f, 1.0f, 0.183f, 0.0f, 0.203f, 0.414f, 1.0f, 0.182f, 0.0f, 0.199f, 0.415f, 1.0f, 0.181f, 0.0f, 0.196f, 0.416f, 1.0f, + 0.181f, 0.0f, 0.193f, 0.417f, 1.0f, 0.18f, 0.0f, 0.189f, 0.417f, 1.0f, 0.18f, 0.0f, 0.186f, 0.418f, 1.0f, 0.179f, 0.0f, 0.182f, 0.419f, 1.0f, + 0.179f, 0.0f, 0.179f, 0.421f, 1.0f, 0.179f, 0.0f, 0.176f, 0.422f, 1.0f, 0.178f, 0.0f, 0.172f, 0.423f, 1.0f, 0.178f, 0.0f, 0.169f, 0.425f, 1.0f, + 0.178f, 0.0f, 0.165f, 0.427f, 1.0f, 0.178f, 0.0f, 0.162f, 0.429f, 1.0f, 0.178f, 0.0f, 0.158f, 0.431f, 1.0f, 0.178f, 0.0f, 0.155f, 0.434f, 1.0f, + 0.178f, 0.0f, 0.152f, 0.436f, 1.0f, 0.178f, 0.0f, 0.148f, 0.439f, 1.0f, 0.178f, 0.0f, 0.145f, 0.442f, 1.0f, 0.178f, 0.0f, 0.141f, 0.446f, 1.0f, + 0.178f, 0.0f, 0.138f, 0.449f, 1.0f, 0.178f, 0.0f, 0.134f, 0.453f, 1.0f, 0.178f, 0.0f, 0.131f, 0.458f, 1.0f, 0.179f, 0.0f, 0.127f, 0.462f, 1.0f, + 0.179f, 0.0f, 0.124f, 0.467f, 1.0f, 0.179f, 0.0f, 0.12f, 0.472f, 1.0f, 0.18f, 0.0f, 0.117f, 0.478f, 1.0f, 0.18f, 0.0f, 0.113f, 0.483f, 1.0f, + 0.181f, 0.0f, 0.11f, 0.489f, 1.0f, 0.182f, 0.0f, 0.106f, 0.494f, 1.0f, 0.182f, 0.0f, 0.103f, 0.5f, 1.0f, 0.183f, 0.0f, 0.099f, 0.505f, 1.0f, + 0.184f, 0.0f, 0.096f, 0.511f, 1.0f, 0.185f, 0.0f, 0.092f, 0.516f, 1.0f, 0.185f, 0.0f, 0.089f, 0.521f, 1.0f, 0.186f, 0.0f, 0.086f, 0.525f, 1.0f, + 0.187f, 0.0f, 0.082f, 0.53f, 1.0f, 0.188f, 0.0f, 0.079f, 0.534f, 1.0f, 0.189f, 0.0f, 0.076f, 0.537f, 1.0f, 0.191f, 0.0f, 0.073f, 0.541f, 1.0f, + 0.192f, 0.0f, 0.069f, 0.544f, 1.0f, 0.193f, 0.0f, 0.066f, 0.547f, 1.0f, 0.194f, 0.0f, 0.063f, 0.55f, 1.0f, 0.196f, 0.0f, 0.061f, 0.553f, 1.0f, + 0.197f, 0.0f, 0.058f, 0.556f, 1.0f, 0.198f, 0.0f, 0.055f, 0.559f, 1.0f, 0.2f, 0.0f, 0.052f, 0.562f, 1.0f, 0.201f, 0.0f, 0.049f, 0.564f, 1.0f, + 0.203f, 0.0f, 0.047f, 0.566f, 1.0f, 0.205f, 0.0f, 0.044f, 0.569f, 1.0f, 0.206f, 0.0f, 0.042f, 0.571f, 1.0f, 0.208f, 0.0f, 0.039f, 0.573f, 1.0f, + 0.21f, 0.0f, 0.037f, 0.575f, 1.0f, 0.212f, 0.0f, 0.035f, 0.576f, 1.0f, 0.214f, 0.0f, 0.032f, 0.578f, 1.0f, 0.215f, 0.0f, 0.03f, 0.579f, 1.0f, + 0.217f, 0.0f, 0.028f, 0.581f, 1.0f, 0.22f, 0.0f, 0.025f, 0.582f, 1.0f, 0.222f, 0.0f, 0.023f, 0.583f, 1.0f, 0.224f, 0.0f, 0.021f, 0.585f, 1.0f, + 0.226f, 0.0f, 0.019f, 0.587f, 1.0f, 0.228f, 0.0f, 0.016f, 0.589f, 1.0f, 0.231f, 0.0f, 0.014f, 0.592f, 1.0f, 0.233f, 0.0f, 0.012f, 0.596f, 1.0f, + 0.236f, 0.0f, 0.01f, 0.599f, 1.0f, 0.238f, 0.0f, 0.008f, 0.604f, 1.0f, 0.241f, 0.0f, 0.006f, 0.608f, 1.0f, 0.243f, 0.0f, 0.004f, 0.612f, 1.0f, + 0.246f, 0.0f, 0.002f, 0.615f, 1.0f, 0.249f, 0.0f, 0.0f, 0.619f, 1.0f, 0.251f, 0.0f, -0.002f, 0.622f, 1.0f, 0.254f, 0.0f, -0.003f, 0.624f, 1.0f, + 0.257f, 0.0f, -0.005f, 0.626f, 1.0f, 0.26f, 0.0f, -0.007f, 0.628f, 1.0f, 0.263f, 0.0f, -0.008f, 0.63f, 1.0f, 0.266f, 0.0f, -0.01f, 0.632f, 1.0f, + 0.269f, 0.0f, -0.011f, 0.634f, 1.0f, 0.272f, 0.0f, -0.013f, 0.636f, 1.0f, 0.275f, 0.0f, -0.014f, 0.638f, 1.0f, 0.278f, 0.0f, -0.015f, 0.64f, 1.0f, + 0.281f, 0.0f, -0.017f, 0.642f, 1.0f, 0.284f, 0.0f, -0.018f, 0.644f, 1.0f, 0.288f, 0.0f, -0.019f, 0.647f, 1.0f, 0.291f, 0.0f, -0.02f, 0.649f, 1.0f, + 0.294f, 0.0f, -0.021f, 0.651f, 1.0f, 0.297f, 0.0f, -0.022f, 0.653f, 1.0f, 0.301f, 0.0f, -0.023f, 0.656f, 1.0f, 0.304f, 0.0f, -0.024f, 0.658f, 1.0f, + 0.307f, 0.0f, -0.025f, 0.659f, 1.0f, 0.31f, 0.0f, -0.026f, 0.661f, 1.0f, 0.314f, 0.0f, -0.027f, 0.662f, 1.0f, 0.317f, 0.0f, -0.027f, 0.664f, 1.0f, + 0.32f, 0.0f, -0.028f, 0.665f, 1.0f, 0.324f, 0.0f, -0.028f, 0.665f, 1.0f, 0.327f, 0.0f, -0.029f, 0.666f, 1.0f, 0.33f, 0.0f, -0.029f, 0.666f, 1.0f, + 0.334f, 0.0f, -0.029f, 0.667f, 1.0f, 0.337f, 0.0f, -0.03f, 0.667f, 1.0f, 0.341f, 0.0f, -0.03f, 0.668f, 1.0f, 0.344f, 0.0f, -0.03f, 0.668f, 1.0f, + 0.348f, 0.0f, -0.03f, 0.668f, 1.0f, 0.351f, 0.0f, -0.03f, 0.668f, 1.0f, 0.354f, 0.0f, -0.03f, 0.668f, 1.0f, 0.358f, 0.0f, -0.029f, 0.668f, 1.0f, + 0.361f, 0.0f, -0.029f, 0.668f, 1.0f, 0.365f, 0.0f, -0.029f, 0.668f, 1.0f, 0.368f, 0.0f, -0.028f, 0.668f, 1.0f, 0.372f, 0.0f, -0.028f, 0.668f, 1.0f, + 0.375f, 0.0f, -0.027f, 0.668f, 1.0f, 0.378f, 0.0f, -0.027f, 0.668f, 1.0f, 0.382f, 0.0f, -0.026f, 0.667f, 1.0f, 0.385f, 0.0f, -0.025f, 0.667f, 1.0f, + 0.388f, 0.0f, -0.025f, 0.666f, 1.0f, 0.392f, 0.0f, -0.024f, 0.666f, 1.0f, 0.395f, 0.0f, -0.023f, 0.665f, 1.0f, 0.398f, 0.0f, -0.022f, 0.664f, 1.0f, + 0.401f, 0.0f, -0.021f, 0.664f, 1.0f, 0.405f, 0.0f, -0.02f, 0.663f, 1.0f, 0.408f, 0.0f, -0.019f, 0.663f, 1.0f, 0.411f, 0.0f, -0.018f, 0.662f, 1.0f, + 0.414f, 0.0f, -0.017f, 0.662f, 1.0f, 0.417f, 0.0f, -0.016f, 0.662f, 1.0f, 0.42f, 0.0f, -0.015f, 0.662f, 1.0f, 0.423f, 0.0f, -0.014f, 0.661f, 1.0f, + 0.426f, 0.0f, -0.012f, 0.661f, 1.0f, 0.429f, 0.0f, -0.011f, 0.661f, 1.0f, 0.432f, 0.0f, -0.01f, 0.661f, 1.0f, 0.434f, 0.0f, -0.009f, 0.66f, 1.0f, + 0.437f, 0.0f, -0.007f, 0.66f, 1.0f, 0.44f, 0.0f, -0.006f, 0.659f, 1.0f, 0.442f, 0.0f, -0.005f, 0.659f, 1.0f, 0.445f, 0.0f, -0.003f, 0.658f, 1.0f, + 0.448f, 0.0f, -0.002f, 0.658f, 1.0f, 0.45f, 0.0f, -0.001f, 0.657f, 1.0f, 0.452f, 0.0f, 0.001f, 0.656f, 1.0f, 0.455f, 0.0f, 0.002f, 0.655f, 1.0f, + 0.457f, 0.0f, 0.004f, 0.654f, 1.0f, 0.459f, 0.0f, 0.005f, 0.653f, 1.0f, 0.462f, 0.0f, 0.007f, 0.652f, 1.0f, 0.464f, 0.0f, 0.009f, 0.651f, 1.0f, + 0.466f, 0.0f, 0.01f, 0.65f, 1.0f, 0.468f, 0.0f, 0.012f, 0.65f, 1.0f, 0.47f, 0.0f, 0.014f, 0.649f, 1.0f, 0.472f, 0.0f, 0.016f, 0.648f, 1.0f, + 0.474f, 0.0f, 0.018f, 0.647f, 1.0f, 0.476f, 0.0f, 0.019f, 0.646f, 1.0f, 0.478f, 0.0f, 0.021f, 0.645f, 1.0f, 0.479f, 0.0f, 0.023f, 0.644f, 1.0f, + 0.481f, 0.0f, 0.025f, 0.643f, 1.0f, 0.483f, 0.0f, 0.027f, 0.642f, 1.0f, 0.485f, 0.0f, 0.03f, 0.642f, 1.0f, 0.486f, 0.0f, 0.032f, 0.641f, 1.0f, + 0.488f, 0.0f, 0.034f, 0.64f, 1.0f, 0.49f, 0.0f, 0.036f, 0.639f, 1.0f, 0.491f, 0.0f, 0.038f, 0.638f, 1.0f, 0.493f, 0.0f, 0.041f, 0.637f, 1.0f, + 0.494f, 0.0f, 0.043f, 0.636f, 1.0f, 0.496f, 0.0f, 0.045f, 0.635f, 1.0f, 0.497f, 0.0f, 0.048f, 0.635f, 1.0f, 0.499f, 0.0f, 0.05f, 0.634f, 1.0f, + 0.5f, 0.0f, 0.053f, 0.633f, 1.0f, 0.502f, 0.0f, 0.055f, 0.632f, 1.0f, 0.503f, 0.0f, 0.058f, 0.631f, 1.0f, 0.505f, 0.0f, 0.06f, 0.63f, 1.0f, + 0.506f, 0.0f, 0.063f, 0.63f, 1.0f, 0.507f, 0.0f, 0.066f, 0.629f, 1.0f, 0.509f, 0.0f, 0.068f, 0.628f, 1.0f, 0.51f, 0.0f, 0.071f, 0.628f, 1.0f, + 0.511f, 0.0f, 0.074f, 0.627f, 1.0f, 0.513f, 0.0f, 0.077f, 0.626f, 1.0f, 0.514f, 0.0f, 0.079f, 0.625f, 1.0f, 0.515f, 0.0f, 0.082f, 0.625f, 1.0f, + 0.516f, 0.0f, 0.085f, 0.624f, 1.0f, 0.518f, 0.0f, 0.088f, 0.623f, 1.0f, 0.519f, 0.0f, 0.091f, 0.622f, 1.0f, 0.52f, 0.0f, 0.094f, 0.62f, 1.0f, + 0.521f, 0.0f, 0.098f, 0.619f, 1.0f, 0.522f, 0.0f, 0.101f, 0.617f, 1.0f, 0.523f, 0.0f, 0.104f, 0.615f, 1.0f, 0.524f, 0.0f, 0.107f, 0.613f, 1.0f, + 0.525f, 0.0f, 0.111f, 0.611f, 1.0f, 0.526f, 0.0f, 0.114f, 0.609f, 1.0f, 0.527f, 0.0f, 0.118f, 0.607f, 1.0f, 0.527f, 0.0f, 0.121f, 0.605f, 1.0f, + 0.528f, 0.0f, 0.124f, 0.603f, 1.0f, 0.529f, 0.0f, 0.128f, 0.602f, 1.0f, 0.529f, 0.0f, 0.132f, 0.6f, 1.0f, 0.53f, 0.0f, 0.135f, 0.599f, 1.0f, + 0.531f, 0.0f, 0.139f, 0.598f, 1.0f, 0.531f, 0.0f, 0.142f, 0.598f, 1.0f, 0.531f, 0.0f, 0.146f, 0.597f, 1.0f, 0.532f, 0.0f, 0.15f, 0.596f, 1.0f, + 0.532f, 0.0f, 0.154f, 0.596f, 1.0f, 0.532f, 0.0f, 0.157f, 0.595f, 1.0f, 0.532f, 0.0f, 0.161f, 0.595f, 1.0f, 0.532f, 0.0f, 0.165f, 0.594f, 1.0f, + 0.532f, 0.0f, 0.169f, 0.593f, 1.0f, 0.532f, 0.0f, 0.173f, 0.592f, 1.0f, 0.532f, 0.0f, 0.177f, 0.591f, 1.0f, 0.532f, 0.0f, 0.181f, 0.59f, 1.0f, + 0.531f, 0.0f, 0.185f, 0.589f, 1.0f, 0.531f, 0.0f, 0.189f, 0.588f, 1.0f, 0.53f, 0.0f, 0.194f, 0.587f, 1.0f, 0.529f, 0.0f, 0.198f, 0.586f, 1.0f, + 0.528f, 0.0f, 0.202f, 0.585f, 1.0f, 0.527f, 0.0f, 0.207f, 0.584f, 1.0f, 0.526f, 0.0f, 0.211f, 0.584f, 1.0f, 0.525f, 0.0f, 0.215f, 0.583f, 1.0f, + 0.523f, 0.0f, 0.22f, 0.583f, 1.0f, 0.522f, 0.0f, 0.224f, 0.583f, 1.0f, 0.52f, 0.0f, 0.229f, 0.582f, 1.0f, 0.518f, 0.0f, 0.234f, 0.582f, 1.0f, + 0.515f, 0.0f, 0.238f, 0.582f, 1.0f, 0.513f, 0.0f, 0.243f, 0.581f, 1.0f, 0.51f, 0.0f, 0.247f, 0.58f, 1.0f, 0.508f, 0.0f, 0.252f, 0.579f, 1.0f, + 0.505f, 0.0f, 0.257f, 0.578f, 1.0f, 0.502f, 0.0f, 0.261f, 0.576f, 1.0f, 0.499f, 0.0f, 0.266f, 0.573f, 1.0f, 0.496f, 0.0f, 0.27f, 0.57f, 1.0f, + 0.492f, 0.0f, 0.275f, 0.566f, 1.0f, 0.489f, 0.0f, 0.279f, 0.561f, 1.0f, 0.485f, 0.0f, 0.284f, 0.555f, 1.0f, 0.481f, 0.0f, 0.288f, 0.548f, 1.0f, + 0.478f, 0.0f, 0.293f, 0.54f, 1.0f, 0.473f, 0.0f, 0.297f, 0.531f, 1.0f, 0.469f, 0.0f, 0.301f, 0.521f, 1.0f, 0.465f, 0.0f, 0.305f, 0.509f, 1.0f, + 0.461f, 0.0f, 0.309f, 0.496f, 1.0f, 0.456f, 0.0f, 0.313f, 0.481f, 1.0f, 0.452f, 0.0f, 0.317f, 0.464f, 1.0f, 0.448f, 0.0f, 0.321f, 0.445f, 1.0f, + 0.443f, 0.0f, 0.324f, 0.424f, 1.0f, 0.438f, 0.0f, 0.328f, 0.401f, 1.0f, 0.434f, 0.0f, 0.331f, 0.374f, 1.0f, 0.429f, 0.0f, 0.334f, 0.346f, 1.0f, + 0.425f, 0.0f, 0.337f, 0.314f, 1.0f, 0.421f, 0.0f, 0.34f, 0.281f, 1.0f, 0.416f, 0.0f, 0.343f, 0.245f, 1.0f, 0.412f, 0.0f, 0.346f, 0.208f, 1.0f, + 0.408f, 0.0f, 0.349f, 0.169f, 1.0f, 0.404f, 0.0f, 0.351f, 0.13f, 1.0f, 0.401f, 0.0f, 0.354f, 0.089f, 1.0f, 0.398f, 0.0f, 0.356f, 0.054f, 1.0f, + 0.394f, 0.0f, 0.359f, 0.0f, 1.0f, +}; + +static const float data23[209 * GP_PRIM_DATABUF_SIZE] = { + -0.751f, 0.0f, 0.173f, 0.0f, 1.0f, -0.751f, 0.0f, 0.168f, 0.0f, 1.0f, -0.75f, 0.0f, 0.164f, 0.0f, 1.0f, -0.75f, 0.0f, 0.16f, 0.0f, 1.0f, + -0.75f, 0.0f, 0.156f, 0.0f, 1.0f, -0.749f, 0.0f, 0.152f, 0.0f, 1.0f, -0.749f, 0.0f, 0.148f, 0.0f, 1.0f, -0.748f, 0.0f, 0.144f, 0.0f, 1.0f, + -0.747f, 0.0f, 0.14f, 0.001f, 1.0f, -0.747f, 0.0f, 0.137f, 0.002f, 1.0f, -0.746f, 0.0f, 0.133f, 0.005f, 1.0f, -0.745f, 0.0f, 0.129f, 0.008f, 1.0f, + -0.745f, 0.0f, 0.125f, 0.013f, 1.0f, -0.744f, 0.0f, 0.122f, 0.02f, 1.0f, -0.743f, 0.0f, 0.118f, 0.028f, 1.0f, -0.742f, 0.0f, 0.115f, 0.038f, 1.0f, + -0.741f, 0.0f, 0.111f, 0.049f, 1.0f, -0.74f, 0.0f, 0.108f, 0.061f, 1.0f, -0.739f, 0.0f, 0.105f, 0.073f, 1.0f, -0.738f, 0.0f, 0.101f, 0.085f, 1.0f, + -0.736f, 0.0f, 0.098f, 0.097f, 1.0f, -0.735f, 0.0f, 0.095f, 0.109f, 1.0f, -0.734f, 0.0f, 0.091f, 0.119f, 1.0f, -0.732f, 0.0f, 0.088f, 0.129f, 1.0f, + -0.731f, 0.0f, 0.085f, 0.138f, 1.0f, -0.729f, 0.0f, 0.082f, 0.146f, 1.0f, -0.728f, 0.0f, 0.079f, 0.153f, 1.0f, -0.726f, 0.0f, 0.076f, 0.158f, 1.0f, + -0.725f, 0.0f, 0.073f, 0.163f, 1.0f, -0.723f, 0.0f, 0.07f, 0.167f, 1.0f, -0.722f, 0.0f, 0.067f, 0.17f, 1.0f, -0.72f, 0.0f, 0.065f, 0.173f, 1.0f, + -0.718f, 0.0f, 0.062f, 0.174f, 1.0f, -0.717f, 0.0f, 0.059f, 0.175f, 1.0f, -0.715f, 0.0f, 0.057f, 0.176f, 1.0f, -0.714f, 0.0f, 0.054f, 0.176f, 1.0f, + -0.712f, 0.0f, 0.051f, 0.176f, 1.0f, -0.71f, 0.0f, 0.049f, 0.176f, 1.0f, -0.709f, 0.0f, 0.046f, 0.176f, 1.0f, -0.707f, 0.0f, 0.043f, 0.176f, 1.0f, + -0.705f, 0.0f, 0.041f, 0.176f, 1.0f, -0.703f, 0.0f, 0.038f, 0.176f, 1.0f, -0.701f, 0.0f, 0.035f, 0.176f, 1.0f, -0.7f, 0.0f, 0.033f, 0.177f, 1.0f, + -0.698f, 0.0f, 0.03f, 0.177f, 1.0f, -0.696f, 0.0f, 0.027f, 0.178f, 1.0f, -0.694f, 0.0f, 0.024f, 0.179f, 1.0f, -0.692f, 0.0f, 0.022f, 0.18f, 1.0f, + -0.69f, 0.0f, 0.019f, 0.181f, 1.0f, -0.688f, 0.0f, 0.016f, 0.182f, 1.0f, -0.685f, 0.0f, 0.013f, 0.184f, 1.0f, -0.683f, 0.0f, 0.01f, 0.187f, 1.0f, + -0.681f, 0.0f, 0.007f, 0.19f, 1.0f, -0.679f, 0.0f, 0.004f, 0.194f, 1.0f, -0.677f, 0.0f, 0.001f, 0.198f, 1.0f, -0.675f, 0.0f, -0.002f, 0.203f, 1.0f, + -0.673f, 0.0f, -0.005f, 0.209f, 1.0f, -0.67f, 0.0f, -0.008f, 0.215f, 1.0f, -0.668f, 0.0f, -0.011f, 0.222f, 1.0f, -0.666f, 0.0f, -0.014f, 0.229f, 1.0f, + -0.664f, 0.0f, -0.017f, 0.237f, 1.0f, -0.661f, 0.0f, -0.02f, 0.246f, 1.0f, -0.659f, 0.0f, -0.023f, 0.255f, 1.0f, -0.657f, 0.0f, -0.025f, 0.264f, 1.0f, + -0.654f, 0.0f, -0.028f, 0.275f, 1.0f, -0.652f, 0.0f, -0.031f, 0.285f, 1.0f, -0.65f, 0.0f, -0.034f, 0.297f, 1.0f, -0.647f, 0.0f, -0.037f, 0.309f, 1.0f, + -0.644f, 0.0f, -0.04f, 0.322f, 1.0f, -0.642f, 0.0f, -0.043f, 0.335f, 1.0f, -0.639f, 0.0f, -0.046f, 0.348f, 1.0f, -0.636f, 0.0f, -0.049f, 0.361f, 1.0f, + -0.633f, 0.0f, -0.052f, 0.374f, 1.0f, -0.63f, 0.0f, -0.055f, 0.386f, 1.0f, -0.627f, 0.0f, -0.058f, 0.397f, 1.0f, -0.624f, 0.0f, -0.061f, 0.408f, 1.0f, + -0.62f, 0.0f, -0.064f, 0.418f, 1.0f, -0.617f, 0.0f, -0.067f, 0.427f, 1.0f, -0.614f, 0.0f, -0.07f, 0.435f, 1.0f, -0.611f, 0.0f, -0.073f, 0.443f, 1.0f, + -0.607f, 0.0f, -0.075f, 0.451f, 1.0f, -0.604f, 0.0f, -0.078f, 0.458f, 1.0f, -0.6f, 0.0f, -0.081f, 0.465f, 1.0f, -0.597f, 0.0f, -0.084f, 0.472f, 1.0f, + -0.593f, 0.0f, -0.086f, 0.479f, 1.0f, -0.59f, 0.0f, -0.089f, 0.486f, 1.0f, -0.586f, 0.0f, -0.092f, 0.492f, 1.0f, -0.583f, 0.0f, -0.094f, 0.499f, 1.0f, + -0.579f, 0.0f, -0.097f, 0.505f, 1.0f, -0.575f, 0.0f, -0.099f, 0.512f, 1.0f, -0.571f, 0.0f, -0.102f, 0.518f, 1.0f, -0.567f, 0.0f, -0.105f, 0.524f, 1.0f, + -0.563f, 0.0f, -0.107f, 0.53f, 1.0f, -0.559f, 0.0f, -0.11f, 0.536f, 1.0f, -0.555f, 0.0f, -0.112f, 0.541f, 1.0f, -0.551f, 0.0f, -0.115f, 0.546f, 1.0f, + -0.546f, 0.0f, -0.117f, 0.551f, 1.0f, -0.542f, 0.0f, -0.12f, 0.555f, 1.0f, -0.538f, 0.0f, -0.122f, 0.559f, 1.0f, -0.533f, 0.0f, -0.125f, 0.562f, 1.0f, + -0.529f, 0.0f, -0.127f, 0.565f, 1.0f, -0.525f, 0.0f, -0.129f, 0.568f, 1.0f, -0.52f, 0.0f, -0.132f, 0.57f, 1.0f, -0.516f, 0.0f, -0.134f, 0.572f, 1.0f, + -0.512f, 0.0f, -0.137f, 0.574f, 1.0f, -0.508f, 0.0f, -0.139f, 0.576f, 1.0f, -0.503f, 0.0f, -0.141f, 0.577f, 1.0f, -0.499f, 0.0f, -0.144f, 0.578f, 1.0f, + -0.495f, 0.0f, -0.146f, 0.579f, 1.0f, -0.491f, 0.0f, -0.148f, 0.579f, 1.0f, -0.487f, 0.0f, -0.151f, 0.578f, 1.0f, -0.483f, 0.0f, -0.153f, 0.577f, 1.0f, + -0.479f, 0.0f, -0.155f, 0.574f, 1.0f, -0.475f, 0.0f, -0.158f, 0.571f, 1.0f, -0.471f, 0.0f, -0.16f, 0.567f, 1.0f, -0.467f, 0.0f, -0.162f, 0.561f, 1.0f, + -0.463f, 0.0f, -0.165f, 0.555f, 1.0f, -0.459f, 0.0f, -0.167f, 0.548f, 1.0f, -0.456f, 0.0f, -0.169f, 0.54f, 1.0f, -0.452f, 0.0f, -0.172f, 0.532f, 1.0f, + -0.448f, 0.0f, -0.174f, 0.523f, 1.0f, -0.445f, 0.0f, -0.176f, 0.514f, 1.0f, -0.441f, 0.0f, -0.179f, 0.505f, 1.0f, -0.438f, 0.0f, -0.181f, 0.497f, 1.0f, + -0.435f, 0.0f, -0.183f, 0.488f, 1.0f, -0.431f, 0.0f, -0.185f, 0.48f, 1.0f, -0.428f, 0.0f, -0.188f, 0.472f, 1.0f, -0.425f, 0.0f, -0.19f, 0.464f, 1.0f, + -0.422f, 0.0f, -0.192f, 0.457f, 1.0f, -0.419f, 0.0f, -0.194f, 0.451f, 1.0f, -0.416f, 0.0f, -0.196f, 0.444f, 1.0f, -0.413f, 0.0f, -0.198f, 0.439f, 1.0f, + -0.41f, 0.0f, -0.2f, 0.434f, 1.0f, -0.407f, 0.0f, -0.202f, 0.429f, 1.0f, -0.404f, 0.0f, -0.204f, 0.426f, 1.0f, -0.401f, 0.0f, -0.206f, 0.422f, 1.0f, + -0.398f, 0.0f, -0.208f, 0.419f, 1.0f, -0.396f, 0.0f, -0.21f, 0.417f, 1.0f, -0.393f, 0.0f, -0.212f, 0.415f, 1.0f, -0.39f, 0.0f, -0.213f, 0.413f, 1.0f, + -0.388f, 0.0f, -0.215f, 0.412f, 1.0f, -0.385f, 0.0f, -0.217f, 0.411f, 1.0f, -0.382f, 0.0f, -0.219f, 0.41f, 1.0f, -0.38f, 0.0f, -0.221f, 0.41f, 1.0f, + -0.377f, 0.0f, -0.222f, 0.409f, 1.0f, -0.375f, 0.0f, -0.224f, 0.409f, 1.0f, -0.372f, 0.0f, -0.226f, 0.409f, 1.0f, -0.37f, 0.0f, -0.228f, 0.409f, 1.0f, + -0.367f, 0.0f, -0.229f, 0.409f, 1.0f, -0.365f, 0.0f, -0.231f, 0.409f, 1.0f, -0.362f, 0.0f, -0.233f, 0.409f, 1.0f, -0.36f, 0.0f, -0.235f, 0.409f, 1.0f, + -0.357f, 0.0f, -0.236f, 0.409f, 1.0f, -0.355f, 0.0f, -0.238f, 0.409f, 1.0f, -0.352f, 0.0f, -0.24f, 0.408f, 1.0f, -0.35f, 0.0f, -0.242f, 0.408f, 1.0f, + -0.348f, 0.0f, -0.243f, 0.407f, 1.0f, -0.345f, 0.0f, -0.245f, 0.406f, 1.0f, -0.343f, 0.0f, -0.247f, 0.405f, 1.0f, -0.34f, 0.0f, -0.249f, 0.404f, 1.0f, + -0.338f, 0.0f, -0.251f, 0.403f, 1.0f, -0.336f, 0.0f, -0.253f, 0.401f, 1.0f, -0.333f, 0.0f, -0.255f, 0.399f, 1.0f, -0.331f, 0.0f, -0.256f, 0.397f, 1.0f, + -0.329f, 0.0f, -0.258f, 0.394f, 1.0f, -0.327f, 0.0f, -0.26f, 0.391f, 1.0f, -0.324f, 0.0f, -0.262f, 0.387f, 1.0f, -0.322f, 0.0f, -0.264f, 0.383f, 1.0f, + -0.32f, 0.0f, -0.266f, 0.379f, 1.0f, -0.318f, 0.0f, -0.268f, 0.374f, 1.0f, -0.316f, 0.0f, -0.27f, 0.368f, 1.0f, -0.314f, 0.0f, -0.272f, 0.362f, 1.0f, + -0.312f, 0.0f, -0.275f, 0.356f, 1.0f, -0.309f, 0.0f, -0.277f, 0.349f, 1.0f, -0.307f, 0.0f, -0.279f, 0.341f, 1.0f, -0.305f, 0.0f, -0.281f, 0.333f, 1.0f, + -0.303f, 0.0f, -0.283f, 0.325f, 1.0f, -0.301f, 0.0f, -0.286f, 0.316f, 1.0f, -0.299f, 0.0f, -0.288f, 0.307f, 1.0f, -0.297f, 0.0f, -0.29f, 0.298f, 1.0f, + -0.295f, 0.0f, -0.293f, 0.289f, 1.0f, -0.293f, 0.0f, -0.295f, 0.279f, 1.0f, -0.291f, 0.0f, -0.298f, 0.269f, 1.0f, -0.29f, 0.0f, -0.3f, 0.259f, 1.0f, + -0.288f, 0.0f, -0.303f, 0.249f, 1.0f, -0.286f, 0.0f, -0.306f, 0.238f, 1.0f, -0.284f, 0.0f, -0.308f, 0.227f, 1.0f, -0.282f, 0.0f, -0.311f, 0.215f, 1.0f, + -0.28f, 0.0f, -0.314f, 0.203f, 1.0f, -0.278f, 0.0f, -0.317f, 0.191f, 1.0f, -0.277f, 0.0f, -0.32f, 0.178f, 1.0f, -0.275f, 0.0f, -0.323f, 0.165f, 1.0f, + -0.273f, 0.0f, -0.326f, 0.151f, 1.0f, -0.271f, 0.0f, -0.33f, 0.138f, 1.0f, -0.27f, 0.0f, -0.333f, 0.124f, 1.0f, -0.268f, 0.0f, -0.336f, 0.11f, 1.0f, + -0.267f, 0.0f, -0.34f, 0.097f, 1.0f, -0.265f, 0.0f, -0.343f, 0.085f, 1.0f, -0.264f, 0.0f, -0.346f, 0.073f, 1.0f, -0.262f, 0.0f, -0.35f, 0.062f, 1.0f, + -0.261f, 0.0f, -0.353f, 0.052f, 1.0f, -0.259f, 0.0f, -0.357f, 0.043f, 1.0f, -0.258f, 0.0f, -0.36f, 0.035f, 1.0f, -0.257f, 0.0f, -0.363f, 0.028f, 1.0f, + -0.255f, 0.0f, -0.366f, 0.021f, 1.0f, -0.254f, 0.0f, -0.369f, 0.016f, 1.0f, -0.253f, 0.0f, -0.372f, 0.01f, 1.0f, -0.252f, 0.0f, -0.375f, 0.006f, 1.0f, + -0.251f, 0.0f, -0.379f, 0.0f, 1.0f, +}; + +static const float data24[133 * GP_PRIM_DATABUF_SIZE] = { + 0.233f, 0.0f, -0.376f, 0.021f, 1.0f, 0.234f, 0.0f, -0.372f, 0.08f, 1.0f, 0.234f, 0.0f, -0.369f, 0.116f, 1.0f, 0.234f, 0.0f, -0.366f, 0.156f, 1.0f, + 0.235f, 0.0f, -0.362f, 0.191f, 1.0f, 0.236f, 0.0f, -0.359f, 0.222f, 1.0f, 0.236f, 0.0f, -0.356f, 0.248f, 1.0f, 0.237f, 0.0f, -0.353f, 0.27f, 1.0f, + 0.238f, 0.0f, -0.35f, 0.289f, 1.0f, 0.239f, 0.0f, -0.346f, 0.304f, 1.0f, 0.24f, 0.0f, -0.343f, 0.319f, 1.0f, 0.241f, 0.0f, -0.34f, 0.334f, 1.0f, + 0.242f, 0.0f, -0.337f, 0.35f, 1.0f, 0.243f, 0.0f, -0.335f, 0.367f, 1.0f, 0.244f, 0.0f, -0.332f, 0.385f, 1.0f, 0.245f, 0.0f, -0.329f, 0.401f, 1.0f, + 0.247f, 0.0f, -0.327f, 0.415f, 1.0f, 0.248f, 0.0f, -0.324f, 0.426f, 1.0f, 0.249f, 0.0f, -0.322f, 0.435f, 1.0f, 0.251f, 0.0f, -0.32f, 0.443f, 1.0f, + 0.252f, 0.0f, -0.318f, 0.449f, 1.0f, 0.254f, 0.0f, -0.316f, 0.455f, 1.0f, 0.255f, 0.0f, -0.314f, 0.461f, 1.0f, 0.257f, 0.0f, -0.312f, 0.467f, 1.0f, + 0.258f, 0.0f, -0.311f, 0.474f, 1.0f, 0.26f, 0.0f, -0.309f, 0.48f, 1.0f, 0.262f, 0.0f, -0.307f, 0.487f, 1.0f, 0.263f, 0.0f, -0.305f, 0.493f, 1.0f, + 0.265f, 0.0f, -0.303f, 0.499f, 1.0f, 0.267f, 0.0f, -0.3f, 0.505f, 1.0f, 0.269f, 0.0f, -0.298f, 0.511f, 1.0f, 0.271f, 0.0f, -0.296f, 0.518f, 1.0f, + 0.273f, 0.0f, -0.294f, 0.524f, 1.0f, 0.276f, 0.0f, -0.291f, 0.531f, 1.0f, 0.278f, 0.0f, -0.289f, 0.539f, 1.0f, 0.281f, 0.0f, -0.287f, 0.546f, 1.0f, + 0.283f, 0.0f, -0.284f, 0.552f, 1.0f, 0.286f, 0.0f, -0.281f, 0.557f, 1.0f, 0.289f, 0.0f, -0.279f, 0.561f, 1.0f, 0.292f, 0.0f, -0.276f, 0.565f, 1.0f, + 0.294f, 0.0f, -0.274f, 0.568f, 1.0f, 0.297f, 0.0f, -0.271f, 0.57f, 1.0f, 0.3f, 0.0f, -0.269f, 0.572f, 1.0f, 0.303f, 0.0f, -0.267f, 0.574f, 1.0f, + 0.306f, 0.0f, -0.264f, 0.575f, 1.0f, 0.308f, 0.0f, -0.262f, 0.576f, 1.0f, 0.311f, 0.0f, -0.26f, 0.577f, 1.0f, 0.314f, 0.0f, -0.257f, 0.578f, 1.0f, + 0.316f, 0.0f, -0.255f, 0.578f, 1.0f, 0.319f, 0.0f, -0.253f, 0.579f, 1.0f, 0.322f, 0.0f, -0.25f, 0.579f, 1.0f, 0.325f, 0.0f, -0.248f, 0.58f, 1.0f, + 0.328f, 0.0f, -0.246f, 0.58f, 1.0f, 0.331f, 0.0f, -0.243f, 0.58f, 1.0f, 0.334f, 0.0f, -0.241f, 0.58f, 1.0f, 0.337f, 0.0f, -0.239f, 0.58f, 1.0f, + 0.341f, 0.0f, -0.236f, 0.58f, 1.0f, 0.344f, 0.0f, -0.233f, 0.581f, 1.0f, 0.348f, 0.0f, -0.231f, 0.581f, 1.0f, 0.352f, 0.0f, -0.228f, 0.581f, 1.0f, + 0.356f, 0.0f, -0.225f, 0.582f, 1.0f, 0.36f, 0.0f, -0.222f, 0.582f, 1.0f, 0.365f, 0.0f, -0.219f, 0.582f, 1.0f, 0.369f, 0.0f, -0.216f, 0.582f, 1.0f, + 0.374f, 0.0f, -0.214f, 0.582f, 1.0f, 0.378f, 0.0f, -0.211f, 0.582f, 1.0f, 0.383f, 0.0f, -0.208f, 0.583f, 1.0f, 0.387f, 0.0f, -0.205f, 0.583f, 1.0f, + 0.392f, 0.0f, -0.202f, 0.583f, 1.0f, 0.397f, 0.0f, -0.199f, 0.583f, 1.0f, 0.401f, 0.0f, -0.197f, 0.583f, 1.0f, 0.406f, 0.0f, -0.194f, 0.583f, 1.0f, + 0.411f, 0.0f, -0.191f, 0.583f, 1.0f, 0.416f, 0.0f, -0.188f, 0.583f, 1.0f, 0.42f, 0.0f, -0.186f, 0.583f, 1.0f, 0.425f, 0.0f, -0.183f, 0.583f, 1.0f, + 0.43f, 0.0f, -0.18f, 0.583f, 1.0f, 0.434f, 0.0f, -0.178f, 0.583f, 1.0f, 0.439f, 0.0f, -0.175f, 0.583f, 1.0f, 0.444f, 0.0f, -0.172f, 0.583f, 1.0f, + 0.449f, 0.0f, -0.17f, 0.584f, 1.0f, 0.453f, 0.0f, -0.167f, 0.584f, 1.0f, 0.458f, 0.0f, -0.164f, 0.584f, 1.0f, 0.463f, 0.0f, -0.161f, 0.585f, 1.0f, + 0.468f, 0.0f, -0.158f, 0.585f, 1.0f, 0.473f, 0.0f, -0.155f, 0.585f, 1.0f, 0.478f, 0.0f, -0.152f, 0.585f, 1.0f, 0.483f, 0.0f, -0.149f, 0.585f, 1.0f, + 0.488f, 0.0f, -0.146f, 0.585f, 1.0f, 0.492f, 0.0f, -0.143f, 0.585f, 1.0f, 0.497f, 0.0f, -0.14f, 0.586f, 1.0f, 0.501f, 0.0f, -0.137f, 0.586f, 1.0f, + 0.506f, 0.0f, -0.134f, 0.586f, 1.0f, 0.51f, 0.0f, -0.13f, 0.586f, 1.0f, 0.515f, 0.0f, -0.127f, 0.586f, 1.0f, 0.52f, 0.0f, -0.124f, 0.586f, 1.0f, + 0.524f, 0.0f, -0.12f, 0.586f, 1.0f, 0.529f, 0.0f, -0.117f, 0.586f, 1.0f, 0.534f, 0.0f, -0.113f, 0.586f, 1.0f, 0.539f, 0.0f, -0.109f, 0.586f, 1.0f, + 0.544f, 0.0f, -0.105f, 0.586f, 1.0f, 0.55f, 0.0f, -0.1f, 0.586f, 1.0f, 0.555f, 0.0f, -0.095f, 0.586f, 1.0f, 0.561f, 0.0f, -0.09f, 0.586f, 1.0f, + 0.567f, 0.0f, -0.084f, 0.587f, 1.0f, 0.573f, 0.0f, -0.078f, 0.587f, 1.0f, 0.579f, 0.0f, -0.071f, 0.587f, 1.0f, 0.586f, 0.0f, -0.063f, 0.588f, 1.0f, + 0.593f, 0.0f, -0.055f, 0.588f, 1.0f, 0.6f, 0.0f, -0.047f, 0.588f, 1.0f, 0.607f, 0.0f, -0.038f, 0.589f, 1.0f, 0.614f, 0.0f, -0.028f, 0.589f, 1.0f, + 0.621f, 0.0f, -0.018f, 0.589f, 1.0f, 0.629f, 0.0f, -0.007f, 0.589f, 1.0f, 0.636f, 0.0f, 0.004f, 0.589f, 1.0f, 0.643f, 0.0f, 0.015f, 0.59f, 1.0f, + 0.65f, 0.0f, 0.026f, 0.589f, 1.0f, 0.656f, 0.0f, 0.038f, 0.589f, 1.0f, 0.663f, 0.0f, 0.049f, 0.588f, 1.0f, 0.669f, 0.0f, 0.06f, 0.587f, 1.0f, + 0.676f, 0.0f, 0.072f, 0.584f, 1.0f, 0.682f, 0.0f, 0.084f, 0.579f, 1.0f, 0.688f, 0.0f, 0.096f, 0.571f, 1.0f, 0.694f, 0.0f, 0.108f, 0.558f, 1.0f, + 0.7f, 0.0f, 0.12f, 0.54f, 1.0f, 0.706f, 0.0f, 0.133f, 0.514f, 1.0f, 0.712f, 0.0f, 0.145f, 0.478f, 1.0f, 0.718f, 0.0f, 0.158f, 0.431f, 1.0f, + 0.723f, 0.0f, 0.17f, 0.369f, 1.0f, 0.728f, 0.0f, 0.182f, 0.294f, 1.0f, 0.733f, 0.0f, 0.194f, 0.205f, 1.0f, 0.737f, 0.0f, 0.204f, 0.125f, 1.0f, + 0.743f, 0.0f, 0.218f, 0.0f, 1.0f, +}; + +static const float data25[389 * GP_PRIM_DATABUF_SIZE] = { + -0.284f, 0.0f, -0.444f, 0.0f, 1.0f, -0.285f, 0.0f, -0.448f, 0.0f, 1.0f, -0.285f, 0.0f, -0.45f, 0.0f, 1.0f, -0.286f, 0.0f, -0.454f, 0.0f, 1.0f, + -0.286f, 0.0f, -0.457f, 0.0f, 1.0f, -0.287f, 0.0f, -0.46f, 0.0f, 1.0f, -0.288f, 0.0f, -0.463f, 0.0f, 1.0f, -0.289f, 0.0f, -0.466f, 0.0f, 1.0f, + -0.289f, 0.0f, -0.47f, 0.0f, 1.0f, -0.29f, 0.0f, -0.473f, 0.0f, 1.0f, -0.291f, 0.0f, -0.476f, 0.0f, 1.0f, -0.292f, 0.0f, -0.48f, 0.0f, 1.0f, + -0.293f, 0.0f, -0.484f, 0.0f, 1.0f, -0.294f, 0.0f, -0.487f, 0.0f, 1.0f, -0.295f, 0.0f, -0.491f, 0.0f, 1.0f, -0.296f, 0.0f, -0.494f, 0.0f, 1.0f, + -0.297f, 0.0f, -0.498f, 0.0f, 1.0f, -0.298f, 0.0f, -0.502f, 0.0f, 1.0f, -0.299f, 0.0f, -0.505f, 0.0f, 1.0f, -0.3f, 0.0f, -0.509f, 0.0f, 1.0f, + -0.301f, 0.0f, -0.513f, 0.0f, 1.0f, -0.302f, 0.0f, -0.517f, 0.0f, 1.0f, -0.303f, 0.0f, -0.52f, 0.0f, 1.0f, -0.304f, 0.0f, -0.524f, 0.0f, 1.0f, + -0.305f, 0.0f, -0.528f, 0.0f, 1.0f, -0.306f, 0.0f, -0.532f, 0.0f, 1.0f, -0.307f, 0.0f, -0.535f, 0.0f, 1.0f, -0.308f, 0.0f, -0.539f, 0.0f, 1.0f, + -0.309f, 0.0f, -0.543f, 0.0f, 1.0f, -0.31f, 0.0f, -0.547f, 0.0f, 1.0f, -0.311f, 0.0f, -0.55f, 0.0f, 1.0f, -0.312f, 0.0f, -0.554f, 0.0f, 1.0f, + -0.313f, 0.0f, -0.558f, 0.0f, 1.0f, -0.314f, 0.0f, -0.562f, 0.0f, 1.0f, -0.315f, 0.0f, -0.565f, 0.0f, 1.0f, -0.316f, 0.0f, -0.569f, 0.0f, 1.0f, + -0.317f, 0.0f, -0.573f, 0.0f, 1.0f, -0.318f, 0.0f, -0.576f, 0.0f, 1.0f, -0.319f, 0.0f, -0.58f, 0.0f, 1.0f, -0.32f, 0.0f, -0.583f, 0.0f, 1.0f, + -0.321f, 0.0f, -0.587f, 0.0f, 1.0f, -0.322f, 0.0f, -0.591f, 0.0f, 1.0f, -0.323f, 0.0f, -0.594f, 0.0f, 1.0f, -0.323f, 0.0f, -0.598f, 0.0f, 1.0f, + -0.324f, 0.0f, -0.601f, 0.0f, 1.0f, -0.325f, 0.0f, -0.605f, 0.0f, 1.0f, -0.326f, 0.0f, -0.608f, 0.0f, 1.0f, -0.326f, 0.0f, -0.612f, 0.0f, 1.0f, + -0.327f, 0.0f, -0.615f, 0.0f, 1.0f, -0.328f, 0.0f, -0.619f, 0.0f, 1.0f, -0.328f, 0.0f, -0.622f, 0.0f, 1.0f, -0.329f, 0.0f, -0.625f, 0.0f, 1.0f, + -0.33f, 0.0f, -0.629f, 0.0f, 1.0f, -0.33f, 0.0f, -0.632f, 0.0f, 1.0f, -0.331f, 0.0f, -0.635f, 0.001f, 1.0f, -0.331f, 0.0f, -0.639f, 0.001f, 1.0f, + -0.332f, 0.0f, -0.642f, 0.002f, 1.0f, -0.332f, 0.0f, -0.645f, 0.002f, 1.0f, -0.333f, 0.0f, -0.649f, 0.003f, 1.0f, -0.333f, 0.0f, -0.652f, 0.005f, 1.0f, + -0.334f, 0.0f, -0.655f, 0.006f, 1.0f, -0.334f, 0.0f, -0.658f, 0.009f, 1.0f, -0.335f, 0.0f, -0.662f, 0.011f, 1.0f, -0.335f, 0.0f, -0.665f, 0.015f, 1.0f, + -0.335f, 0.0f, -0.668f, 0.019f, 1.0f, -0.336f, 0.0f, -0.672f, 0.024f, 1.0f, -0.336f, 0.0f, -0.675f, 0.031f, 1.0f, -0.337f, 0.0f, -0.678f, 0.038f, 1.0f, + -0.337f, 0.0f, -0.682f, 0.046f, 1.0f, -0.337f, 0.0f, -0.685f, 0.056f, 1.0f, -0.338f, 0.0f, -0.689f, 0.067f, 1.0f, -0.338f, 0.0f, -0.692f, 0.079f, 1.0f, + -0.338f, 0.0f, -0.696f, 0.093f, 1.0f, -0.339f, 0.0f, -0.699f, 0.107f, 1.0f, -0.339f, 0.0f, -0.703f, 0.123f, 1.0f, -0.34f, 0.0f, -0.706f, 0.139f, 1.0f, + -0.34f, 0.0f, -0.71f, 0.157f, 1.0f, -0.34f, 0.0f, -0.714f, 0.174f, 1.0f, -0.34f, 0.0f, -0.717f, 0.193f, 1.0f, -0.341f, 0.0f, -0.721f, 0.211f, 1.0f, + -0.341f, 0.0f, -0.725f, 0.23f, 1.0f, -0.341f, 0.0f, -0.729f, 0.248f, 1.0f, -0.342f, 0.0f, -0.732f, 0.266f, 1.0f, -0.342f, 0.0f, -0.736f, 0.284f, 1.0f, + -0.342f, 0.0f, -0.74f, 0.302f, 1.0f, -0.342f, 0.0f, -0.744f, 0.318f, 1.0f, -0.342f, 0.0f, -0.748f, 0.334f, 1.0f, -0.342f, 0.0f, -0.752f, 0.349f, 1.0f, + -0.343f, 0.0f, -0.756f, 0.364f, 1.0f, -0.343f, 0.0f, -0.76f, 0.377f, 1.0f, -0.343f, 0.0f, -0.763f, 0.389f, 1.0f, -0.343f, 0.0f, -0.767f, 0.401f, 1.0f, + -0.343f, 0.0f, -0.771f, 0.411f, 1.0f, -0.343f, 0.0f, -0.775f, 0.421f, 1.0f, -0.342f, 0.0f, -0.779f, 0.429f, 1.0f, -0.342f, 0.0f, -0.783f, 0.437f, 1.0f, + -0.342f, 0.0f, -0.786f, 0.444f, 1.0f, -0.342f, 0.0f, -0.79f, 0.451f, 1.0f, -0.342f, 0.0f, -0.794f, 0.456f, 1.0f, -0.341f, 0.0f, -0.797f, 0.461f, 1.0f, + -0.341f, 0.0f, -0.801f, 0.466f, 1.0f, -0.34f, 0.0f, -0.805f, 0.469f, 1.0f, -0.34f, 0.0f, -0.808f, 0.473f, 1.0f, -0.339f, 0.0f, -0.812f, 0.476f, 1.0f, + -0.339f, 0.0f, -0.815f, 0.478f, 1.0f, -0.338f, 0.0f, -0.818f, 0.48f, 1.0f, -0.338f, 0.0f, -0.822f, 0.482f, 1.0f, -0.337f, 0.0f, -0.825f, 0.483f, 1.0f, + -0.336f, 0.0f, -0.828f, 0.484f, 1.0f, -0.335f, 0.0f, -0.831f, 0.485f, 1.0f, -0.334f, 0.0f, -0.834f, 0.486f, 1.0f, -0.333f, 0.0f, -0.837f, 0.487f, 1.0f, + -0.332f, 0.0f, -0.84f, 0.487f, 1.0f, -0.331f, 0.0f, -0.843f, 0.487f, 1.0f, -0.33f, 0.0f, -0.846f, 0.488f, 1.0f, -0.329f, 0.0f, -0.849f, 0.488f, 1.0f, + -0.328f, 0.0f, -0.852f, 0.488f, 1.0f, -0.326f, 0.0f, -0.855f, 0.488f, 1.0f, -0.325f, 0.0f, -0.857f, 0.488f, 1.0f, -0.324f, 0.0f, -0.86f, 0.488f, 1.0f, + -0.322f, 0.0f, -0.863f, 0.488f, 1.0f, -0.321f, 0.0f, -0.865f, 0.488f, 1.0f, -0.319f, 0.0f, -0.868f, 0.488f, 1.0f, -0.318f, 0.0f, -0.871f, 0.488f, 1.0f, + -0.316f, 0.0f, -0.873f, 0.489f, 1.0f, -0.314f, 0.0f, -0.876f, 0.489f, 1.0f, -0.312f, 0.0f, -0.878f, 0.489f, 1.0f, -0.311f, 0.0f, -0.881f, 0.489f, 1.0f, + -0.309f, 0.0f, -0.883f, 0.489f, 1.0f, -0.307f, 0.0f, -0.885f, 0.489f, 1.0f, -0.305f, 0.0f, -0.888f, 0.49f, 1.0f, -0.303f, 0.0f, -0.89f, 0.491f, 1.0f, + -0.301f, 0.0f, -0.892f, 0.491f, 1.0f, -0.298f, 0.0f, -0.894f, 0.492f, 1.0f, -0.296f, 0.0f, -0.897f, 0.494f, 1.0f, -0.294f, 0.0f, -0.899f, 0.495f, 1.0f, + -0.292f, 0.0f, -0.901f, 0.497f, 1.0f, -0.289f, 0.0f, -0.903f, 0.5f, 1.0f, -0.287f, 0.0f, -0.905f, 0.502f, 1.0f, -0.284f, 0.0f, -0.907f, 0.505f, 1.0f, + -0.282f, 0.0f, -0.909f, 0.509f, 1.0f, -0.279f, 0.0f, -0.912f, 0.512f, 1.0f, -0.277f, 0.0f, -0.914f, 0.517f, 1.0f, -0.274f, 0.0f, -0.916f, 0.521f, 1.0f, + -0.271f, 0.0f, -0.918f, 0.526f, 1.0f, -0.269f, 0.0f, -0.919f, 0.531f, 1.0f, -0.266f, 0.0f, -0.921f, 0.537f, 1.0f, -0.263f, 0.0f, -0.923f, 0.543f, 1.0f, + -0.26f, 0.0f, -0.925f, 0.548f, 1.0f, -0.257f, 0.0f, -0.927f, 0.554f, 1.0f, -0.255f, 0.0f, -0.929f, 0.56f, 1.0f, -0.252f, 0.0f, -0.931f, 0.566f, 1.0f, + -0.249f, 0.0f, -0.932f, 0.571f, 1.0f, -0.246f, 0.0f, -0.934f, 0.577f, 1.0f, -0.243f, 0.0f, -0.936f, 0.582f, 1.0f, -0.24f, 0.0f, -0.938f, 0.587f, 1.0f, + -0.237f, 0.0f, -0.939f, 0.592f, 1.0f, -0.234f, 0.0f, -0.941f, 0.597f, 1.0f, -0.231f, 0.0f, -0.943f, 0.601f, 1.0f, -0.228f, 0.0f, -0.944f, 0.605f, 1.0f, + -0.225f, 0.0f, -0.946f, 0.609f, 1.0f, -0.222f, 0.0f, -0.948f, 0.613f, 1.0f, -0.219f, 0.0f, -0.949f, 0.617f, 1.0f, -0.216f, 0.0f, -0.951f, 0.62f, 1.0f, + -0.213f, 0.0f, -0.953f, 0.624f, 1.0f, -0.21f, 0.0f, -0.954f, 0.627f, 1.0f, -0.207f, 0.0f, -0.956f, 0.63f, 1.0f, -0.204f, 0.0f, -0.958f, 0.633f, 1.0f, + -0.201f, 0.0f, -0.959f, 0.636f, 1.0f, -0.198f, 0.0f, -0.961f, 0.639f, 1.0f, -0.195f, 0.0f, -0.962f, 0.641f, 1.0f, -0.191f, 0.0f, -0.964f, 0.643f, 1.0f, + -0.188f, 0.0f, -0.965f, 0.646f, 1.0f, -0.185f, 0.0f, -0.967f, 0.648f, 1.0f, -0.181f, 0.0f, -0.968f, 0.649f, 1.0f, -0.178f, 0.0f, -0.969f, 0.651f, 1.0f, + -0.175f, 0.0f, -0.971f, 0.653f, 1.0f, -0.171f, 0.0f, -0.972f, 0.654f, 1.0f, -0.168f, 0.0f, -0.973f, 0.655f, 1.0f, -0.165f, 0.0f, -0.974f, 0.657f, 1.0f, + -0.161f, 0.0f, -0.976f, 0.658f, 1.0f, -0.158f, 0.0f, -0.977f, 0.659f, 1.0f, -0.154f, 0.0f, -0.978f, 0.66f, 1.0f, -0.151f, 0.0f, -0.979f, 0.661f, 1.0f, + -0.148f, 0.0f, -0.98f, 0.662f, 1.0f, -0.144f, 0.0f, -0.981f, 0.664f, 1.0f, -0.141f, 0.0f, -0.982f, 0.665f, 1.0f, -0.137f, 0.0f, -0.983f, 0.667f, 1.0f, + -0.134f, 0.0f, -0.984f, 0.669f, 1.0f, -0.13f, 0.0f, -0.985f, 0.671f, 1.0f, -0.127f, 0.0f, -0.986f, 0.673f, 1.0f, -0.124f, 0.0f, -0.987f, 0.675f, 1.0f, + -0.12f, 0.0f, -0.988f, 0.678f, 1.0f, -0.117f, 0.0f, -0.989f, 0.68f, 1.0f, -0.113f, 0.0f, -0.99f, 0.683f, 1.0f, -0.11f, 0.0f, -0.991f, 0.685f, 1.0f, + -0.107f, 0.0f, -0.992f, 0.688f, 1.0f, -0.103f, 0.0f, -0.992f, 0.691f, 1.0f, -0.1f, 0.0f, -0.993f, 0.693f, 1.0f, -0.097f, 0.0f, -0.994f, 0.696f, 1.0f, + -0.093f, 0.0f, -0.995f, 0.698f, 1.0f, -0.09f, 0.0f, -0.996f, 0.701f, 1.0f, -0.087f, 0.0f, -0.997f, 0.703f, 1.0f, -0.084f, 0.0f, -0.997f, 0.705f, 1.0f, + -0.08f, 0.0f, -0.998f, 0.707f, 1.0f, -0.077f, 0.0f, -0.999f, 0.708f, 1.0f, -0.074f, 0.0f, -1.0f, 0.71f, 1.0f, -0.07f, 0.0f, -1.0f, 0.712f, 1.0f, + -0.067f, 0.0f, -1.001f, 0.713f, 1.0f, -0.063f, 0.0f, -1.002f, 0.715f, 1.0f, -0.06f, 0.0f, -1.002f, 0.717f, 1.0f, -0.056f, 0.0f, -1.003f, 0.718f, 1.0f, + -0.053f, 0.0f, -1.003f, 0.72f, 1.0f, -0.049f, 0.0f, -1.004f, 0.723f, 1.0f, -0.045f, 0.0f, -1.004f, 0.725f, 1.0f, -0.041f, 0.0f, -1.005f, 0.728f, 1.0f, + -0.038f, 0.0f, -1.005f, 0.73f, 1.0f, -0.034f, 0.0f, -1.006f, 0.733f, 1.0f, -0.03f, 0.0f, -1.006f, 0.736f, 1.0f, -0.026f, 0.0f, -1.007f, 0.738f, 1.0f, + -0.022f, 0.0f, -1.007f, 0.741f, 1.0f, -0.018f, 0.0f, -1.007f, 0.743f, 1.0f, -0.014f, 0.0f, -1.008f, 0.746f, 1.0f, -0.01f, 0.0f, -1.008f, 0.748f, 1.0f, + -0.006f, 0.0f, -1.009f, 0.75f, 1.0f, -0.001f, 0.0f, -1.009f, 0.752f, 1.0f, 0.003f, 0.0f, -1.009f, 0.754f, 1.0f, 0.007f, 0.0f, -1.01f, 0.755f, 1.0f, + 0.011f, 0.0f, -1.01f, 0.757f, 1.0f, 0.015f, 0.0f, -1.01f, 0.758f, 1.0f, 0.02f, 0.0f, -1.011f, 0.759f, 1.0f, 0.024f, 0.0f, -1.011f, 0.76f, 1.0f, + 0.028f, 0.0f, -1.011f, 0.761f, 1.0f, 0.033f, 0.0f, -1.011f, 0.761f, 1.0f, 0.037f, 0.0f, -1.012f, 0.762f, 1.0f, 0.041f, 0.0f, -1.012f, 0.762f, 1.0f, + 0.045f, 0.0f, -1.012f, 0.763f, 1.0f, 0.05f, 0.0f, -1.012f, 0.763f, 1.0f, 0.054f, 0.0f, -1.012f, 0.764f, 1.0f, 0.058f, 0.0f, -1.013f, 0.764f, 1.0f, + 0.062f, 0.0f, -1.013f, 0.764f, 1.0f, 0.066f, 0.0f, -1.013f, 0.764f, 1.0f, 0.071f, 0.0f, -1.013f, 0.764f, 1.0f, 0.075f, 0.0f, -1.013f, 0.765f, 1.0f, + 0.079f, 0.0f, -1.013f, 0.765f, 1.0f, 0.083f, 0.0f, -1.013f, 0.765f, 1.0f, 0.087f, 0.0f, -1.013f, 0.765f, 1.0f, 0.091f, 0.0f, -1.013f, 0.765f, 1.0f, + 0.095f, 0.0f, -1.013f, 0.765f, 1.0f, 0.099f, 0.0f, -1.013f, 0.766f, 1.0f, 0.103f, 0.0f, -1.013f, 0.766f, 1.0f, 0.108f, 0.0f, -1.012f, 0.766f, 1.0f, + 0.112f, 0.0f, -1.012f, 0.766f, 1.0f, 0.116f, 0.0f, -1.012f, 0.766f, 1.0f, 0.119f, 0.0f, -1.012f, 0.767f, 1.0f, 0.123f, 0.0f, -1.011f, 0.767f, 1.0f, + 0.127f, 0.0f, -1.011f, 0.767f, 1.0f, 0.131f, 0.0f, -1.01f, 0.767f, 1.0f, 0.135f, 0.0f, -1.01f, 0.767f, 1.0f, 0.139f, 0.0f, -1.009f, 0.768f, 1.0f, + 0.143f, 0.0f, -1.009f, 0.768f, 1.0f, 0.147f, 0.0f, -1.008f, 0.768f, 1.0f, 0.151f, 0.0f, -1.007f, 0.769f, 1.0f, 0.154f, 0.0f, -1.007f, 0.769f, 1.0f, + 0.158f, 0.0f, -1.006f, 0.769f, 1.0f, 0.162f, 0.0f, -1.005f, 0.769f, 1.0f, 0.166f, 0.0f, -1.004f, 0.77f, 1.0f, 0.17f, 0.0f, -1.003f, 0.77f, 1.0f, + 0.173f, 0.0f, -1.003f, 0.77f, 1.0f, 0.177f, 0.0f, -1.002f, 0.771f, 1.0f, 0.181f, 0.0f, -1.001f, 0.771f, 1.0f, 0.184f, 0.0f, -1.0f, 0.772f, 1.0f, + 0.188f, 0.0f, -0.999f, 0.772f, 1.0f, 0.192f, 0.0f, -0.998f, 0.773f, 1.0f, 0.195f, 0.0f, -0.997f, 0.773f, 1.0f, 0.199f, 0.0f, -0.996f, 0.774f, 1.0f, + 0.202f, 0.0f, -0.995f, 0.774f, 1.0f, 0.206f, 0.0f, -0.994f, 0.775f, 1.0f, 0.209f, 0.0f, -0.993f, 0.776f, 1.0f, 0.213f, 0.0f, -0.992f, 0.776f, 1.0f, + 0.216f, 0.0f, -0.991f, 0.777f, 1.0f, 0.22f, 0.0f, -0.99f, 0.777f, 1.0f, 0.223f, 0.0f, -0.988f, 0.778f, 1.0f, 0.227f, 0.0f, -0.987f, 0.778f, 1.0f, + 0.23f, 0.0f, -0.986f, 0.778f, 1.0f, 0.233f, 0.0f, -0.985f, 0.779f, 1.0f, 0.237f, 0.0f, -0.983f, 0.779f, 1.0f, 0.24f, 0.0f, -0.982f, 0.779f, 1.0f, + 0.243f, 0.0f, -0.981f, 0.779f, 1.0f, 0.246f, 0.0f, -0.979f, 0.778f, 1.0f, 0.249f, 0.0f, -0.978f, 0.778f, 1.0f, 0.252f, 0.0f, -0.976f, 0.777f, 1.0f, + 0.255f, 0.0f, -0.975f, 0.777f, 1.0f, 0.258f, 0.0f, -0.973f, 0.776f, 1.0f, 0.261f, 0.0f, -0.972f, 0.775f, 1.0f, 0.264f, 0.0f, -0.97f, 0.773f, 1.0f, + 0.267f, 0.0f, -0.968f, 0.772f, 1.0f, 0.269f, 0.0f, -0.967f, 0.77f, 1.0f, 0.272f, 0.0f, -0.965f, 0.769f, 1.0f, 0.275f, 0.0f, -0.963f, 0.767f, 1.0f, + 0.277f, 0.0f, -0.961f, 0.765f, 1.0f, 0.279f, 0.0f, -0.959f, 0.763f, 1.0f, 0.282f, 0.0f, -0.957f, 0.761f, 1.0f, 0.284f, 0.0f, -0.955f, 0.759f, 1.0f, + 0.286f, 0.0f, -0.953f, 0.756f, 1.0f, 0.288f, 0.0f, -0.951f, 0.754f, 1.0f, 0.29f, 0.0f, -0.948f, 0.752f, 1.0f, 0.292f, 0.0f, -0.946f, 0.749f, 1.0f, + 0.294f, 0.0f, -0.944f, 0.746f, 1.0f, 0.296f, 0.0f, -0.941f, 0.744f, 1.0f, 0.298f, 0.0f, -0.939f, 0.741f, 1.0f, 0.3f, 0.0f, -0.937f, 0.738f, 1.0f, + 0.302f, 0.0f, -0.934f, 0.736f, 1.0f, 0.303f, 0.0f, -0.932f, 0.733f, 1.0f, 0.305f, 0.0f, -0.929f, 0.73f, 1.0f, 0.306f, 0.0f, -0.926f, 0.727f, 1.0f, + 0.308f, 0.0f, -0.924f, 0.724f, 1.0f, 0.309f, 0.0f, -0.921f, 0.721f, 1.0f, 0.311f, 0.0f, -0.918f, 0.719f, 1.0f, 0.312f, 0.0f, -0.916f, 0.716f, 1.0f, + 0.313f, 0.0f, -0.913f, 0.713f, 1.0f, 0.315f, 0.0f, -0.91f, 0.71f, 1.0f, 0.316f, 0.0f, -0.907f, 0.707f, 1.0f, 0.317f, 0.0f, -0.904f, 0.704f, 1.0f, + 0.318f, 0.0f, -0.901f, 0.7f, 1.0f, 0.319f, 0.0f, -0.898f, 0.697f, 1.0f, 0.32f, 0.0f, -0.895f, 0.693f, 1.0f, 0.321f, 0.0f, -0.892f, 0.69f, 1.0f, + 0.322f, 0.0f, -0.889f, 0.686f, 1.0f, 0.323f, 0.0f, -0.886f, 0.681f, 1.0f, 0.324f, 0.0f, -0.883f, 0.677f, 1.0f, 0.325f, 0.0f, -0.88f, 0.672f, 1.0f, + 0.326f, 0.0f, -0.876f, 0.667f, 1.0f, 0.326f, 0.0f, -0.873f, 0.661f, 1.0f, 0.327f, 0.0f, -0.87f, 0.655f, 1.0f, 0.328f, 0.0f, -0.867f, 0.649f, 1.0f, + 0.329f, 0.0f, -0.864f, 0.643f, 1.0f, 0.329f, 0.0f, -0.861f, 0.637f, 1.0f, 0.33f, 0.0f, -0.857f, 0.63f, 1.0f, 0.331f, 0.0f, -0.854f, 0.624f, 1.0f, + 0.331f, 0.0f, -0.851f, 0.618f, 1.0f, 0.332f, 0.0f, -0.848f, 0.613f, 1.0f, 0.333f, 0.0f, -0.845f, 0.607f, 1.0f, 0.333f, 0.0f, -0.841f, 0.603f, 1.0f, + 0.334f, 0.0f, -0.838f, 0.598f, 1.0f, 0.334f, 0.0f, -0.835f, 0.594f, 1.0f, 0.335f, 0.0f, -0.832f, 0.591f, 1.0f, 0.335f, 0.0f, -0.828f, 0.588f, 1.0f, + 0.335f, 0.0f, -0.825f, 0.586f, 1.0f, 0.336f, 0.0f, -0.821f, 0.584f, 1.0f, 0.336f, 0.0f, -0.818f, 0.582f, 1.0f, 0.336f, 0.0f, -0.814f, 0.581f, 1.0f, + 0.337f, 0.0f, -0.811f, 0.58f, 1.0f, 0.337f, 0.0f, -0.807f, 0.58f, 1.0f, 0.337f, 0.0f, -0.803f, 0.579f, 1.0f, 0.337f, 0.0f, -0.799f, 0.579f, 1.0f, + 0.337f, 0.0f, -0.795f, 0.578f, 1.0f, 0.337f, 0.0f, -0.79f, 0.578f, 1.0f, 0.337f, 0.0f, -0.786f, 0.578f, 1.0f, 0.338f, 0.0f, -0.782f, 0.577f, 1.0f, + 0.338f, 0.0f, -0.777f, 0.576f, 1.0f, 0.337f, 0.0f, -0.772f, 0.574f, 1.0f, 0.337f, 0.0f, -0.767f, 0.572f, 1.0f, 0.337f, 0.0f, -0.762f, 0.569f, 1.0f, + 0.337f, 0.0f, -0.756f, 0.565f, 1.0f, 0.337f, 0.0f, -0.751f, 0.559f, 1.0f, 0.337f, 0.0f, -0.745f, 0.553f, 1.0f, 0.336f, 0.0f, -0.739f, 0.544f, 1.0f, + 0.336f, 0.0f, -0.732f, 0.534f, 1.0f, 0.335f, 0.0f, -0.725f, 0.521f, 1.0f, 0.334f, 0.0f, -0.718f, 0.505f, 1.0f, 0.333f, 0.0f, -0.711f, 0.487f, 1.0f, + 0.332f, 0.0f, -0.703f, 0.466f, 1.0f, 0.331f, 0.0f, -0.694f, 0.441f, 1.0f, 0.33f, 0.0f, -0.686f, 0.413f, 1.0f, 0.328f, 0.0f, -0.677f, 0.383f, 1.0f, + 0.326f, 0.0f, -0.667f, 0.35f, 1.0f, 0.325f, 0.0f, -0.657f, 0.316f, 1.0f, 0.323f, 0.0f, -0.647f, 0.281f, 1.0f, 0.32f, 0.0f, -0.636f, 0.246f, 1.0f, + 0.318f, 0.0f, -0.625f, 0.212f, 1.0f, 0.316f, 0.0f, -0.614f, 0.18f, 1.0f, 0.313f, 0.0f, -0.603f, 0.149f, 1.0f, 0.311f, 0.0f, -0.592f, 0.12f, 1.0f, + 0.308f, 0.0f, -0.581f, 0.093f, 1.0f, 0.306f, 0.0f, -0.57f, 0.069f, 1.0f, 0.303f, 0.0f, -0.559f, 0.046f, 1.0f, 0.301f, 0.0f, -0.55f, 0.027f, 1.0f, + 0.298f, 0.0f, -0.537f, 0.0f, 1.0f, +}; + +static const float data26[41 * GP_PRIM_DATABUF_SIZE] = { + -0.104f, 0.0f, -0.795f, 0.258f, 1.0f, -0.1f, 0.0f, -0.799f, 0.28f, 1.0f, -0.097f, 0.0f, -0.801f, 0.294f, 1.0f, -0.094f, 0.0f, -0.805f, 0.312f, 1.0f, + -0.09f, 0.0f, -0.808f, 0.328f, 1.0f, -0.086f, 0.0f, -0.811f, 0.345f, 1.0f, -0.082f, 0.0f, -0.815f, 0.361f, 1.0f, -0.078f, 0.0f, -0.818f, 0.377f, 1.0f, + -0.073f, 0.0f, -0.821f, 0.392f, 1.0f, -0.068f, 0.0f, -0.824f, 0.407f, 1.0f, -0.063f, 0.0f, -0.827f, 0.421f, 1.0f, -0.057f, 0.0f, -0.83f, 0.435f, 1.0f, + -0.051f, 0.0f, -0.833f, 0.448f, 1.0f, -0.045f, 0.0f, -0.835f, 0.46f, 1.0f, -0.039f, 0.0f, -0.837f, 0.471f, 1.0f, -0.033f, 0.0f, -0.839f, 0.481f, 1.0f, + -0.026f, 0.0f, -0.841f, 0.491f, 1.0f, -0.019f, 0.0f, -0.842f, 0.5f, 1.0f, -0.012f, 0.0f, -0.843f, 0.508f, 1.0f, -0.005f, 0.0f, -0.843f, 0.515f, 1.0f, + 0.002f, 0.0f, -0.843f, 0.522f, 1.0f, 0.009f, 0.0f, -0.843f, 0.527f, 1.0f, 0.016f, 0.0f, -0.842f, 0.532f, 1.0f, 0.023f, 0.0f, -0.841f, 0.535f, 1.0f, + 0.03f, 0.0f, -0.839f, 0.538f, 1.0f, 0.037f, 0.0f, -0.837f, 0.538f, 1.0f, 0.044f, 0.0f, -0.835f, 0.537f, 1.0f, 0.05f, 0.0f, -0.833f, 0.532f, 1.0f, + 0.056f, 0.0f, -0.83f, 0.524f, 1.0f, 0.062f, 0.0f, -0.827f, 0.513f, 1.0f, 0.068f, 0.0f, -0.823f, 0.496f, 1.0f, 0.074f, 0.0f, -0.82f, 0.474f, 1.0f, + 0.079f, 0.0f, -0.817f, 0.446f, 1.0f, 0.084f, 0.0f, -0.813f, 0.411f, 1.0f, 0.089f, 0.0f, -0.809f, 0.37f, 1.0f, 0.093f, 0.0f, -0.806f, 0.323f, 1.0f, + 0.098f, 0.0f, -0.802f, 0.269f, 1.0f, 0.102f, 0.0f, -0.798f, 0.211f, 1.0f, 0.106f, 0.0f, -0.795f, 0.146f, 1.0f, 0.109f, 0.0f, -0.792f, 0.089f, 1.0f, + 0.114f, 0.0f, -0.787f, 0.0f, 1.0f, +}; + +static const float data27[77 * GP_PRIM_DATABUF_SIZE] = { + -0.105f, 0.0f, -0.259f, 0.214f, 1.0f, -0.103f, 0.0f, -0.253f, 0.263f, 1.0f, -0.101f, 0.0f, -0.249f, 0.291f, 1.0f, -0.099f, 0.0f, -0.244f, 0.324f, 1.0f, + -0.098f, 0.0f, -0.24f, 0.351f, 1.0f, -0.096f, 0.0f, -0.235f, 0.376f, 1.0f, -0.094f, 0.0f, -0.231f, 0.397f, 1.0f, -0.092f, 0.0f, -0.227f, 0.416f, 1.0f, + -0.09f, 0.0f, -0.222f, 0.432f, 1.0f, -0.088f, 0.0f, -0.218f, 0.446f, 1.0f, -0.086f, 0.0f, -0.215f, 0.458f, 1.0f, -0.084f, 0.0f, -0.211f, 0.469f, 1.0f, + -0.082f, 0.0f, -0.208f, 0.478f, 1.0f, -0.079f, 0.0f, -0.205f, 0.486f, 1.0f, -0.077f, 0.0f, -0.203f, 0.494f, 1.0f, -0.075f, 0.0f, -0.2f, 0.501f, 1.0f, + -0.073f, 0.0f, -0.198f, 0.508f, 1.0f, -0.071f, 0.0f, -0.197f, 0.515f, 1.0f, -0.068f, 0.0f, -0.195f, 0.521f, 1.0f, -0.066f, 0.0f, -0.194f, 0.528f, 1.0f, + -0.064f, 0.0f, -0.194f, 0.534f, 1.0f, -0.061f, 0.0f, -0.194f, 0.54f, 1.0f, -0.059f, 0.0f, -0.194f, 0.546f, 1.0f, -0.056f, 0.0f, -0.194f, 0.551f, 1.0f, + -0.054f, 0.0f, -0.195f, 0.555f, 1.0f, -0.051f, 0.0f, -0.196f, 0.559f, 1.0f, -0.049f, 0.0f, -0.198f, 0.562f, 1.0f, -0.046f, 0.0f, -0.2f, 0.565f, 1.0f, + -0.044f, 0.0f, -0.201f, 0.567f, 1.0f, -0.041f, 0.0f, -0.204f, 0.568f, 1.0f, -0.039f, 0.0f, -0.206f, 0.569f, 1.0f, -0.036f, 0.0f, -0.208f, 0.57f, 1.0f, + -0.034f, 0.0f, -0.21f, 0.571f, 1.0f, -0.032f, 0.0f, -0.213f, 0.571f, 1.0f, -0.029f, 0.0f, -0.215f, 0.571f, 1.0f, -0.027f, 0.0f, -0.217f, 0.572f, 1.0f, + -0.024f, 0.0f, -0.219f, 0.572f, 1.0f, -0.022f, 0.0f, -0.221f, 0.572f, 1.0f, -0.019f, 0.0f, -0.222f, 0.572f, 1.0f, -0.016f, 0.0f, -0.224f, 0.572f, 1.0f, + -0.013f, 0.0f, -0.225f, 0.572f, 1.0f, -0.01f, 0.0f, -0.226f, 0.573f, 1.0f, -0.007f, 0.0f, -0.227f, 0.573f, 1.0f, -0.004f, 0.0f, -0.227f, 0.573f, 1.0f, + -0.001f, 0.0f, -0.227f, 0.574f, 1.0f, 0.002f, 0.0f, -0.227f, 0.575f, 1.0f, 0.005f, 0.0f, -0.227f, 0.576f, 1.0f, 0.008f, 0.0f, -0.226f, 0.577f, 1.0f, + 0.011f, 0.0f, -0.225f, 0.578f, 1.0f, 0.015f, 0.0f, -0.224f, 0.579f, 1.0f, 0.018f, 0.0f, -0.222f, 0.58f, 1.0f, 0.021f, 0.0f, -0.221f, 0.581f, 1.0f, + 0.024f, 0.0f, -0.219f, 0.582f, 1.0f, 0.027f, 0.0f, -0.217f, 0.582f, 1.0f, 0.03f, 0.0f, -0.215f, 0.583f, 1.0f, 0.033f, 0.0f, -0.213f, 0.583f, 1.0f, + 0.036f, 0.0f, -0.212f, 0.583f, 1.0f, 0.039f, 0.0f, -0.21f, 0.583f, 1.0f, 0.042f, 0.0f, -0.208f, 0.583f, 1.0f, 0.045f, 0.0f, -0.207f, 0.583f, 1.0f, + 0.048f, 0.0f, -0.205f, 0.583f, 1.0f, 0.051f, 0.0f, -0.204f, 0.583f, 1.0f, 0.054f, 0.0f, -0.203f, 0.583f, 1.0f, 0.058f, 0.0f, -0.203f, 0.583f, 1.0f, + 0.061f, 0.0f, -0.202f, 0.583f, 1.0f, 0.064f, 0.0f, -0.202f, 0.574f, 1.0f, 0.067f, 0.0f, -0.202f, 0.565f, 1.0f, 0.07f, 0.0f, -0.203f, 0.556f, 1.0f, + 0.073f, 0.0f, -0.203f, 0.547f, 1.0f, 0.075f, 0.0f, -0.204f, 0.515f, 1.0f, 0.078f, 0.0f, -0.204f, 0.483f, 1.0f, 0.08f, 0.0f, -0.205f, 0.451f, 1.0f, + 0.083f, 0.0f, -0.206f, 0.419f, 1.0f, 0.085f, 0.0f, -0.207f, 0.314f, 1.0f, 0.087f, 0.0f, -0.208f, 0.21f, 1.0f, 0.089f, 0.0f, -0.209f, 0.105f, 1.0f, + 0.091f, 0.0f, -0.21f, 0.0f, 1.0f, +}; + +static const float data28[257 * GP_PRIM_DATABUF_SIZE] = { + -0.637f, 0.0f, -0.172f, 0.0f, 1.0f, -0.641f, 0.0f, -0.172f, 0.0f, 1.0f, -0.643f, 0.0f, -0.172f, 0.0f, 1.0f, -0.646f, 0.0f, -0.172f, 0.0f, 1.0f, + -0.65f, 0.0f, -0.172f, 0.0f, 1.0f, -0.653f, 0.0f, -0.172f, 0.0f, 1.0f, -0.657f, 0.0f, -0.172f, 0.0f, 1.0f, -0.66f, 0.0f, -0.172f, 0.0f, 1.0f, + -0.664f, 0.0f, -0.171f, 0.0f, 1.0f, -0.668f, 0.0f, -0.171f, 0.0f, 1.0f, -0.672f, 0.0f, -0.171f, 0.0f, 1.0f, -0.677f, 0.0f, -0.171f, 0.0f, 1.0f, + -0.681f, 0.0f, -0.171f, 0.0f, 1.0f, -0.685f, 0.0f, -0.171f, 0.0f, 1.0f, -0.69f, 0.0f, -0.17f, 0.0f, 1.0f, -0.694f, 0.0f, -0.17f, 0.0f, 1.0f, + -0.699f, 0.0f, -0.17f, 0.0f, 1.0f, -0.704f, 0.0f, -0.169f, 0.0f, 1.0f, -0.708f, 0.0f, -0.169f, 0.0f, 1.0f, -0.713f, 0.0f, -0.168f, 0.0f, 1.0f, + -0.717f, 0.0f, -0.168f, 0.0f, 1.0f, -0.722f, 0.0f, -0.167f, 0.0f, 1.0f, -0.727f, 0.0f, -0.167f, 0.0f, 1.0f, -0.731f, 0.0f, -0.166f, 0.0f, 1.0f, + -0.735f, 0.0f, -0.166f, 0.0f, 1.0f, -0.74f, 0.0f, -0.165f, 0.0f, 1.0f, -0.744f, 0.0f, -0.164f, 0.0f, 1.0f, -0.749f, 0.0f, -0.163f, 0.0f, 1.0f, + -0.753f, 0.0f, -0.163f, 0.0f, 1.0f, -0.757f, 0.0f, -0.162f, 0.0f, 1.0f, -0.761f, 0.0f, -0.161f, 0.0f, 1.0f, -0.765f, 0.0f, -0.16f, 0.0f, 1.0f, + -0.769f, 0.0f, -0.159f, 0.0f, 1.0f, -0.773f, 0.0f, -0.158f, 0.0f, 1.0f, -0.777f, 0.0f, -0.157f, 0.0f, 1.0f, -0.781f, 0.0f, -0.156f, 0.001f, 1.0f, + -0.785f, 0.0f, -0.155f, 0.001f, 1.0f, -0.789f, 0.0f, -0.154f, 0.002f, 1.0f, -0.793f, 0.0f, -0.153f, 0.003f, 1.0f, -0.797f, 0.0f, -0.152f, 0.004f, 1.0f, + -0.801f, 0.0f, -0.15f, 0.005f, 1.0f, -0.805f, 0.0f, -0.149f, 0.006f, 1.0f, -0.81f, 0.0f, -0.147f, 0.008f, 1.0f, -0.814f, 0.0f, -0.146f, 0.009f, 1.0f, + -0.818f, 0.0f, -0.144f, 0.011f, 1.0f, -0.823f, 0.0f, -0.143f, 0.014f, 1.0f, -0.827f, 0.0f, -0.141f, 0.016f, 1.0f, -0.831f, 0.0f, -0.139f, 0.019f, 1.0f, + -0.836f, 0.0f, -0.138f, 0.022f, 1.0f, -0.84f, 0.0f, -0.136f, 0.024f, 1.0f, -0.844f, 0.0f, -0.135f, 0.026f, 1.0f, -0.849f, 0.0f, -0.133f, 0.027f, 1.0f, + -0.853f, 0.0f, -0.131f, 0.027f, 1.0f, -0.857f, 0.0f, -0.13f, 0.027f, 1.0f, -0.861f, 0.0f, -0.128f, 0.027f, 1.0f, -0.865f, 0.0f, -0.126f, 0.027f, 1.0f, + -0.868f, 0.0f, -0.125f, 0.026f, 1.0f, -0.872f, 0.0f, -0.123f, 0.025f, 1.0f, -0.876f, 0.0f, -0.121f, 0.025f, 1.0f, -0.879f, 0.0f, -0.119f, 0.024f, 1.0f, + -0.883f, 0.0f, -0.118f, 0.023f, 1.0f, -0.886f, 0.0f, -0.116f, 0.022f, 1.0f, -0.89f, 0.0f, -0.114f, 0.022f, 1.0f, -0.894f, 0.0f, -0.112f, 0.021f, 1.0f, + -0.898f, 0.0f, -0.11f, 0.022f, 1.0f, -0.901f, 0.0f, -0.107f, 0.022f, 1.0f, -0.905f, 0.0f, -0.105f, 0.024f, 1.0f, -0.909f, 0.0f, -0.103f, 0.026f, 1.0f, + -0.913f, 0.0f, -0.1f, 0.029f, 1.0f, -0.917f, 0.0f, -0.098f, 0.032f, 1.0f, -0.921f, 0.0f, -0.095f, 0.035f, 1.0f, -0.926f, 0.0f, -0.092f, 0.039f, 1.0f, + -0.93f, 0.0f, -0.09f, 0.043f, 1.0f, -0.934f, 0.0f, -0.087f, 0.047f, 1.0f, -0.938f, 0.0f, -0.084f, 0.051f, 1.0f, -0.942f, 0.0f, -0.081f, 0.055f, 1.0f, + -0.946f, 0.0f, -0.078f, 0.06f, 1.0f, -0.95f, 0.0f, -0.075f, 0.065f, 1.0f, -0.954f, 0.0f, -0.073f, 0.07f, 1.0f, -0.958f, 0.0f, -0.07f, 0.075f, 1.0f, + -0.961f, 0.0f, -0.067f, 0.081f, 1.0f, -0.965f, 0.0f, -0.064f, 0.087f, 1.0f, -0.968f, 0.0f, -0.061f, 0.092f, 1.0f, -0.972f, 0.0f, -0.058f, 0.098f, 1.0f, + -0.975f, 0.0f, -0.055f, 0.103f, 1.0f, -0.979f, 0.0f, -0.053f, 0.108f, 1.0f, -0.982f, 0.0f, -0.05f, 0.112f, 1.0f, -0.985f, 0.0f, -0.047f, 0.116f, 1.0f, + -0.988f, 0.0f, -0.045f, 0.12f, 1.0f, -0.991f, 0.0f, -0.042f, 0.123f, 1.0f, -0.994f, 0.0f, -0.039f, 0.126f, 1.0f, -0.997f, 0.0f, -0.037f, 0.129f, 1.0f, + -1.0f, 0.0f, -0.034f, 0.131f, 1.0f, -1.003f, 0.0f, -0.031f, 0.133f, 1.0f, -1.005f, 0.0f, -0.029f, 0.135f, 1.0f, -1.008f, 0.0f, -0.026f, 0.137f, 1.0f, + -1.01f, 0.0f, -0.024f, 0.139f, 1.0f, -1.013f, 0.0f, -0.021f, 0.141f, 1.0f, -1.016f, 0.0f, -0.018f, 0.143f, 1.0f, -1.018f, 0.0f, -0.016f, 0.144f, 1.0f, + -1.02f, 0.0f, -0.013f, 0.146f, 1.0f, -1.023f, 0.0f, -0.011f, 0.148f, 1.0f, -1.025f, 0.0f, -0.008f, 0.149f, 1.0f, -1.027f, 0.0f, -0.006f, 0.151f, 1.0f, + -1.029f, 0.0f, -0.003f, 0.152f, 1.0f, -1.032f, 0.0f, -0.001f, 0.154f, 1.0f, -1.034f, 0.0f, 0.001f, 0.154f, 1.0f, -1.036f, 0.0f, 0.004f, 0.155f, 1.0f, + -1.038f, 0.0f, 0.006f, 0.156f, 1.0f, -1.041f, 0.0f, 0.008f, 0.156f, 1.0f, -1.043f, 0.0f, 0.01f, 0.157f, 1.0f, -1.045f, 0.0f, 0.013f, 0.157f, 1.0f, + -1.047f, 0.0f, 0.015f, 0.157f, 1.0f, -1.049f, 0.0f, 0.018f, 0.158f, 1.0f, -1.051f, 0.0f, 0.02f, 0.158f, 1.0f, -1.053f, 0.0f, 0.023f, 0.158f, 1.0f, + -1.055f, 0.0f, 0.025f, 0.158f, 1.0f, -1.057f, 0.0f, 0.028f, 0.158f, 1.0f, -1.059f, 0.0f, 0.03f, 0.158f, 1.0f, -1.061f, 0.0f, 0.033f, 0.158f, 1.0f, + -1.063f, 0.0f, 0.036f, 0.158f, 1.0f, -1.065f, 0.0f, 0.038f, 0.158f, 1.0f, -1.067f, 0.0f, 0.041f, 0.158f, 1.0f, -1.069f, 0.0f, 0.044f, 0.157f, 1.0f, + -1.071f, 0.0f, 0.047f, 0.157f, 1.0f, -1.073f, 0.0f, 0.049f, 0.156f, 1.0f, -1.074f, 0.0f, 0.052f, 0.155f, 1.0f, -1.076f, 0.0f, 0.055f, 0.154f, 1.0f, + -1.078f, 0.0f, 0.058f, 0.153f, 1.0f, -1.08f, 0.0f, 0.061f, 0.152f, 1.0f, -1.082f, 0.0f, 0.064f, 0.15f, 1.0f, -1.083f, 0.0f, 0.067f, 0.148f, 1.0f, + -1.085f, 0.0f, 0.07f, 0.146f, 1.0f, -1.087f, 0.0f, 0.073f, 0.144f, 1.0f, -1.089f, 0.0f, 0.076f, 0.142f, 1.0f, -1.091f, 0.0f, 0.08f, 0.14f, 1.0f, + -1.092f, 0.0f, 0.083f, 0.138f, 1.0f, -1.094f, 0.0f, 0.086f, 0.136f, 1.0f, -1.096f, 0.0f, 0.09f, 0.135f, 1.0f, -1.097f, 0.0f, 0.093f, 0.134f, 1.0f, + -1.099f, 0.0f, 0.096f, 0.134f, 1.0f, -1.101f, 0.0f, 0.1f, 0.134f, 1.0f, -1.103f, 0.0f, 0.103f, 0.136f, 1.0f, -1.104f, 0.0f, 0.107f, 0.139f, 1.0f, + -1.106f, 0.0f, 0.111f, 0.144f, 1.0f, -1.107f, 0.0f, 0.114f, 0.15f, 1.0f, -1.109f, 0.0f, 0.118f, 0.158f, 1.0f, -1.11f, 0.0f, 0.122f, 0.167f, 1.0f, + -1.111f, 0.0f, 0.126f, 0.178f, 1.0f, -1.113f, 0.0f, 0.13f, 0.191f, 1.0f, -1.114f, 0.0f, 0.134f, 0.205f, 1.0f, -1.115f, 0.0f, 0.138f, 0.22f, 1.0f, + -1.116f, 0.0f, 0.142f, 0.237f, 1.0f, -1.117f, 0.0f, 0.146f, 0.254f, 1.0f, -1.118f, 0.0f, 0.15f, 0.272f, 1.0f, -1.119f, 0.0f, 0.155f, 0.291f, 1.0f, + -1.119f, 0.0f, 0.159f, 0.31f, 1.0f, -1.12f, 0.0f, 0.163f, 0.329f, 1.0f, -1.121f, 0.0f, 0.167f, 0.348f, 1.0f, -1.121f, 0.0f, 0.172f, 0.367f, 1.0f, + -1.122f, 0.0f, 0.176f, 0.386f, 1.0f, -1.122f, 0.0f, 0.18f, 0.405f, 1.0f, -1.123f, 0.0f, 0.184f, 0.423f, 1.0f, -1.123f, 0.0f, 0.189f, 0.441f, 1.0f, + -1.124f, 0.0f, 0.193f, 0.458f, 1.0f, -1.124f, 0.0f, 0.197f, 0.475f, 1.0f, -1.124f, 0.0f, 0.202f, 0.492f, 1.0f, -1.124f, 0.0f, 0.206f, 0.508f, 1.0f, + -1.125f, 0.0f, 0.21f, 0.524f, 1.0f, -1.125f, 0.0f, 0.214f, 0.539f, 1.0f, -1.125f, 0.0f, 0.218f, 0.554f, 1.0f, -1.124f, 0.0f, 0.223f, 0.568f, 1.0f, + -1.124f, 0.0f, 0.227f, 0.581f, 1.0f, -1.124f, 0.0f, 0.231f, 0.593f, 1.0f, -1.124f, 0.0f, 0.235f, 0.604f, 1.0f, -1.123f, 0.0f, 0.239f, 0.614f, 1.0f, + -1.123f, 0.0f, 0.243f, 0.624f, 1.0f, -1.122f, 0.0f, 0.247f, 0.632f, 1.0f, -1.122f, 0.0f, 0.251f, 0.64f, 1.0f, -1.121f, 0.0f, 0.255f, 0.646f, 1.0f, + -1.121f, 0.0f, 0.258f, 0.653f, 1.0f, -1.12f, 0.0f, 0.262f, 0.658f, 1.0f, -1.119f, 0.0f, 0.266f, 0.663f, 1.0f, -1.118f, 0.0f, 0.269f, 0.668f, 1.0f, + -1.117f, 0.0f, 0.272f, 0.673f, 1.0f, -1.117f, 0.0f, 0.276f, 0.678f, 1.0f, -1.116f, 0.0f, 0.279f, 0.682f, 1.0f, -1.115f, 0.0f, 0.282f, 0.687f, 1.0f, + -1.113f, 0.0f, 0.285f, 0.692f, 1.0f, -1.112f, 0.0f, 0.289f, 0.697f, 1.0f, -1.111f, 0.0f, 0.292f, 0.702f, 1.0f, -1.11f, 0.0f, 0.294f, 0.708f, 1.0f, + -1.109f, 0.0f, 0.297f, 0.713f, 1.0f, -1.108f, 0.0f, 0.3f, 0.718f, 1.0f, -1.106f, 0.0f, 0.303f, 0.724f, 1.0f, -1.105f, 0.0f, 0.306f, 0.73f, 1.0f, + -1.104f, 0.0f, 0.309f, 0.735f, 1.0f, -1.102f, 0.0f, 0.312f, 0.741f, 1.0f, -1.101f, 0.0f, 0.315f, 0.746f, 1.0f, -1.099f, 0.0f, 0.318f, 0.751f, 1.0f, + -1.098f, 0.0f, 0.321f, 0.756f, 1.0f, -1.096f, 0.0f, 0.323f, 0.761f, 1.0f, -1.094f, 0.0f, 0.326f, 0.766f, 1.0f, -1.093f, 0.0f, 0.329f, 0.771f, 1.0f, + -1.091f, 0.0f, 0.332f, 0.776f, 1.0f, -1.089f, 0.0f, 0.335f, 0.781f, 1.0f, -1.087f, 0.0f, 0.338f, 0.786f, 1.0f, -1.085f, 0.0f, 0.341f, 0.791f, 1.0f, + -1.082f, 0.0f, 0.344f, 0.797f, 1.0f, -1.08f, 0.0f, 0.347f, 0.802f, 1.0f, -1.078f, 0.0f, 0.349f, 0.808f, 1.0f, -1.075f, 0.0f, 0.352f, 0.814f, 1.0f, + -1.072f, 0.0f, 0.355f, 0.82f, 1.0f, -1.069f, 0.0f, 0.358f, 0.826f, 1.0f, -1.066f, 0.0f, 0.36f, 0.831f, 1.0f, -1.063f, 0.0f, 0.363f, 0.837f, 1.0f, + -1.059f, 0.0f, 0.366f, 0.842f, 1.0f, -1.055f, 0.0f, 0.368f, 0.847f, 1.0f, -1.051f, 0.0f, 0.371f, 0.851f, 1.0f, -1.047f, 0.0f, 0.373f, 0.856f, 1.0f, + -1.042f, 0.0f, 0.375f, 0.86f, 1.0f, -1.037f, 0.0f, 0.378f, 0.863f, 1.0f, -1.031f, 0.0f, 0.38f, 0.866f, 1.0f, -1.026f, 0.0f, 0.382f, 0.869f, 1.0f, + -1.02f, 0.0f, 0.384f, 0.871f, 1.0f, -1.014f, 0.0f, 0.386f, 0.873f, 1.0f, -1.007f, 0.0f, 0.387f, 0.875f, 1.0f, -1.0f, 0.0f, 0.389f, 0.876f, 1.0f, + -0.994f, 0.0f, 0.39f, 0.877f, 1.0f, -0.987f, 0.0f, 0.392f, 0.878f, 1.0f, -0.979f, 0.0f, 0.393f, 0.879f, 1.0f, -0.972f, 0.0f, 0.394f, 0.88f, 1.0f, + -0.964f, 0.0f, 0.395f, 0.881f, 1.0f, -0.956f, 0.0f, 0.395f, 0.881f, 1.0f, -0.948f, 0.0f, 0.395f, 0.882f, 1.0f, -0.94f, 0.0f, 0.395f, 0.882f, 1.0f, + -0.932f, 0.0f, 0.395f, 0.883f, 1.0f, -0.923f, 0.0f, 0.394f, 0.883f, 1.0f, -0.915f, 0.0f, 0.393f, 0.883f, 1.0f, -0.906f, 0.0f, 0.391f, 0.883f, 1.0f, + -0.896f, 0.0f, 0.389f, 0.881f, 1.0f, -0.887f, 0.0f, 0.386f, 0.876f, 1.0f, -0.877f, 0.0f, 0.382f, 0.866f, 1.0f, -0.867f, 0.0f, 0.378f, 0.85f, 1.0f, + -0.857f, 0.0f, 0.373f, 0.828f, 1.0f, -0.848f, 0.0f, 0.368f, 0.799f, 1.0f, -0.838f, 0.0f, 0.363f, 0.764f, 1.0f, -0.829f, 0.0f, 0.357f, 0.723f, 1.0f, + -0.819f, 0.0f, 0.352f, 0.679f, 1.0f, -0.811f, 0.0f, 0.347f, 0.631f, 1.0f, -0.802f, 0.0f, 0.342f, 0.579f, 1.0f, -0.794f, 0.0f, 0.338f, 0.525f, 1.0f, + -0.786f, 0.0f, 0.333f, 0.469f, 1.0f, -0.779f, 0.0f, 0.329f, 0.412f, 1.0f, -0.772f, 0.0f, 0.325f, 0.351f, 1.0f, -0.766f, 0.0f, 0.321f, 0.3f, 1.0f, + -0.757f, 0.0f, 0.317f, 0.219f, 1.0f, +}; + +static const float data29[205 * GP_PRIM_DATABUF_SIZE] = { + 0.816f, 0.0f, 0.326f, 0.285f, 1.0f, 0.819f, 0.0f, 0.328f, 0.287f, 1.0f, 0.821f, 0.0f, 0.33f, 0.29f, 1.0f, 0.823f, 0.0f, 0.331f, 0.295f, 1.0f, + 0.825f, 0.0f, 0.333f, 0.304f, 1.0f, 0.828f, 0.0f, 0.335f, 0.315f, 1.0f, 0.83f, 0.0f, 0.337f, 0.328f, 1.0f, 0.833f, 0.0f, 0.339f, 0.341f, 1.0f, + 0.836f, 0.0f, 0.341f, 0.355f, 1.0f, 0.839f, 0.0f, 0.343f, 0.368f, 1.0f, 0.842f, 0.0f, 0.345f, 0.38f, 1.0f, 0.845f, 0.0f, 0.347f, 0.392f, 1.0f, + 0.848f, 0.0f, 0.349f, 0.402f, 1.0f, 0.851f, 0.0f, 0.351f, 0.412f, 1.0f, 0.854f, 0.0f, 0.352f, 0.421f, 1.0f, 0.857f, 0.0f, 0.354f, 0.429f, 1.0f, + 0.861f, 0.0f, 0.356f, 0.437f, 1.0f, 0.865f, 0.0f, 0.357f, 0.444f, 1.0f, 0.869f, 0.0f, 0.359f, 0.452f, 1.0f, 0.872f, 0.0f, 0.36f, 0.46f, 1.0f, + 0.876f, 0.0f, 0.361f, 0.47f, 1.0f, 0.881f, 0.0f, 0.363f, 0.481f, 1.0f, 0.885f, 0.0f, 0.364f, 0.491f, 1.0f, 0.889f, 0.0f, 0.365f, 0.501f, 1.0f, + 0.893f, 0.0f, 0.366f, 0.511f, 1.0f, 0.898f, 0.0f, 0.367f, 0.52f, 1.0f, 0.902f, 0.0f, 0.368f, 0.528f, 1.0f, 0.906f, 0.0f, 0.37f, 0.535f, 1.0f, + 0.911f, 0.0f, 0.371f, 0.542f, 1.0f, 0.915f, 0.0f, 0.372f, 0.548f, 1.0f, 0.92f, 0.0f, 0.373f, 0.554f, 1.0f, 0.924f, 0.0f, 0.374f, 0.559f, 1.0f, + 0.929f, 0.0f, 0.375f, 0.564f, 1.0f, 0.933f, 0.0f, 0.376f, 0.567f, 1.0f, 0.938f, 0.0f, 0.377f, 0.57f, 1.0f, 0.943f, 0.0f, 0.378f, 0.572f, 1.0f, + 0.947f, 0.0f, 0.378f, 0.574f, 1.0f, 0.952f, 0.0f, 0.379f, 0.576f, 1.0f, 0.956f, 0.0f, 0.38f, 0.577f, 1.0f, 0.961f, 0.0f, 0.38f, 0.579f, 1.0f, + 0.966f, 0.0f, 0.381f, 0.581f, 1.0f, 0.971f, 0.0f, 0.381f, 0.585f, 1.0f, 0.975f, 0.0f, 0.382f, 0.588f, 1.0f, 0.98f, 0.0f, 0.382f, 0.591f, 1.0f, + 0.985f, 0.0f, 0.382f, 0.595f, 1.0f, 0.989f, 0.0f, 0.382f, 0.597f, 1.0f, 0.994f, 0.0f, 0.382f, 0.6f, 1.0f, 0.999f, 0.0f, 0.382f, 0.603f, 1.0f, + 1.003f, 0.0f, 0.382f, 0.605f, 1.0f, 1.008f, 0.0f, 0.381f, 0.607f, 1.0f, 1.013f, 0.0f, 0.381f, 0.61f, 1.0f, 1.017f, 0.0f, 0.381f, 0.611f, 1.0f, + 1.021f, 0.0f, 0.381f, 0.613f, 1.0f, 1.025f, 0.0f, 0.38f, 0.613f, 1.0f, 1.029f, 0.0f, 0.38f, 0.614f, 1.0f, 1.033f, 0.0f, 0.379f, 0.614f, 1.0f, + 1.037f, 0.0f, 0.379f, 0.614f, 1.0f, 1.041f, 0.0f, 0.378f, 0.614f, 1.0f, 1.044f, 0.0f, 0.378f, 0.614f, 1.0f, 1.048f, 0.0f, 0.377f, 0.614f, 1.0f, + 1.051f, 0.0f, 0.376f, 0.613f, 1.0f, 1.054f, 0.0f, 0.375f, 0.612f, 1.0f, 1.057f, 0.0f, 0.374f, 0.611f, 1.0f, 1.06f, 0.0f, 0.373f, 0.61f, 1.0f, + 1.063f, 0.0f, 0.372f, 0.609f, 1.0f, 1.066f, 0.0f, 0.371f, 0.609f, 1.0f, 1.068f, 0.0f, 0.37f, 0.608f, 1.0f, 1.071f, 0.0f, 0.368f, 0.608f, 1.0f, + 1.073f, 0.0f, 0.367f, 0.608f, 1.0f, 1.076f, 0.0f, 0.365f, 0.608f, 1.0f, 1.078f, 0.0f, 0.364f, 0.607f, 1.0f, 1.081f, 0.0f, 0.362f, 0.607f, 1.0f, + 1.083f, 0.0f, 0.36f, 0.607f, 1.0f, 1.085f, 0.0f, 0.358f, 0.606f, 1.0f, 1.087f, 0.0f, 0.356f, 0.606f, 1.0f, 1.09f, 0.0f, 0.354f, 0.606f, 1.0f, + 1.092f, 0.0f, 0.352f, 0.606f, 1.0f, 1.094f, 0.0f, 0.35f, 0.606f, 1.0f, 1.096f, 0.0f, 0.348f, 0.606f, 1.0f, 1.097f, 0.0f, 0.346f, 0.606f, 1.0f, + 1.099f, 0.0f, 0.344f, 0.606f, 1.0f, 1.101f, 0.0f, 0.341f, 0.606f, 1.0f, 1.103f, 0.0f, 0.339f, 0.606f, 1.0f, 1.104f, 0.0f, 0.337f, 0.607f, 1.0f, + 1.106f, 0.0f, 0.335f, 0.607f, 1.0f, 1.108f, 0.0f, 0.332f, 0.607f, 1.0f, 1.109f, 0.0f, 0.33f, 0.608f, 1.0f, 1.111f, 0.0f, 0.327f, 0.608f, 1.0f, + 1.113f, 0.0f, 0.324f, 0.608f, 1.0f, 1.114f, 0.0f, 0.322f, 0.609f, 1.0f, 1.116f, 0.0f, 0.319f, 0.609f, 1.0f, 1.117f, 0.0f, 0.316f, 0.609f, 1.0f, + 1.118f, 0.0f, 0.313f, 0.609f, 1.0f, 1.12f, 0.0f, 0.31f, 0.609f, 1.0f, 1.121f, 0.0f, 0.307f, 0.609f, 1.0f, 1.123f, 0.0f, 0.304f, 0.608f, 1.0f, + 1.124f, 0.0f, 0.301f, 0.608f, 1.0f, 1.125f, 0.0f, 0.297f, 0.607f, 1.0f, 1.126f, 0.0f, 0.294f, 0.606f, 1.0f, 1.127f, 0.0f, 0.29f, 0.605f, 1.0f, + 1.129f, 0.0f, 0.287f, 0.603f, 1.0f, 1.13f, 0.0f, 0.283f, 0.601f, 1.0f, 1.131f, 0.0f, 0.279f, 0.599f, 1.0f, 1.132f, 0.0f, 0.276f, 0.597f, 1.0f, + 1.132f, 0.0f, 0.272f, 0.595f, 1.0f, 1.133f, 0.0f, 0.268f, 0.593f, 1.0f, 1.134f, 0.0f, 0.264f, 0.592f, 1.0f, 1.135f, 0.0f, 0.26f, 0.591f, 1.0f, + 1.135f, 0.0f, 0.256f, 0.59f, 1.0f, 1.136f, 0.0f, 0.252f, 0.589f, 1.0f, 1.136f, 0.0f, 0.248f, 0.588f, 1.0f, 1.137f, 0.0f, 0.244f, 0.587f, 1.0f, + 1.137f, 0.0f, 0.24f, 0.586f, 1.0f, 1.138f, 0.0f, 0.236f, 0.585f, 1.0f, 1.138f, 0.0f, 0.232f, 0.584f, 1.0f, 1.138f, 0.0f, 0.228f, 0.582f, 1.0f, + 1.138f, 0.0f, 0.224f, 0.581f, 1.0f, 1.138f, 0.0f, 0.22f, 0.579f, 1.0f, 1.138f, 0.0f, 0.216f, 0.578f, 1.0f, 1.138f, 0.0f, 0.212f, 0.576f, 1.0f, + 1.138f, 0.0f, 0.208f, 0.575f, 1.0f, 1.138f, 0.0f, 0.204f, 0.573f, 1.0f, 1.137f, 0.0f, 0.2f, 0.572f, 1.0f, 1.137f, 0.0f, 0.196f, 0.571f, 1.0f, + 1.137f, 0.0f, 0.192f, 0.569f, 1.0f, 1.136f, 0.0f, 0.188f, 0.568f, 1.0f, 1.136f, 0.0f, 0.184f, 0.567f, 1.0f, 1.135f, 0.0f, 0.18f, 0.566f, 1.0f, + 1.134f, 0.0f, 0.176f, 0.565f, 1.0f, 1.133f, 0.0f, 0.172f, 0.563f, 1.0f, 1.132f, 0.0f, 0.168f, 0.561f, 1.0f, 1.131f, 0.0f, 0.164f, 0.559f, 1.0f, + 1.13f, 0.0f, 0.16f, 0.556f, 1.0f, 1.129f, 0.0f, 0.156f, 0.552f, 1.0f, 1.128f, 0.0f, 0.152f, 0.548f, 1.0f, 1.127f, 0.0f, 0.148f, 0.543f, 1.0f, + 1.126f, 0.0f, 0.144f, 0.537f, 1.0f, 1.124f, 0.0f, 0.14f, 0.53f, 1.0f, 1.123f, 0.0f, 0.136f, 0.522f, 1.0f, 1.122f, 0.0f, 0.132f, 0.514f, 1.0f, + 1.12f, 0.0f, 0.128f, 0.505f, 1.0f, 1.118f, 0.0f, 0.123f, 0.495f, 1.0f, 1.117f, 0.0f, 0.119f, 0.486f, 1.0f, 1.115f, 0.0f, 0.115f, 0.476f, 1.0f, + 1.113f, 0.0f, 0.111f, 0.466f, 1.0f, 1.111f, 0.0f, 0.107f, 0.456f, 1.0f, 1.11f, 0.0f, 0.102f, 0.446f, 1.0f, 1.108f, 0.0f, 0.098f, 0.436f, 1.0f, + 1.105f, 0.0f, 0.094f, 0.425f, 1.0f, 1.103f, 0.0f, 0.09f, 0.414f, 1.0f, 1.101f, 0.0f, 0.085f, 0.402f, 1.0f, 1.099f, 0.0f, 0.081f, 0.389f, 1.0f, + 1.096f, 0.0f, 0.077f, 0.377f, 1.0f, 1.094f, 0.0f, 0.072f, 0.364f, 1.0f, 1.091f, 0.0f, 0.068f, 0.351f, 1.0f, 1.088f, 0.0f, 0.063f, 0.338f, 1.0f, + 1.085f, 0.0f, 0.059f, 0.325f, 1.0f, 1.082f, 0.0f, 0.054f, 0.313f, 1.0f, 1.079f, 0.0f, 0.05f, 0.301f, 1.0f, 1.075f, 0.0f, 0.045f, 0.29f, 1.0f, + 1.071f, 0.0f, 0.04f, 0.281f, 1.0f, 1.067f, 0.0f, 0.035f, 0.272f, 1.0f, 1.063f, 0.0f, 0.031f, 0.266f, 1.0f, 1.059f, 0.0f, 0.026f, 0.261f, 1.0f, + 1.054f, 0.0f, 0.021f, 0.258f, 1.0f, 1.049f, 0.0f, 0.016f, 0.257f, 1.0f, 1.043f, 0.0f, 0.011f, 0.259f, 1.0f, 1.037f, 0.0f, 0.006f, 0.264f, 1.0f, + 1.031f, 0.0f, 0.0f, 0.272f, 1.0f, 1.025f, 0.0f, -0.005f, 0.283f, 1.0f, 1.018f, 0.0f, -0.01f, 0.296f, 1.0f, 1.011f, 0.0f, -0.015f, 0.313f, 1.0f, + 1.003f, 0.0f, -0.021f, 0.33f, 1.0f, 0.996f, 0.0f, -0.026f, 0.348f, 1.0f, 0.988f, 0.0f, -0.032f, 0.365f, 1.0f, 0.979f, 0.0f, -0.038f, 0.379f, 1.0f, + 0.971f, 0.0f, -0.044f, 0.389f, 1.0f, 0.962f, 0.0f, -0.05f, 0.394f, 1.0f, 0.953f, 0.0f, -0.057f, 0.392f, 1.0f, 0.944f, 0.0f, -0.063f, 0.384f, 1.0f, + 0.934f, 0.0f, -0.069f, 0.368f, 1.0f, 0.924f, 0.0f, -0.075f, 0.347f, 1.0f, 0.914f, 0.0f, -0.081f, 0.32f, 1.0f, 0.903f, 0.0f, -0.087f, 0.289f, 1.0f, + 0.893f, 0.0f, -0.092f, 0.256f, 1.0f, 0.882f, 0.0f, -0.098f, 0.223f, 1.0f, 0.871f, 0.0f, -0.103f, 0.191f, 1.0f, 0.86f, 0.0f, -0.108f, 0.162f, 1.0f, + 0.849f, 0.0f, -0.112f, 0.136f, 1.0f, 0.838f, 0.0f, -0.117f, 0.112f, 1.0f, 0.827f, 0.0f, -0.121f, 0.091f, 1.0f, 0.815f, 0.0f, -0.125f, 0.074f, 1.0f, + 0.804f, 0.0f, -0.128f, 0.059f, 1.0f, 0.793f, 0.0f, -0.132f, 0.046f, 1.0f, 0.782f, 0.0f, -0.135f, 0.036f, 1.0f, 0.771f, 0.0f, -0.138f, 0.028f, 1.0f, + 0.76f, 0.0f, -0.141f, 0.021f, 1.0f, 0.749f, 0.0f, -0.144f, 0.016f, 1.0f, 0.738f, 0.0f, -0.147f, 0.012f, 1.0f, 0.728f, 0.0f, -0.149f, 0.009f, 1.0f, + 0.718f, 0.0f, -0.152f, 0.006f, 1.0f, 0.708f, 0.0f, -0.154f, 0.004f, 1.0f, 0.699f, 0.0f, -0.157f, 0.003f, 1.0f, 0.691f, 0.0f, -0.159f, 0.002f, 1.0f, + 0.68f, 0.0f, -0.162f, 0.0f, 1.0f, +}; + +static const float data30[33 * GP_PRIM_DATABUF_SIZE] = { + -1.02f, 0.0f, 0.179f, 0.21f, 1.0f, -1.014f, 0.0f, 0.182f, 0.301f, 1.0f, -1.01f, 0.0f, 0.184f, 0.36f, 1.0f, -1.004f, 0.0f, 0.186f, 0.426f, 1.0f, + -0.999f, 0.0f, 0.188f, 0.479f, 1.0f, -0.993f, 0.0f, 0.19f, 0.519f, 1.0f, -0.987f, 0.0f, 0.191f, 0.545f, 1.0f, -0.981f, 0.0f, 0.192f, 0.562f, 1.0f, + -0.975f, 0.0f, 0.193f, 0.575f, 1.0f, -0.968f, 0.0f, 0.193f, 0.582f, 1.0f, -0.961f, 0.0f, 0.193f, 0.587f, 1.0f, -0.954f, 0.0f, 0.191f, 0.592f, 1.0f, + -0.946f, 0.0f, 0.19f, 0.597f, 1.0f, -0.938f, 0.0f, 0.187f, 0.6f, 1.0f, -0.93f, 0.0f, 0.183f, 0.603f, 1.0f, -0.922f, 0.0f, 0.178f, 0.606f, 1.0f, + -0.913f, 0.0f, 0.173f, 0.608f, 1.0f, -0.905f, 0.0f, 0.168f, 0.61f, 1.0f, -0.898f, 0.0f, 0.162f, 0.612f, 1.0f, -0.89f, 0.0f, 0.156f, 0.613f, 1.0f, + -0.883f, 0.0f, 0.15f, 0.612f, 1.0f, -0.877f, 0.0f, 0.143f, 0.608f, 1.0f, -0.871f, 0.0f, 0.137f, 0.602f, 1.0f, -0.865f, 0.0f, 0.131f, 0.593f, 1.0f, + -0.86f, 0.0f, 0.125f, 0.577f, 1.0f, -0.855f, 0.0f, 0.12f, 0.554f, 1.0f, -0.85f, 0.0f, 0.114f, 0.524f, 1.0f, -0.846f, 0.0f, 0.109f, 0.487f, 1.0f, + -0.842f, 0.0f, 0.104f, 0.443f, 1.0f, -0.838f, 0.0f, 0.1f, 0.394f, 1.0f, -0.835f, 0.0f, 0.095f, 0.339f, 1.0f, -0.832f, 0.0f, 0.091f, 0.295f, 1.0f, + -0.828f, 0.0f, 0.086f, 0.227f, 1.0f, +}; + +static const float data31[37 * GP_PRIM_DATABUF_SIZE] = { + 0.777f, 0.0f, 0.096f, 0.278f, 1.0f, 0.779f, 0.0f, 0.1f, 0.307f, 1.0f, 0.781f, 0.0f, 0.103f, 0.326f, 1.0f, 0.782f, 0.0f, 0.106f, 0.349f, 1.0f, + 0.784f, 0.0f, 0.109f, 0.372f, 1.0f, 0.786f, 0.0f, 0.112f, 0.395f, 1.0f, 0.789f, 0.0f, 0.116f, 0.418f, 1.0f, 0.791f, 0.0f, 0.119f, 0.44f, 1.0f, + 0.794f, 0.0f, 0.123f, 0.462f, 1.0f, 0.798f, 0.0f, 0.127f, 0.484f, 1.0f, 0.801f, 0.0f, 0.13f, 0.504f, 1.0f, 0.806f, 0.0f, 0.134f, 0.522f, 1.0f, + 0.81f, 0.0f, 0.138f, 0.54f, 1.0f, 0.815f, 0.0f, 0.142f, 0.556f, 1.0f, 0.82f, 0.0f, 0.146f, 0.571f, 1.0f, 0.826f, 0.0f, 0.15f, 0.584f, 1.0f, + 0.832f, 0.0f, 0.154f, 0.596f, 1.0f, 0.839f, 0.0f, 0.159f, 0.607f, 1.0f, 0.846f, 0.0f, 0.163f, 0.616f, 1.0f, 0.854f, 0.0f, 0.166f, 0.623f, 1.0f, + 0.862f, 0.0f, 0.17f, 0.628f, 1.0f, 0.87f, 0.0f, 0.174f, 0.632f, 1.0f, 0.878f, 0.0f, 0.177f, 0.632f, 1.0f, 0.887f, 0.0f, 0.18f, 0.63f, 1.0f, + 0.895f, 0.0f, 0.183f, 0.623f, 1.0f, 0.903f, 0.0f, 0.186f, 0.611f, 1.0f, 0.912f, 0.0f, 0.188f, 0.592f, 1.0f, 0.92f, 0.0f, 0.19f, 0.567f, 1.0f, + 0.928f, 0.0f, 0.192f, 0.533f, 1.0f, 0.935f, 0.0f, 0.193f, 0.492f, 1.0f, 0.943f, 0.0f, 0.194f, 0.442f, 1.0f, 0.95f, 0.0f, 0.196f, 0.385f, 1.0f, + 0.957f, 0.0f, 0.197f, 0.321f, 1.0f, 0.963f, 0.0f, 0.197f, 0.253f, 1.0f, 0.97f, 0.0f, 0.198f, 0.175f, 1.0f, 0.975f, 0.0f, 0.199f, 0.107f, 1.0f, + 0.983f, 0.0f, 0.199f, 0.0f, 1.0f, +}; + +static const float data32[201 * GP_PRIM_DATABUF_SIZE] = { + -0.437f, 0.0f, 0.508f, 0.0f, 1.0f, -0.435f, 0.0f, 0.51f, 0.0f, 1.0f, -0.434f, 0.0f, 0.511f, 0.0f, 1.0f, -0.432f, 0.0f, 0.512f, 0.0f, 1.0f, + -0.43f, 0.0f, 0.513f, 0.0f, 1.0f, -0.428f, 0.0f, 0.514f, 0.001f, 1.0f, -0.426f, 0.0f, 0.515f, 0.002f, 1.0f, -0.424f, 0.0f, 0.517f, 0.004f, 1.0f, + -0.422f, 0.0f, 0.518f, 0.007f, 1.0f, -0.42f, 0.0f, 0.519f, 0.012f, 1.0f, -0.418f, 0.0f, 0.521f, 0.018f, 1.0f, -0.416f, 0.0f, 0.522f, 0.025f, 1.0f, + -0.414f, 0.0f, 0.523f, 0.034f, 1.0f, -0.411f, 0.0f, 0.525f, 0.043f, 1.0f, -0.409f, 0.0f, 0.526f, 0.053f, 1.0f, -0.407f, 0.0f, 0.528f, 0.063f, 1.0f, + -0.404f, 0.0f, 0.529f, 0.073f, 1.0f, -0.402f, 0.0f, 0.531f, 0.083f, 1.0f, -0.399f, 0.0f, 0.532f, 0.092f, 1.0f, -0.396f, 0.0f, 0.534f, 0.101f, 1.0f, + -0.394f, 0.0f, 0.535f, 0.11f, 1.0f, -0.391f, 0.0f, 0.536f, 0.118f, 1.0f, -0.388f, 0.0f, 0.538f, 0.126f, 1.0f, -0.386f, 0.0f, 0.539f, 0.133f, 1.0f, + -0.383f, 0.0f, 0.54f, 0.14f, 1.0f, -0.38f, 0.0f, 0.542f, 0.147f, 1.0f, -0.377f, 0.0f, 0.543f, 0.153f, 1.0f, -0.374f, 0.0f, 0.544f, 0.159f, 1.0f, + -0.37f, 0.0f, 0.545f, 0.166f, 1.0f, -0.367f, 0.0f, 0.546f, 0.172f, 1.0f, -0.364f, 0.0f, 0.547f, 0.179f, 1.0f, -0.361f, 0.0f, 0.548f, 0.186f, 1.0f, + -0.357f, 0.0f, 0.549f, 0.193f, 1.0f, -0.354f, 0.0f, 0.55f, 0.202f, 1.0f, -0.35f, 0.0f, 0.551f, 0.211f, 1.0f, -0.347f, 0.0f, 0.552f, 0.221f, 1.0f, + -0.343f, 0.0f, 0.552f, 0.233f, 1.0f, -0.339f, 0.0f, 0.553f, 0.245f, 1.0f, -0.336f, 0.0f, 0.553f, 0.258f, 1.0f, -0.332f, 0.0f, 0.554f, 0.272f, 1.0f, + -0.328f, 0.0f, 0.554f, 0.286f, 1.0f, -0.324f, 0.0f, 0.554f, 0.301f, 1.0f, -0.321f, 0.0f, 0.555f, 0.317f, 1.0f, -0.317f, 0.0f, 0.555f, 0.332f, 1.0f, + -0.313f, 0.0f, 0.555f, 0.348f, 1.0f, -0.309f, 0.0f, 0.555f, 0.364f, 1.0f, -0.305f, 0.0f, 0.555f, 0.38f, 1.0f, -0.302f, 0.0f, 0.555f, 0.396f, 1.0f, + -0.298f, 0.0f, 0.555f, 0.411f, 1.0f, -0.294f, 0.0f, 0.555f, 0.426f, 1.0f, -0.29f, 0.0f, 0.554f, 0.44f, 1.0f, -0.287f, 0.0f, 0.554f, 0.454f, 1.0f, + -0.283f, 0.0f, 0.554f, 0.467f, 1.0f, -0.28f, 0.0f, 0.553f, 0.479f, 1.0f, -0.276f, 0.0f, 0.553f, 0.49f, 1.0f, -0.273f, 0.0f, 0.552f, 0.5f, 1.0f, + -0.269f, 0.0f, 0.552f, 0.51f, 1.0f, -0.266f, 0.0f, 0.551f, 0.519f, 1.0f, -0.263f, 0.0f, 0.55f, 0.527f, 1.0f, -0.26f, 0.0f, 0.549f, 0.534f, 1.0f, + -0.256f, 0.0f, 0.549f, 0.541f, 1.0f, -0.253f, 0.0f, 0.548f, 0.547f, 1.0f, -0.25f, 0.0f, 0.547f, 0.552f, 1.0f, -0.247f, 0.0f, 0.546f, 0.557f, 1.0f, + -0.244f, 0.0f, 0.545f, 0.561f, 1.0f, -0.241f, 0.0f, 0.544f, 0.564f, 1.0f, -0.238f, 0.0f, 0.543f, 0.567f, 1.0f, -0.235f, 0.0f, 0.542f, 0.57f, 1.0f, + -0.233f, 0.0f, 0.541f, 0.572f, 1.0f, -0.23f, 0.0f, 0.54f, 0.574f, 1.0f, -0.227f, 0.0f, 0.539f, 0.575f, 1.0f, -0.224f, 0.0f, 0.538f, 0.576f, 1.0f, + -0.221f, 0.0f, 0.537f, 0.577f, 1.0f, -0.219f, 0.0f, 0.535f, 0.578f, 1.0f, -0.216f, 0.0f, 0.534f, 0.578f, 1.0f, -0.213f, 0.0f, 0.533f, 0.579f, 1.0f, + -0.211f, 0.0f, 0.532f, 0.579f, 1.0f, -0.208f, 0.0f, 0.53f, 0.579f, 1.0f, -0.206f, 0.0f, 0.529f, 0.578f, 1.0f, -0.203f, 0.0f, 0.528f, 0.578f, 1.0f, + -0.2f, 0.0f, 0.526f, 0.577f, 1.0f, -0.198f, 0.0f, 0.525f, 0.576f, 1.0f, -0.195f, 0.0f, 0.523f, 0.575f, 1.0f, -0.193f, 0.0f, 0.522f, 0.574f, 1.0f, + -0.19f, 0.0f, 0.52f, 0.572f, 1.0f, -0.188f, 0.0f, 0.518f, 0.571f, 1.0f, -0.185f, 0.0f, 0.517f, 0.569f, 1.0f, -0.182f, 0.0f, 0.515f, 0.568f, 1.0f, + -0.18f, 0.0f, 0.513f, 0.567f, 1.0f, -0.177f, 0.0f, 0.512f, 0.565f, 1.0f, -0.174f, 0.0f, 0.51f, 0.564f, 1.0f, -0.172f, 0.0f, 0.508f, 0.562f, 1.0f, + -0.169f, 0.0f, 0.506f, 0.56f, 1.0f, -0.166f, 0.0f, 0.504f, 0.559f, 1.0f, -0.164f, 0.0f, 0.502f, 0.556f, 1.0f, -0.161f, 0.0f, 0.501f, 0.554f, 1.0f, + -0.158f, 0.0f, 0.499f, 0.552f, 1.0f, -0.155f, 0.0f, 0.497f, 0.55f, 1.0f, -0.153f, 0.0f, 0.495f, 0.547f, 1.0f, -0.15f, 0.0f, 0.493f, 0.545f, 1.0f, + -0.147f, 0.0f, 0.491f, 0.543f, 1.0f, -0.144f, 0.0f, 0.489f, 0.54f, 1.0f, -0.142f, 0.0f, 0.487f, 0.538f, 1.0f, -0.139f, 0.0f, 0.485f, 0.536f, 1.0f, + -0.136f, 0.0f, 0.483f, 0.533f, 1.0f, -0.133f, 0.0f, 0.481f, 0.53f, 1.0f, -0.13f, 0.0f, 0.479f, 0.527f, 1.0f, -0.127f, 0.0f, 0.477f, 0.524f, 1.0f, + -0.124f, 0.0f, 0.475f, 0.521f, 1.0f, -0.121f, 0.0f, 0.473f, 0.519f, 1.0f, -0.118f, 0.0f, 0.471f, 0.516f, 1.0f, -0.115f, 0.0f, 0.469f, 0.514f, 1.0f, + -0.112f, 0.0f, 0.467f, 0.511f, 1.0f, -0.109f, 0.0f, 0.465f, 0.509f, 1.0f, -0.106f, 0.0f, 0.463f, 0.506f, 1.0f, -0.103f, 0.0f, 0.461f, 0.503f, 1.0f, + -0.099f, 0.0f, 0.458f, 0.501f, 1.0f, -0.096f, 0.0f, 0.456f, 0.5f, 1.0f, -0.093f, 0.0f, 0.454f, 0.498f, 1.0f, -0.09f, 0.0f, 0.452f, 0.497f, 1.0f, + -0.086f, 0.0f, 0.45f, 0.496f, 1.0f, -0.083f, 0.0f, 0.448f, 0.496f, 1.0f, -0.079f, 0.0f, 0.446f, 0.495f, 1.0f, -0.076f, 0.0f, 0.444f, 0.495f, 1.0f, + -0.072f, 0.0f, 0.442f, 0.494f, 1.0f, -0.069f, 0.0f, 0.44f, 0.494f, 1.0f, -0.065f, 0.0f, 0.438f, 0.494f, 1.0f, -0.062f, 0.0f, 0.436f, 0.494f, 1.0f, + -0.058f, 0.0f, 0.435f, 0.494f, 1.0f, -0.054f, 0.0f, 0.433f, 0.494f, 1.0f, -0.05f, 0.0f, 0.431f, 0.494f, 1.0f, -0.046f, 0.0f, 0.43f, 0.494f, 1.0f, + -0.042f, 0.0f, 0.428f, 0.494f, 1.0f, -0.038f, 0.0f, 0.427f, 0.494f, 1.0f, -0.033f, 0.0f, 0.426f, 0.494f, 1.0f, -0.029f, 0.0f, 0.425f, 0.494f, 1.0f, + -0.025f, 0.0f, 0.424f, 0.494f, 1.0f, -0.02f, 0.0f, 0.423f, 0.494f, 1.0f, -0.015f, 0.0f, 0.422f, 0.494f, 1.0f, -0.011f, 0.0f, 0.422f, 0.494f, 1.0f, + -0.006f, 0.0f, 0.421f, 0.494f, 1.0f, -0.001f, 0.0f, 0.421f, 0.495f, 1.0f, 0.004f, 0.0f, 0.421f, 0.495f, 1.0f, 0.009f, 0.0f, 0.421f, 0.495f, 1.0f, + 0.014f, 0.0f, 0.422f, 0.495f, 1.0f, 0.019f, 0.0f, 0.422f, 0.495f, 1.0f, 0.024f, 0.0f, 0.423f, 0.495f, 1.0f, 0.029f, 0.0f, 0.424f, 0.495f, 1.0f, + 0.034f, 0.0f, 0.426f, 0.495f, 1.0f, 0.039f, 0.0f, 0.427f, 0.495f, 1.0f, 0.044f, 0.0f, 0.429f, 0.496f, 1.0f, 0.049f, 0.0f, 0.43f, 0.497f, 1.0f, + 0.054f, 0.0f, 0.432f, 0.498f, 1.0f, 0.059f, 0.0f, 0.435f, 0.5f, 1.0f, 0.064f, 0.0f, 0.438f, 0.502f, 1.0f, 0.069f, 0.0f, 0.44f, 0.506f, 1.0f, + 0.074f, 0.0f, 0.443f, 0.51f, 1.0f, 0.08f, 0.0f, 0.446f, 0.516f, 1.0f, 0.085f, 0.0f, 0.45f, 0.522f, 1.0f, 0.09f, 0.0f, 0.453f, 0.528f, 1.0f, + 0.095f, 0.0f, 0.456f, 0.533f, 1.0f, 0.101f, 0.0f, 0.46f, 0.537f, 1.0f, 0.107f, 0.0f, 0.463f, 0.539f, 1.0f, 0.112f, 0.0f, 0.467f, 0.542f, 1.0f, + 0.118f, 0.0f, 0.471f, 0.543f, 1.0f, 0.124f, 0.0f, 0.475f, 0.545f, 1.0f, 0.13f, 0.0f, 0.478f, 0.546f, 1.0f, 0.137f, 0.0f, 0.482f, 0.546f, 1.0f, + 0.143f, 0.0f, 0.486f, 0.547f, 1.0f, 0.149f, 0.0f, 0.49f, 0.546f, 1.0f, 0.156f, 0.0f, 0.493f, 0.544f, 1.0f, 0.163f, 0.0f, 0.497f, 0.54f, 1.0f, + 0.17f, 0.0f, 0.5f, 0.533f, 1.0f, 0.176f, 0.0f, 0.503f, 0.525f, 1.0f, 0.183f, 0.0f, 0.507f, 0.515f, 1.0f, 0.191f, 0.0f, 0.509f, 0.503f, 1.0f, + 0.198f, 0.0f, 0.512f, 0.491f, 1.0f, 0.205f, 0.0f, 0.515f, 0.477f, 1.0f, 0.214f, 0.0f, 0.518f, 0.462f, 1.0f, 0.222f, 0.0f, 0.521f, 0.445f, 1.0f, + 0.23f, 0.0f, 0.524f, 0.427f, 1.0f, 0.238f, 0.0f, 0.527f, 0.409f, 1.0f, 0.245f, 0.0f, 0.529f, 0.388f, 1.0f, 0.254f, 0.0f, 0.531f, 0.366f, 1.0f, + 0.262f, 0.0f, 0.532f, 0.343f, 1.0f, 0.272f, 0.0f, 0.533f, 0.317f, 1.0f, 0.282f, 0.0f, 0.534f, 0.289f, 1.0f, 0.292f, 0.0f, 0.535f, 0.258f, 1.0f, + 0.301f, 0.0f, 0.535f, 0.224f, 1.0f, 0.311f, 0.0f, 0.536f, 0.189f, 1.0f, 0.32f, 0.0f, 0.536f, 0.153f, 1.0f, 0.328f, 0.0f, 0.536f, 0.117f, 1.0f, + 0.338f, 0.0f, 0.537f, 0.084f, 1.0f, 0.346f, 0.0f, 0.537f, 0.057f, 1.0f, 0.353f, 0.0f, 0.536f, 0.037f, 1.0f, 0.361f, 0.0f, 0.536f, 0.022f, 1.0f, + 0.37f, 0.0f, 0.537f, 0.013f, 1.0f, 0.376f, 0.0f, 0.536f, 0.007f, 1.0f, 0.384f, 0.0f, 0.536f, 0.004f, 1.0f, 0.39f, 0.0f, 0.536f, 0.002f, 1.0f, + 0.399f, 0.0f, 0.535f, 0.0f, 1.0f, +}; + +static const float data33[69 * GP_PRIM_DATABUF_SIZE] = { + -0.308f, 0.0f, 0.151f, 0.363f, 1.0f, -0.31f, 0.0f, 0.15f, 0.377f, 1.0f, -0.311f, 0.0f, 0.149f, 0.386f, 1.0f, -0.313f, 0.0f, 0.149f, 0.397f, 1.0f, + -0.314f, 0.0f, 0.149f, 0.408f, 1.0f, -0.316f, 0.0f, 0.148f, 0.42f, 1.0f, -0.318f, 0.0f, 0.148f, 0.431f, 1.0f, -0.32f, 0.0f, 0.148f, 0.443f, 1.0f, + -0.322f, 0.0f, 0.148f, 0.455f, 1.0f, -0.325f, 0.0f, 0.149f, 0.467f, 1.0f, -0.327f, 0.0f, 0.149f, 0.478f, 1.0f, -0.33f, 0.0f, 0.151f, 0.49f, 1.0f, + -0.333f, 0.0f, 0.152f, 0.501f, 1.0f, -0.336f, 0.0f, 0.154f, 0.512f, 1.0f, -0.34f, 0.0f, 0.157f, 0.522f, 1.0f, -0.343f, 0.0f, 0.161f, 0.533f, 1.0f, + -0.346f, 0.0f, 0.166f, 0.543f, 1.0f, -0.349f, 0.0f, 0.171f, 0.553f, 1.0f, -0.351f, 0.0f, 0.178f, 0.563f, 1.0f, -0.352f, 0.0f, 0.186f, 0.572f, 1.0f, + -0.353f, 0.0f, 0.193f, 0.582f, 1.0f, -0.352f, 0.0f, 0.2f, 0.591f, 1.0f, -0.351f, 0.0f, 0.206f, 0.6f, 1.0f, -0.349f, 0.0f, 0.211f, 0.608f, 1.0f, + -0.347f, 0.0f, 0.215f, 0.616f, 1.0f, -0.345f, 0.0f, 0.219f, 0.623f, 1.0f, -0.343f, 0.0f, 0.222f, 0.63f, 1.0f, -0.341f, 0.0f, 0.224f, 0.637f, 1.0f, + -0.339f, 0.0f, 0.226f, 0.642f, 1.0f, -0.337f, 0.0f, 0.228f, 0.647f, 1.0f, -0.335f, 0.0f, 0.229f, 0.652f, 1.0f, -0.333f, 0.0f, 0.23f, 0.656f, 1.0f, + -0.332f, 0.0f, 0.231f, 0.66f, 1.0f, -0.33f, 0.0f, 0.232f, 0.663f, 1.0f, -0.328f, 0.0f, 0.232f, 0.666f, 1.0f, -0.327f, 0.0f, 0.233f, 0.669f, 1.0f, + -0.325f, 0.0f, 0.233f, 0.672f, 1.0f, -0.324f, 0.0f, 0.234f, 0.676f, 1.0f, -0.322f, 0.0f, 0.234f, 0.679f, 1.0f, -0.321f, 0.0f, 0.234f, 0.682f, 1.0f, + -0.319f, 0.0f, 0.234f, 0.686f, 1.0f, -0.317f, 0.0f, 0.234f, 0.689f, 1.0f, -0.316f, 0.0f, 0.234f, 0.693f, 1.0f, -0.314f, 0.0f, 0.234f, 0.697f, 1.0f, + -0.312f, 0.0f, 0.233f, 0.701f, 1.0f, -0.31f, 0.0f, 0.232f, 0.705f, 1.0f, -0.307f, 0.0f, 0.231f, 0.709f, 1.0f, -0.305f, 0.0f, 0.23f, 0.713f, 1.0f, + -0.302f, 0.0f, 0.228f, 0.716f, 1.0f, -0.299f, 0.0f, 0.225f, 0.719f, 1.0f, -0.295f, 0.0f, 0.222f, 0.722f, 1.0f, -0.292f, 0.0f, 0.217f, 0.725f, 1.0f, + -0.289f, 0.0f, 0.21f, 0.727f, 1.0f, -0.287f, 0.0f, 0.202f, 0.728f, 1.0f, -0.285f, 0.0f, 0.194f, 0.729f, 1.0f, -0.286f, 0.0f, 0.185f, 0.729f, 1.0f, + -0.287f, 0.0f, 0.178f, 0.728f, 1.0f, -0.289f, 0.0f, 0.171f, 0.726f, 1.0f, -0.292f, 0.0f, 0.166f, 0.723f, 1.0f, -0.294f, 0.0f, 0.162f, 0.717f, 1.0f, + -0.297f, 0.0f, 0.159f, 0.71f, 1.0f, -0.299f, 0.0f, 0.157f, 0.701f, 1.0f, -0.301f, 0.0f, 0.155f, 0.689f, 1.0f, -0.303f, 0.0f, 0.154f, 0.675f, 1.0f, + -0.305f, 0.0f, 0.152f, 0.659f, 1.0f, -0.306f, 0.0f, 0.151f, 0.641f, 1.0f, -0.308f, 0.0f, 0.151f, 0.62f, 1.0f, -0.309f, 0.0f, 0.15f, 0.602f, 1.0f, + -0.31f, 0.0f, 0.15f, 0.572f, 1.0f, +}; + +static const float data34[57 * GP_PRIM_DATABUF_SIZE] = { + 0.302f, 0.0f, 0.166f, 0.25f, 1.0f, 0.301f, 0.0f, 0.167f, 0.319f, 1.0f, 0.3f, 0.0f, 0.167f, 0.363f, 1.0f, 0.299f, 0.0f, 0.167f, 0.414f, 1.0f, + 0.298f, 0.0f, 0.167f, 0.459f, 1.0f, 0.296f, 0.0f, 0.168f, 0.501f, 1.0f, 0.295f, 0.0f, 0.168f, 0.539f, 1.0f, 0.293f, 0.0f, 0.169f, 0.573f, 1.0f, + 0.291f, 0.0f, 0.17f, 0.603f, 1.0f, 0.289f, 0.0f, 0.171f, 0.629f, 1.0f, 0.286f, 0.0f, 0.173f, 0.652f, 1.0f, 0.283f, 0.0f, 0.176f, 0.672f, 1.0f, + 0.279f, 0.0f, 0.18f, 0.69f, 1.0f, 0.276f, 0.0f, 0.186f, 0.705f, 1.0f, 0.272f, 0.0f, 0.195f, 0.719f, 1.0f, 0.271f, 0.0f, 0.205f, 0.73f, 1.0f, + 0.272f, 0.0f, 0.217f, 0.741f, 1.0f, 0.275f, 0.0f, 0.227f, 0.75f, 1.0f, 0.279f, 0.0f, 0.234f, 0.758f, 1.0f, 0.283f, 0.0f, 0.24f, 0.765f, 1.0f, + 0.287f, 0.0f, 0.243f, 0.771f, 1.0f, 0.291f, 0.0f, 0.245f, 0.776f, 1.0f, 0.294f, 0.0f, 0.247f, 0.781f, 1.0f, 0.296f, 0.0f, 0.248f, 0.785f, 1.0f, + 0.299f, 0.0f, 0.249f, 0.789f, 1.0f, 0.301f, 0.0f, 0.249f, 0.793f, 1.0f, 0.303f, 0.0f, 0.249f, 0.796f, 1.0f, 0.305f, 0.0f, 0.25f, 0.799f, 1.0f, + 0.306f, 0.0f, 0.25f, 0.802f, 1.0f, 0.308f, 0.0f, 0.249f, 0.805f, 1.0f, 0.31f, 0.0f, 0.249f, 0.808f, 1.0f, 0.311f, 0.0f, 0.249f, 0.81f, 1.0f, + 0.313f, 0.0f, 0.249f, 0.813f, 1.0f, 0.314f, 0.0f, 0.248f, 0.816f, 1.0f, 0.316f, 0.0f, 0.248f, 0.819f, 1.0f, 0.317f, 0.0f, 0.247f, 0.822f, 1.0f, + 0.319f, 0.0f, 0.246f, 0.825f, 1.0f, 0.321f, 0.0f, 0.245f, 0.828f, 1.0f, 0.323f, 0.0f, 0.244f, 0.832f, 1.0f, 0.325f, 0.0f, 0.243f, 0.835f, 1.0f, + 0.328f, 0.0f, 0.24f, 0.838f, 1.0f, 0.33f, 0.0f, 0.237f, 0.841f, 1.0f, 0.333f, 0.0f, 0.233f, 0.844f, 1.0f, 0.337f, 0.0f, 0.228f, 0.847f, 1.0f, + 0.339f, 0.0f, 0.219f, 0.849f, 1.0f, 0.341f, 0.0f, 0.209f, 0.852f, 1.0f, 0.34f, 0.0f, 0.197f, 0.854f, 1.0f, 0.336f, 0.0f, 0.186f, 0.856f, 1.0f, + 0.331f, 0.0f, 0.178f, 0.858f, 1.0f, 0.325f, 0.0f, 0.173f, 0.86f, 1.0f, 0.321f, 0.0f, 0.17f, 0.861f, 1.0f, 0.318f, 0.0f, 0.169f, 0.862f, 1.0f, + 0.315f, 0.0f, 0.168f, 0.864f, 1.0f, 0.312f, 0.0f, 0.167f, 0.865f, 1.0f, 0.311f, 0.0f, 0.167f, 0.866f, 1.0f, 0.309f, 0.0f, 0.166f, 0.867f, 1.0f, + 0.308f, 0.0f, 0.166f, 0.868f, 1.0f, +}; + +static const float data35[261 * GP_PRIM_DATABUF_SIZE] = { + -0.685f, 0.0f, 0.408f, 0.0f, 1.0f, -0.683f, 0.0f, 0.41f, 0.023f, 1.0f, -0.681f, 0.0f, 0.412f, 0.051f, 1.0f, -0.679f, 0.0f, 0.414f, 0.092f, 1.0f, + -0.678f, 0.0f, 0.415f, 0.125f, 1.0f, -0.676f, 0.0f, 0.417f, 0.149f, 1.0f, -0.674f, 0.0f, 0.419f, 0.167f, 1.0f, -0.672f, 0.0f, 0.42f, 0.183f, 1.0f, + -0.67f, 0.0f, 0.422f, 0.199f, 1.0f, -0.668f, 0.0f, 0.424f, 0.218f, 1.0f, -0.666f, 0.0f, 0.426f, 0.237f, 1.0f, -0.664f, 0.0f, 0.429f, 0.257f, 1.0f, + -0.661f, 0.0f, 0.431f, 0.275f, 1.0f, -0.659f, 0.0f, 0.434f, 0.291f, 1.0f, -0.657f, 0.0f, 0.436f, 0.305f, 1.0f, -0.655f, 0.0f, 0.439f, 0.315f, 1.0f, + -0.653f, 0.0f, 0.442f, 0.322f, 1.0f, -0.65f, 0.0f, 0.444f, 0.327f, 1.0f, -0.648f, 0.0f, 0.447f, 0.331f, 1.0f, -0.646f, 0.0f, 0.45f, 0.334f, 1.0f, + -0.643f, 0.0f, 0.453f, 0.334f, 1.0f, -0.641f, 0.0f, 0.456f, 0.334f, 1.0f, -0.639f, 0.0f, 0.459f, 0.334f, 1.0f, -0.636f, 0.0f, 0.462f, 0.333f, 1.0f, + -0.634f, 0.0f, 0.466f, 0.332f, 1.0f, -0.631f, 0.0f, 0.469f, 0.332f, 1.0f, -0.628f, 0.0f, 0.473f, 0.332f, 1.0f, -0.625f, 0.0f, 0.476f, 0.333f, 1.0f, + -0.622f, 0.0f, 0.48f, 0.335f, 1.0f, -0.618f, 0.0f, 0.483f, 0.338f, 1.0f, -0.615f, 0.0f, 0.488f, 0.342f, 1.0f, -0.611f, 0.0f, 0.492f, 0.347f, 1.0f, + -0.608f, 0.0f, 0.495f, 0.352f, 1.0f, -0.605f, 0.0f, 0.5f, 0.358f, 1.0f, -0.601f, 0.0f, 0.505f, 0.363f, 1.0f, -0.597f, 0.0f, 0.509f, 0.366f, 1.0f, + -0.593f, 0.0f, 0.514f, 0.367f, 1.0f, -0.589f, 0.0f, 0.518f, 0.367f, 1.0f, -0.585f, 0.0f, 0.522f, 0.369f, 1.0f, -0.582f, 0.0f, 0.526f, 0.372f, 1.0f, + -0.578f, 0.0f, 0.531f, 0.376f, 1.0f, -0.575f, 0.0f, 0.535f, 0.382f, 1.0f, -0.571f, 0.0f, 0.539f, 0.388f, 1.0f, -0.567f, 0.0f, 0.543f, 0.394f, 1.0f, + -0.563f, 0.0f, 0.547f, 0.4f, 1.0f, -0.56f, 0.0f, 0.551f, 0.406f, 1.0f, -0.556f, 0.0f, 0.555f, 0.411f, 1.0f, -0.552f, 0.0f, 0.559f, 0.415f, 1.0f, + -0.548f, 0.0f, 0.563f, 0.418f, 1.0f, -0.544f, 0.0f, 0.566f, 0.419f, 1.0f, -0.54f, 0.0f, 0.569f, 0.42f, 1.0f, -0.537f, 0.0f, 0.572f, 0.421f, 1.0f, + -0.533f, 0.0f, 0.576f, 0.421f, 1.0f, -0.529f, 0.0f, 0.579f, 0.421f, 1.0f, -0.526f, 0.0f, 0.582f, 0.422f, 1.0f, -0.523f, 0.0f, 0.585f, 0.422f, 1.0f, + -0.52f, 0.0f, 0.588f, 0.423f, 1.0f, -0.516f, 0.0f, 0.591f, 0.426f, 1.0f, -0.513f, 0.0f, 0.594f, 0.43f, 1.0f, -0.51f, 0.0f, 0.597f, 0.435f, 1.0f, + -0.507f, 0.0f, 0.6f, 0.441f, 1.0f, -0.504f, 0.0f, 0.603f, 0.447f, 1.0f, -0.501f, 0.0f, 0.606f, 0.453f, 1.0f, -0.498f, 0.0f, 0.609f, 0.458f, 1.0f, + -0.496f, 0.0f, 0.611f, 0.461f, 1.0f, -0.493f, 0.0f, 0.614f, 0.465f, 1.0f, -0.49f, 0.0f, 0.616f, 0.468f, 1.0f, -0.487f, 0.0f, 0.619f, 0.472f, 1.0f, + -0.484f, 0.0f, 0.621f, 0.476f, 1.0f, -0.482f, 0.0f, 0.624f, 0.48f, 1.0f, -0.479f, 0.0f, 0.627f, 0.484f, 1.0f, -0.476f, 0.0f, 0.629f, 0.487f, 1.0f, + -0.473f, 0.0f, 0.632f, 0.491f, 1.0f, -0.471f, 0.0f, 0.634f, 0.495f, 1.0f, -0.468f, 0.0f, 0.637f, 0.499f, 1.0f, -0.465f, 0.0f, 0.639f, 0.504f, 1.0f, + -0.462f, 0.0f, 0.641f, 0.508f, 1.0f, -0.459f, 0.0f, 0.643f, 0.513f, 1.0f, -0.456f, 0.0f, 0.646f, 0.519f, 1.0f, -0.453f, 0.0f, 0.648f, 0.525f, 1.0f, + -0.45f, 0.0f, 0.65f, 0.533f, 1.0f, -0.447f, 0.0f, 0.652f, 0.54f, 1.0f, -0.444f, 0.0f, 0.655f, 0.546f, 1.0f, -0.441f, 0.0f, 0.657f, 0.553f, 1.0f, + -0.438f, 0.0f, 0.659f, 0.56f, 1.0f, -0.435f, 0.0f, 0.662f, 0.567f, 1.0f, -0.432f, 0.0f, 0.664f, 0.574f, 1.0f, -0.429f, 0.0f, 0.666f, 0.58f, 1.0f, + -0.426f, 0.0f, 0.669f, 0.585f, 1.0f, -0.423f, 0.0f, 0.671f, 0.591f, 1.0f, -0.419f, 0.0f, 0.673f, 0.595f, 1.0f, -0.416f, 0.0f, 0.675f, 0.6f, 1.0f, + -0.412f, 0.0f, 0.678f, 0.604f, 1.0f, -0.409f, 0.0f, 0.68f, 0.609f, 1.0f, -0.405f, 0.0f, 0.682f, 0.613f, 1.0f, -0.401f, 0.0f, 0.684f, 0.618f, 1.0f, + -0.398f, 0.0f, 0.687f, 0.622f, 1.0f, -0.394f, 0.0f, 0.689f, 0.627f, 1.0f, -0.39f, 0.0f, 0.692f, 0.632f, 1.0f, -0.386f, 0.0f, 0.694f, 0.638f, 1.0f, + -0.381f, 0.0f, 0.697f, 0.643f, 1.0f, -0.377f, 0.0f, 0.7f, 0.649f, 1.0f, -0.373f, 0.0f, 0.702f, 0.654f, 1.0f, -0.368f, 0.0f, 0.705f, 0.659f, 1.0f, + -0.363f, 0.0f, 0.707f, 0.663f, 1.0f, -0.359f, 0.0f, 0.71f, 0.667f, 1.0f, -0.354f, 0.0f, 0.712f, 0.671f, 1.0f, -0.349f, 0.0f, 0.715f, 0.674f, 1.0f, + -0.345f, 0.0f, 0.717f, 0.677f, 1.0f, -0.34f, 0.0f, 0.72f, 0.68f, 1.0f, -0.335f, 0.0f, 0.722f, 0.683f, 1.0f, -0.33f, 0.0f, 0.725f, 0.685f, 1.0f, + -0.326f, 0.0f, 0.727f, 0.687f, 1.0f, -0.321f, 0.0f, 0.73f, 0.689f, 1.0f, -0.316f, 0.0f, 0.732f, 0.691f, 1.0f, -0.312f, 0.0f, 0.734f, 0.693f, 1.0f, + -0.307f, 0.0f, 0.736f, 0.694f, 1.0f, -0.302f, 0.0f, 0.738f, 0.696f, 1.0f, -0.298f, 0.0f, 0.74f, 0.697f, 1.0f, -0.293f, 0.0f, 0.741f, 0.698f, 1.0f, + -0.288f, 0.0f, 0.743f, 0.699f, 1.0f, -0.284f, 0.0f, 0.745f, 0.699f, 1.0f, -0.279f, 0.0f, 0.746f, 0.7f, 1.0f, -0.275f, 0.0f, 0.748f, 0.701f, 1.0f, + -0.27f, 0.0f, 0.749f, 0.702f, 1.0f, -0.265f, 0.0f, 0.751f, 0.702f, 1.0f, -0.261f, 0.0f, 0.752f, 0.704f, 1.0f, -0.256f, 0.0f, 0.753f, 0.705f, 1.0f, + -0.252f, 0.0f, 0.755f, 0.706f, 1.0f, -0.247f, 0.0f, 0.756f, 0.707f, 1.0f, -0.242f, 0.0f, 0.757f, 0.709f, 1.0f, -0.237f, 0.0f, 0.758f, 0.711f, 1.0f, + -0.233f, 0.0f, 0.759f, 0.713f, 1.0f, -0.228f, 0.0f, 0.761f, 0.715f, 1.0f, -0.223f, 0.0f, 0.762f, 0.717f, 1.0f, -0.218f, 0.0f, 0.763f, 0.719f, 1.0f, + -0.213f, 0.0f, 0.764f, 0.721f, 1.0f, -0.209f, 0.0f, 0.765f, 0.723f, 1.0f, -0.204f, 0.0f, 0.765f, 0.726f, 1.0f, -0.199f, 0.0f, 0.766f, 0.728f, 1.0f, + -0.194f, 0.0f, 0.767f, 0.73f, 1.0f, -0.189f, 0.0f, 0.768f, 0.731f, 1.0f, -0.183f, 0.0f, 0.769f, 0.733f, 1.0f, -0.178f, 0.0f, 0.77f, 0.735f, 1.0f, + -0.173f, 0.0f, 0.77f, 0.736f, 1.0f, -0.168f, 0.0f, 0.771f, 0.738f, 1.0f, -0.163f, 0.0f, 0.772f, 0.739f, 1.0f, -0.158f, 0.0f, 0.772f, 0.741f, 1.0f, + -0.152f, 0.0f, 0.773f, 0.742f, 1.0f, -0.147f, 0.0f, 0.774f, 0.744f, 1.0f, -0.142f, 0.0f, 0.774f, 0.746f, 1.0f, -0.137f, 0.0f, 0.775f, 0.748f, 1.0f, + -0.132f, 0.0f, 0.775f, 0.749f, 1.0f, -0.127f, 0.0f, 0.776f, 0.751f, 1.0f, -0.122f, 0.0f, 0.776f, 0.752f, 1.0f, -0.117f, 0.0f, 0.776f, 0.753f, 1.0f, + -0.112f, 0.0f, 0.777f, 0.754f, 1.0f, -0.108f, 0.0f, 0.777f, 0.755f, 1.0f, -0.103f, 0.0f, 0.777f, 0.755f, 1.0f, -0.099f, 0.0f, 0.777f, 0.756f, 1.0f, + -0.095f, 0.0f, 0.778f, 0.757f, 1.0f, -0.09f, 0.0f, 0.778f, 0.758f, 1.0f, -0.086f, 0.0f, 0.778f, 0.759f, 1.0f, -0.082f, 0.0f, 0.778f, 0.759f, 1.0f, + -0.077f, 0.0f, 0.778f, 0.76f, 1.0f, -0.073f, 0.0f, 0.779f, 0.76f, 1.0f, -0.069f, 0.0f, 0.779f, 0.761f, 1.0f, -0.064f, 0.0f, 0.779f, 0.761f, 1.0f, + -0.06f, 0.0f, 0.779f, 0.761f, 1.0f, -0.055f, 0.0f, 0.78f, 0.762f, 1.0f, -0.051f, 0.0f, 0.78f, 0.762f, 1.0f, -0.046f, 0.0f, 0.78f, 0.762f, 1.0f, + -0.041f, 0.0f, 0.78f, 0.762f, 1.0f, -0.037f, 0.0f, 0.781f, 0.762f, 1.0f, -0.032f, 0.0f, 0.781f, 0.763f, 1.0f, -0.027f, 0.0f, 0.781f, 0.763f, 1.0f, + -0.022f, 0.0f, 0.781f, 0.763f, 1.0f, -0.017f, 0.0f, 0.781f, 0.764f, 1.0f, -0.012f, 0.0f, 0.782f, 0.764f, 1.0f, -0.006f, 0.0f, 0.782f, 0.764f, 1.0f, + -0.001f, 0.0f, 0.782f, 0.765f, 1.0f, 0.004f, 0.0f, 0.782f, 0.766f, 1.0f, 0.009f, 0.0f, 0.782f, 0.766f, 1.0f, 0.015f, 0.0f, 0.782f, 0.767f, 1.0f, + 0.02f, 0.0f, 0.782f, 0.768f, 1.0f, 0.025f, 0.0f, 0.782f, 0.769f, 1.0f, 0.031f, 0.0f, 0.782f, 0.77f, 1.0f, 0.036f, 0.0f, 0.782f, 0.771f, 1.0f, + 0.042f, 0.0f, 0.782f, 0.772f, 1.0f, 0.048f, 0.0f, 0.782f, 0.773f, 1.0f, 0.053f, 0.0f, 0.782f, 0.774f, 1.0f, 0.059f, 0.0f, 0.782f, 0.775f, 1.0f, + 0.065f, 0.0f, 0.782f, 0.775f, 1.0f, 0.07f, 0.0f, 0.782f, 0.776f, 1.0f, 0.076f, 0.0f, 0.782f, 0.776f, 1.0f, 0.082f, 0.0f, 0.782f, 0.776f, 1.0f, + 0.088f, 0.0f, 0.782f, 0.776f, 1.0f, 0.094f, 0.0f, 0.782f, 0.777f, 1.0f, 0.1f, 0.0f, 0.781f, 0.777f, 1.0f, 0.106f, 0.0f, 0.781f, 0.778f, 1.0f, + 0.111f, 0.0f, 0.781f, 0.779f, 1.0f, 0.117f, 0.0f, 0.781f, 0.779f, 1.0f, 0.123f, 0.0f, 0.781f, 0.78f, 1.0f, 0.129f, 0.0f, 0.78f, 0.78f, 1.0f, + 0.135f, 0.0f, 0.78f, 0.781f, 1.0f, 0.141f, 0.0f, 0.779f, 0.781f, 1.0f, 0.147f, 0.0f, 0.779f, 0.782f, 1.0f, 0.153f, 0.0f, 0.778f, 0.783f, 1.0f, + 0.159f, 0.0f, 0.777f, 0.784f, 1.0f, 0.165f, 0.0f, 0.776f, 0.785f, 1.0f, 0.171f, 0.0f, 0.775f, 0.786f, 1.0f, 0.178f, 0.0f, 0.774f, 0.787f, 1.0f, + 0.185f, 0.0f, 0.773f, 0.788f, 1.0f, 0.192f, 0.0f, 0.772f, 0.789f, 1.0f, 0.2f, 0.0f, 0.771f, 0.79f, 1.0f, 0.208f, 0.0f, 0.77f, 0.791f, 1.0f, + 0.218f, 0.0f, 0.768f, 0.793f, 1.0f, 0.228f, 0.0f, 0.766f, 0.796f, 1.0f, 0.239f, 0.0f, 0.764f, 0.799f, 1.0f, 0.25f, 0.0f, 0.762f, 0.802f, 1.0f, + 0.261f, 0.0f, 0.759f, 0.806f, 1.0f, 0.271f, 0.0f, 0.755f, 0.81f, 1.0f, 0.282f, 0.0f, 0.752f, 0.815f, 1.0f, 0.293f, 0.0f, 0.748f, 0.819f, 1.0f, + 0.304f, 0.0f, 0.744f, 0.825f, 1.0f, 0.315f, 0.0f, 0.74f, 0.83f, 1.0f, 0.326f, 0.0f, 0.736f, 0.836f, 1.0f, 0.337f, 0.0f, 0.731f, 0.843f, 1.0f, + 0.349f, 0.0f, 0.727f, 0.85f, 1.0f, 0.361f, 0.0f, 0.722f, 0.858f, 1.0f, 0.372f, 0.0f, 0.718f, 0.866f, 1.0f, 0.384f, 0.0f, 0.712f, 0.874f, 1.0f, + 0.395f, 0.0f, 0.706f, 0.882f, 1.0f, 0.407f, 0.0f, 0.7f, 0.89f, 1.0f, 0.418f, 0.0f, 0.693f, 0.898f, 1.0f, 0.43f, 0.0f, 0.685f, 0.905f, 1.0f, + 0.442f, 0.0f, 0.677f, 0.912f, 1.0f, 0.458f, 0.0f, 0.666f, 0.918f, 1.0f, 0.473f, 0.0f, 0.654f, 0.924f, 1.0f, 0.49f, 0.0f, 0.64f, 0.93f, 1.0f, + 0.506f, 0.0f, 0.625f, 0.935f, 1.0f, 0.522f, 0.0f, 0.611f, 0.939f, 1.0f, 0.538f, 0.0f, 0.596f, 0.941f, 1.0f, 0.554f, 0.0f, 0.58f, 0.942f, 1.0f, + 0.569f, 0.0f, 0.564f, 0.941f, 1.0f, 0.584f, 0.0f, 0.548f, 0.935f, 1.0f, 0.598f, 0.0f, 0.533f, 0.925f, 1.0f, 0.612f, 0.0f, 0.517f, 0.91f, 1.0f, + 0.625f, 0.0f, 0.501f, 0.891f, 1.0f, 0.638f, 0.0f, 0.484f, 0.868f, 1.0f, 0.65f, 0.0f, 0.468f, 0.839f, 1.0f, 0.662f, 0.0f, 0.452f, 0.806f, 1.0f, + 0.671f, 0.0f, 0.437f, 0.766f, 1.0f, 0.679f, 0.0f, 0.423f, 0.718f, 1.0f, 0.685f, 0.0f, 0.412f, 0.661f, 1.0f, 0.691f, 0.0f, 0.403f, 0.595f, 1.0f, + 0.697f, 0.0f, 0.396f, 0.519f, 1.0f, 0.701f, 0.0f, 0.391f, 0.44f, 1.0f, 0.704f, 0.0f, 0.387f, 0.344f, 1.0f, 0.707f, 0.0f, 0.384f, 0.264f, 1.0f, + 0.711f, 0.0f, 0.38f, 0.133f, 1.0f, +}; + +/* ***************************************************************** */ +/* Monkey Color Data */ + +static const ColorTemplate gp_monkey_pct_black = { + "Black", + {0.0f, 0.0f, 0.0f, 1.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, +}; + +static const ColorTemplate gp_monkey_pct_skin = { + "Skin", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.733f, 0.567f, 0.359f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_skin_light = { + "Skin_Light", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.913f, 0.828f, 0.637f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_skin_shadow = { + "Skin_Shadow", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.32f, 0.29f, 0.223f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_eyes = { + "Eyes", + {0.553f, 0.39f, 0.266f, 0.0f}, + {0.773f, 0.762f, 0.73f, 1.0f}, +}; + +static const ColorTemplate gp_monkey_pct_pupils = { + "Pupils", + {0.107f, 0.075f, 0.051f, 0.0f}, + {0.153f, 0.057f, 0.063f, 1.0f}, +}; + +/* ***************************************************************** */ +/* Monkey API */ + +/* add a 2D Suzanne (original model created by Matias Mendiola) */ +void ED_gpencil_create_monkey(bContext *C, float mat[4][4]) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPdata *gpd = (bGPdata *)ob->data; + bGPDstroke *gps; + + /* create colors */ + int color_Black = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_black); + int color_Skin = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin); + int color_Skin_Light = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_light); + int color_Skin_Shadow = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_skin_shadow); + int color_Eyes = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_eyes); + int color_Pupils = gpencil_monkey_color(bmain, ob, &gp_monkey_pct_pupils); + + /* layers */ + /* NOTE: For now, we just add new layers, to make it easier to separate out old/new instances */ + bGPDlayer *Colors = BKE_gpencil_layer_addnew(gpd, "Colors", false); + bGPDlayer *Lines = BKE_gpencil_layer_addnew(gpd, "Lines", true); + + /* frames */ + /* NOTE: No need to check for existing, as this will tkae care of it for us */ + bGPDframe *frameColor = BKE_gpencil_frame_addnew(Colors, cfra_eval); + bGPDframe *frameLines = BKE_gpencil_frame_addnew(Lines, cfra_eval); + + /* generate strokes */ + gps = BKE_gpencil_add_stroke(frameColor, color_Skin, 538, 3); + BKE_gpencil_stroke_add_points(gps, data0, 538, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Eyes, 136, 3); + BKE_gpencil_stroke_add_points(gps, data1, 136, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin, 2, 3); + BKE_gpencil_stroke_add_points(gps, data2, 2, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 1, 3); + BKE_gpencil_stroke_add_points(gps, data3, 1, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 1, 3); + BKE_gpencil_stroke_add_points(gps, data4, 1, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 48, 3); + BKE_gpencil_stroke_add_points(gps, data5, 48, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 47, 3); + BKE_gpencil_stroke_add_points(gps, data6, 47, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 162, 3); + BKE_gpencil_stroke_add_points(gps, data7, 162, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 55, 3); + BKE_gpencil_stroke_add_points(gps, data8, 55, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 70, 3); + BKE_gpencil_stroke_add_points(gps, data9, 70, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Light, 227, 3); + BKE_gpencil_stroke_add_points(gps, data10, 227, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 1, 3); + BKE_gpencil_stroke_add_points(gps, data11, 1, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 123, 3); + BKE_gpencil_stroke_add_points(gps, data12, 123, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 125, 3); + BKE_gpencil_stroke_add_points(gps, data13, 125, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 45, 3); + BKE_gpencil_stroke_add_points(gps, data14, 45, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 44, 3); + BKE_gpencil_stroke_add_points(gps, data15, 44, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 84, 3); + BKE_gpencil_stroke_add_points(gps, data16, 84, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 56, 3); + BKE_gpencil_stroke_add_points(gps, data17, 56, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 59, 3); + BKE_gpencil_stroke_add_points(gps, data18, 59, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Skin_Shadow, 100, 3); + BKE_gpencil_stroke_add_points(gps, data19, 100, mat); + + gps = BKE_gpencil_add_stroke(frameColor, color_Eyes, 136, 3); + BKE_gpencil_stroke_add_points(gps, data20, 136, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 353, 3); + BKE_gpencil_stroke_add_points(gps, data21, 353, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 309, 3); + BKE_gpencil_stroke_add_points(gps, data22, 309, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 209, 3); + BKE_gpencil_stroke_add_points(gps, data23, 209, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 133, 3); + BKE_gpencil_stroke_add_points(gps, data24, 133, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 389, 3); + BKE_gpencil_stroke_add_points(gps, data25, 389, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 41, 3); + BKE_gpencil_stroke_add_points(gps, data26, 41, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 77, 3); + BKE_gpencil_stroke_add_points(gps, data27, 77, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 257, 3); + BKE_gpencil_stroke_add_points(gps, data28, 257, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 205, 3); + BKE_gpencil_stroke_add_points(gps, data29, 205, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 33, 3); + BKE_gpencil_stroke_add_points(gps, data30, 33, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 37, 3); + BKE_gpencil_stroke_add_points(gps, data31, 37, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 201, 3); + BKE_gpencil_stroke_add_points(gps, data32, 201, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 69, 3); + BKE_gpencil_stroke_add_points(gps, data33, 69, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Pupils, 57, 3); + BKE_gpencil_stroke_add_points(gps, data34, 57, mat); + + gps = BKE_gpencil_add_stroke(frameLines, color_Black, 261, 3); + BKE_gpencil_stroke_add_points(gps, data35, 261, mat); + + /* update depsgraph */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; +} + + +/* ***************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_brush.c b/source/blender/editors/gpencil/gpencil_brush.c index bd9bfcf7025..0fadef55b9d 100644 --- a/source/blender/editors/gpencil/gpencil_brush.c +++ b/source/blender/editors/gpencil/gpencil_brush.c @@ -48,6 +48,7 @@ #include "BLT_translation.h" +#include "DNA_meshdata_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -60,6 +61,9 @@ #include "BKE_library.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_object_deform.h" +#include "BKE_colortools.h" +#include "BKE_material.h" #include "UI_interface.h" @@ -80,6 +84,9 @@ #include "GPU_immediate_util.h" #include "GPU_state.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ************************************************ */ @@ -89,7 +96,9 @@ typedef struct tGP_BrushEditData { /* Current editor/region/etc. */ /* NOTE: This stuff is mainly needed to handle 3D view projection stuff... */ + Depsgraph *depsgraph; Scene *scene; + Object *object; ScrArea *sa; ARegion *ar; @@ -114,6 +123,10 @@ typedef struct tGP_BrushEditData { /* Start of new sculpt stroke */ bool first; + /* Is multiframe editing enabled, and are we using falloff for that? */ + bool is_multiframe; + bool use_multiframe_falloff; + /* Current frame */ int cfra; @@ -128,6 +141,13 @@ typedef struct tGP_BrushEditData { /* - effect vector (e.g. 2D/3D translation for grab brush) */ float dvec[3]; + /* - multiframe falloff factor */ + float mf_falloff; + + /* active vertex group */ + int vrgroup; + + /* brush geometry (bounding box) */ rcti brush_rect; @@ -147,12 +167,34 @@ typedef struct tGP_BrushEditData { /* Callback for performing some brush operation on a single point */ -typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, int i, +typedef bool (*GP_BrushApplyCb)(tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]); /* ************************************************ */ /* Utility Functions */ +/* apply lock axis reset */ +static void gpsculpt_compute_lock_axis(tGP_BrushEditData *gso, bGPDspoint *pt, const float save_pt[3]) +{ + if (gso->sa->spacetype != SPACE_VIEW3D) { + return; + } + + ToolSettings *ts = gso->scene->toolsettings; + int axis = ts->gp_sculpt.lock_axis; + + /* lock axis control */ + if (axis == 1) { + pt->x = save_pt[0]; + } + if (axis == 2) { + pt->y = save_pt[1]; + } + if (axis == 3) { + pt->z = save_pt[2]; + } +} + /* Context ---------------------------------------- */ /* Get the sculpting settings */ @@ -162,10 +204,18 @@ static GP_BrushEdit_Settings *gpsculpt_get_settings(Scene *scene) } /* Get the active brush */ -static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene) +static GP_EditBrush_Data *gpsculpt_get_brush(Scene *scene, bool is_weight_mode) { GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; - return &gset->brush[gset->brushtype]; + GP_EditBrush_Data *brush = NULL; + if (is_weight_mode) { + brush = &gset->brush[gset->weighttype]; + } + else { + brush = &gset->brush[gset->brushtype]; + } + + return brush; } /* Brush Operations ------------------------------- */ @@ -181,6 +231,14 @@ static bool gp_brush_invert_check(tGP_BrushEditData *gso) invert ^= true; } + /* set temporary status */ + if (invert) { + gso->brush->flag |= GP_EDITBRUSH_FLAG_TMP_INVERT; + } + else { + gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT; + } + return invert; } @@ -208,6 +266,9 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c influence *= fac; } + /* apply multiframe falloff */ + influence *= gso->mf_falloff; + /* return influence */ return influence; } @@ -222,29 +283,34 @@ static float gp_brush_influence_calc(tGP_BrushEditData *gso, const int radius, c /* Smooth Brush */ /* A simple (but slower + inaccurate) smooth-brush implementation to test the algorithm for stroke smoothing */ -static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_smooth_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - GP_EditBrush_Data *brush = gso->brush; + // 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) + if ((gso->settings->flag & + (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS | + GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0) { gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; } /* perform smoothing */ if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_POSITION) { - gp_smooth_stroke(gps, i, inf, affect_pressure); + BKE_gpencil_smooth_stroke(gps, pt_index, inf); } if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_STRENGTH) { - gp_smooth_stroke_strength(gps, i, inf); + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); } if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_THICKNESS) { - gp_smooth_stroke_thickness(gps, i, inf); + BKE_gpencil_smooth_stroke_thickness(gps, pt_index, inf); + } + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_UV) { + BKE_gpencil_smooth_stroke_uv(gps, pt_index, inf); } return true; @@ -254,10 +320,11 @@ static bool gp_brush_smooth_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i /* Line Thickness Brush */ /* Make lines thicker or thinner by the specified amounts */ -static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_thickness_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + bGPDspoint *pt = gps->points + pt_index; float inf; /* Compute strength of effect @@ -294,28 +361,29 @@ static bool gp_brush_thickness_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* Make color more or less transparent by the specified amounts */ static bool gp_brush_strength_apply( - tGP_BrushEditData *gso, bGPDstroke *gps, int i, + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + bGPDspoint *pt = gps->points + pt_index; float inf; /* Compute strength of effect - * - We divide the strength by 10, so that users can set "sane" values. + * - We divide the strength, 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; + inf = gp_brush_influence_calc(gso, radius, co) / 20.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 */ + /* make line more transparent - reduce alpha factor */ pt->strength -= inf; } else { - /* make line thicker - increase stroke pressure */ + /* make line more opaque - increase stroke strength */ pt->strength += inf; } + /* smooth the strength */ + BKE_gpencil_smooth_stroke_strength(gps, pt_index, inf); /* Strength should stay within [0.0, 1.0] */ CLAMP(pt->strength, 0.0f, 1.0f); @@ -382,8 +450,9 @@ static void gp_brush_grab_stroke_init(tGP_BrushEditData *gso, bGPDstroke *gps) } /* store references to stroke points in the initial stage */ -static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_grab_store_points( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); float inf = gp_brush_influence_calc(gso, radius, co); @@ -392,7 +461,7 @@ static bool gp_brush_grab_store_points(tGP_BrushEditData *gso, bGPDstroke *gps, BLI_assert(data->size < data->capacity); /* insert this point into the set of affected points */ - data->points[data->size] = i; + data->points[data->size] = pt_index; data->weights[data->size] = inf; data->size++; @@ -431,7 +500,7 @@ 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, bool parented, float diff_mat[4][4]) + tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4]) { tGPSB_Grab_StrokeData *data = BLI_ghash_lookup(gso->stroke_customdata, gps); int i; @@ -443,23 +512,21 @@ static void gp_brush_grab_apply_cached( /* 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); - } + float fpt[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + /* apply transformation */ + mul_v3_m4v3(fpt, diff_mat, &pt->x); + /* apply */ + add_v3_v3v3(&pt->x, fpt, delta); + /* 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); + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); } } @@ -480,10 +547,14 @@ static void gp_brush_grab_stroke_free(void *ptr) /* Push Brush */ /* NOTE: Depends on gp_brush_grab_calc_dvec() */ -static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_push_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + bGPDspoint *pt = gps->points + pt_index; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); + float inf = gp_brush_influence_calc(gso, radius, co); float delta[3] = {0.0f}; @@ -493,6 +564,9 @@ static bool gp_brush_push_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, /* apply */ add_v3_v3(&pt->x, delta); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + /* done */ return true; } @@ -512,7 +586,8 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) float *rvec = ED_view3d_cursor3d_get(gso->scene, v3d)->location; float zfac = ED_view3d_calc_zfac(rv3d, rvec, NULL); - float mval_f[2] = {UNPACK2(gso->mval)}; + float mval_f[2]; + copy_v2fl_v2i(mval_f, gso->mval); float mval_prj[2]; float dvec[3]; @@ -536,12 +611,15 @@ static void gp_brush_calc_midpoint(tGP_BrushEditData *gso) } /* Shrink distance between midpoint and this point... */ -static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_pinch_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + bGPDspoint *pt = gps->points + pt_index; float fac, inf; float vec[3]; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); /* Scale down standard influence value to get it more manageable... * - No damping = Unmanageable at > 0.5 strength @@ -571,6 +649,9 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, /* 3) Translate back to original space, with the shrinkage applied */ add_v3_v3v3(&pt->x, gso->dvec, vec); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); + /* done */ return true; } @@ -582,11 +663,14 @@ static bool gp_brush_pinch_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, * convert the rotated point and convert it into "data" space */ -static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_twist_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + bGPDspoint *pt = gps->points + pt_index; float angle, inf; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); /* Angle to rotate by */ inf = gp_brush_influence_calc(gso, radius, co); @@ -615,6 +699,9 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, sub_v3_v3v3(vec, &pt->x, gso->dvec); /* make relative to center (center is stored in dvec) */ mul_m3_v3(rmat, vec); add_v3_v3v3(&pt->x, vec, gso->dvec); /* restore */ + + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); } else { const float axis[3] = {0.0f, 0.0f, 1.0f}; @@ -654,10 +741,13 @@ static bool gp_brush_twist_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, /* Randomize Brush */ /* Apply some random jitter to the point */ -static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, int i, - const int radius, const int co[2]) +static bool gp_brush_randomize_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) { - bGPDspoint *pt = gps->points + i; + bGPDspoint *pt = gps->points + pt_index; + float save_pt[3]; + copy_v3_v3(save_pt, &pt->x); /* Amount of jitter to apply depends on the distance of the point to the cursor, * as well as the strength of the brush @@ -665,9 +755,11 @@ 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_rng_get_float(gso->rng) * inf; /* 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) + if ((gso->settings->flag & + (GP_BRUSHEDIT_FLAG_APPLY_POSITION | + GP_BRUSHEDIT_FLAG_APPLY_STRENGTH | + GP_BRUSHEDIT_FLAG_APPLY_THICKNESS | + GP_BRUSHEDIT_FLAG_APPLY_UV)) == 0) { gso->settings->flag |= GP_BRUSHEDIT_FLAG_APPLY_POSITION; } @@ -710,6 +802,8 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in float dvec[3]; ED_view3d_win_to_delta(gso->gsc.ar, svec, dvec, zfac); add_v3_v3(&pt->x, dvec); + /* compute lock axis */ + gpsculpt_compute_lock_axis(gso, pt, save_pt); } } else { @@ -749,11 +843,76 @@ static bool gp_brush_randomize_apply(tGP_BrushEditData *gso, bGPDstroke *gps, in /* only limit lower value */ CLAMP_MIN(pt->pressure, 0.0f); } + /* apply random to UV (use pressure) */ + if (gso->settings->flag & GP_BRUSHEDIT_FLAG_APPLY_UV) { + if (BLI_rng_get_float(gso->rng) > 0.5f) { + pt->uv_rot += fac; + } + else { + pt->uv_rot -= fac; + } + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); + } /* done */ return true; } +/* Weight Paint Brush */ + +/* Change weight paint for vertex groups */ +static bool gp_brush_weight_apply( + tGP_BrushEditData *gso, bGPDstroke *gps, int pt_index, + const int radius, const int co[2]) +{ + bGPDspoint *pt = gps->points + pt_index; + MDeformVert *dvert = gps->dvert + pt_index; + 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; + + /* need a vertex group */ + if (gso->vrgroup == -1) { + if (gso->object) { + BKE_object_defgroup_add(gso->object); + gso->vrgroup = 0; + } + } + /* get current weight */ + float curweight = 0.0f; + for (int i = 0; i < dvert->totweight; i++) { + MDeformWeight *gpw = &dvert->dw[i]; + if (gpw->def_nr == gso->vrgroup) { + curweight = gpw->weight; + break; + } + } + + if (gp_brush_invert_check(gso)) { + /* reduce weight */ + curweight -= inf; + } + else { + /* increase weight */ + curweight += inf; + } + + CLAMP(curweight, 0.0f, 1.0f); + BKE_gpencil_vgroup_add_point_weight(dvert, gso->vrgroup, curweight); + + /* weight should stay within [0.0, 1.0] */ + if (pt->pressure < 0.0f) + pt->pressure = 0.0f; + + return true; +} + + + /* ************************************************ */ /* Non Callback-Based Brushes */ @@ -827,7 +986,7 @@ static void gp_brush_clone_init(bContext *C, tGP_BrushEditData *gso) /* Init colormap for mapping between the pasted stroke's source colour(names) * and the final colours that will be used here instead... */ - data->new_colors = gp_copybuf_validate_colormap(gso->gpd); + data->new_colors = gp_copybuf_validate_colormap(C); } /* Free custom data used for "clone" brush */ @@ -857,9 +1016,12 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) { tGPSB_CloneBrushData *data = gso->customdata; - Scene *scene = gso->scene; + Object *ob = CTX_data_active_object(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, true); bGPDstroke *gps; float delta[3]; @@ -882,17 +1044,22 @@ static void gp_brush_clone_add(bContext *C, tGP_BrushEditData *gso) new_stroke = MEM_dupallocN(gps); new_stroke->points = MEM_dupallocN(gps->points); + new_stroke->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); new_stroke->triangles = MEM_dupallocN(gps->triangles); new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); /* Fix color references */ - BLI_assert(new_stroke->colorname[0] != '\0'); - new_stroke->palcolor = BLI_ghash_lookup(data->new_colors, new_stroke->colorname); - - BLI_assert(new_stroke->palcolor != NULL); - BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); + Material *ma = BLI_ghash_lookup(data->new_colors, &new_stroke->mat_nr); + if ((ma) && (BKE_object_material_slot_find_index(ob, ma) > 0)) { + gps->mat_nr = BKE_object_material_slot_find_index(ob, ma) - 1; + CLAMP_MIN(gps->mat_nr, 0); + } + else { + gps->mat_nr = 0; /* only if the color is not found */ + } /* Adjust all the stroke's points, so that the strokes * get pasted relative to where the cursor is now @@ -977,57 +1144,6 @@ static bool gpsculpt_brush_apply_clone(bContext *C, tGP_BrushEditData *gso) } /* ************************************************ */ -/* Cursor drawing */ - -/* Helper callback for drawing the cursor itself */ -static void gp_brush_drawcursor(bContext *C, int x, int y, void *UNUSED(customdata)) -{ - GP_EditBrush_Data *brush = gpsculpt_get_brush(CTX_data_scene(C)); - - if (brush) { - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - GPU_line_smooth(true); - GPU_blend(true); - - /* Inner Ring: Light color for action of the brush */ - /* TODO: toggle between add and remove? */ - immUniformColor4ub(255, 255, 255, 200); - imm_draw_circle_wire_2d(pos, x, y, brush->size, 40); - - /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ - immUniformColor3ub(30, 30, 30); - imm_draw_circle_wire_2d(pos, x, y, brush->size + 1, 40); - - immUnbindProgram(); - - GPU_blend(false); - GPU_line_smooth(false); - } -} - -/* Turn brush cursor in on/off */ -static void gpencil_toggle_brush_cursor(bContext *C, bool enable) -{ - GP_BrushEdit_Settings *gset = gpsculpt_get_settings(CTX_data_scene(C)); - - if (gset->paintcursor && !enable) { - /* clear cursor */ - WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); - gset->paintcursor = NULL; - } - else if (enable) { - /* enable cursor */ - gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C), - NULL, - gp_brush_drawcursor, NULL); - } -} - - -/* ************************************************ */ /* Header Info for GPencil Sculpt */ static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) @@ -1054,17 +1170,40 @@ static void gpsculpt_brush_header_set(bContext *C, tGP_BrushEditData *gso) static bool gpsculpt_brush_init(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); + + const bool is_weight_mode = ob->mode == OB_MODE_GPENCIL_WEIGHT; + /* set the brush using the tool */ + GP_BrushEdit_Settings *gset = &ts->gp_sculpt; + eGP_EditBrush_Types mode = RNA_enum_get(op->ptr, "mode"); + const bool keep_brush = RNA_boolean_get(op->ptr, "keep_brush"); + + if (!keep_brush) { + if (is_weight_mode) { + gset->weighttype = mode; + } + else { + gset->brushtype = mode; + } + } tGP_BrushEditData *gso; /* setup operator data */ gso = MEM_callocN(sizeof(tGP_BrushEditData), "tGP_BrushEditData"); op->customdata = gso; + gso->depsgraph = CTX_data_depsgraph(C); /* store state */ gso->settings = gpsculpt_get_settings(scene); - gso->brush = gpsculpt_get_brush(scene); + gso->brush = gpsculpt_get_brush(scene, is_weight_mode); - gso->brush_type = gso->settings->brushtype; + if (is_weight_mode) { + gso->brush_type = gso->settings->weighttype; + } + else { + gso->brush_type = gso->settings->brushtype; + } /* Random generator, only init once. */ uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); @@ -1078,10 +1217,31 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) gso->cfra = INT_MAX; /* NOTE: So that first stroke will get handled in init_stroke() */ gso->scene = scene; + gso->object = ob; + if (ob) { + gso->vrgroup = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, gso->vrgroup)) { + gso->vrgroup = -1; + } + } + else { + gso->vrgroup = - 1; + } gso->sa = CTX_wm_area(C); gso->ar = CTX_wm_region(C); + /* multiframe settings */ + gso->is_multiframe = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gso->gpd); + gso->use_multiframe_falloff = (ts->gp_sculpt.flag & GP_BRUSHEDIT_FLAG_FRAME_FALLOFF) != 0; + + /* init multiedit falloff curve data before doing anything, + * so we won't have to do it again later + */ + if (gso->is_multiframe) { + curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + /* initialise custom data for brushes */ switch (gso->brush_type) { case GP_EDITBRUSH_TYPE_CLONE: @@ -1133,9 +1293,10 @@ static bool gpsculpt_brush_init(bContext *C, wmOperator *op) gpsculpt_brush_header_set(C, gso); /* setup cursor drawing */ - WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); - gpencil_toggle_brush_cursor(C, true); - + //WM_cursor_modal_set(CTX_wm_window(C), BC_CROSSCURSOR); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } return true; } @@ -1179,7 +1340,12 @@ static void gpsculpt_brush_exit(bContext *C, wmOperator *op) /* disable cursor and headerprints */ ED_workspace_status_text(C, NULL); WM_cursor_modal_restore(win); - gpencil_toggle_brush_cursor(C, false); + if (gso->sa->spacetype != SPACE_VIEW3D) { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + + /* disable temp invert flag */ + gso->brush->flag &= ~GP_EDITBRUSH_FLAG_TMP_INVERT; /* free operator data */ MEM_freeN(gso); @@ -1197,13 +1363,13 @@ static bool gpsculpt_brush_poll(bContext *C) static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) { - Scene *scene = gso->scene; bGPdata *gpd = gso->gpd; + bGPDlayer *gpl; - int cfra = CFRA; + int cfra_eval = (int)DEG_get_ctime(gso->depsgraph); /* only try to add a new frame if this is the first stroke, or the frame has changed */ - if ((gpd == NULL) || (cfra == gso->cfra)) + if ((gpd == NULL) || (cfra_eval == gso->cfra)) return; /* go through each layer, and ensure that we've got a valid frame to use */ @@ -1217,21 +1383,21 @@ static void gpsculpt_brush_init_stroke(tGP_BrushEditData *gso) * spent too much time editing the wrong frame... */ // XXX: should this be allowed when framelock is enabled? - if (gpf->framenum != cfra) { - BKE_gpencil_frame_addcopy(gpl, cfra); + if (gpf->framenum != cfra_eval) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); } } } /* save off new current frame, so that next update works fine */ - gso->cfra = cfra; + gso->cfra = cfra_eval; } /* Apply ----------------------------------------------- */ /* Apply brush operation to points in this stroke */ static bool gpsculpt_brush_do_stroke( - tGP_BrushEditData *gso, bGPDstroke *gps, bool parented, + tGP_BrushEditData *gso, bGPDstroke *gps, float diff_mat[4][4], GP_BrushApplyCb apply) { GP_SpaceConversion *gsc = &gso->gsc; @@ -1246,14 +1412,9 @@ static bool gpsculpt_brush_do_stroke( bool changed = false; if (gps->totpoints == 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]); - } + 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])) { @@ -1280,19 +1441,12 @@ static bool gpsculpt_brush_do_stroke( continue; } } - 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]); - } + 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])) || @@ -1342,9 +1496,114 @@ static bool gpsculpt_brush_do_stroke( return changed; } +/* Apply sculpt brushes to strokes in the given frame */ +static bool gpsculpt_brush_do_frame( + bContext *C, tGP_BrushEditData *gso, + bGPDlayer *gpl, bGPDframe *gpf, + float diff_mat[4][4]) +{ + bool changed = false; + Object *ob = CTX_data_active_object(C); + + for (bGPDstroke *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(ob, gpl, gps) == false) { + continue; + } + + switch (gso->brush_type) { + case GP_EDITBRUSH_TYPE_SMOOTH: /* Smooth strokes */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_smooth_apply); + break; + } + + case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_thickness_apply); + break; + } + + case GP_EDITBRUSH_TYPE_STRENGTH: /* Adjust stroke color strength */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, 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, diff_mat, gp_brush_grab_store_points); + } + else { + /* Apply effect to the stored points */ + gp_brush_grab_apply_cached(gso, gps, diff_mat); + changed |= true; + } + break; + } + + case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_push_apply); + break; + } + + case GP_EDITBRUSH_TYPE_PINCH: /* Pinch points */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_pinch_apply); + break; + } + + case GP_EDITBRUSH_TYPE_TWIST: /* Twist points around midpoint */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_twist_apply); + break; + } + + case GP_EDITBRUSH_TYPE_RANDOMIZE: /* Apply jitter */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_randomize_apply); + break; + } + + case GP_EDITBRUSH_TYPE_WEIGHT: /* Adjust vertex group weight */ + { + changed |= gpsculpt_brush_do_stroke(gso, gps, diff_mat, gp_brush_weight_apply); + break; + } + + + 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; + } + } + + return changed; +} + /* Perform two-pass brushes which modify the existing strokes */ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) { + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = gso->object; + bGPdata *gpd = gso->gpd; bool changed = false; /* Calculate brush-specific data which applies equally to all points */ @@ -1378,104 +1637,53 @@ static bool gpsculpt_brush_apply_standard(bContext *C, tGP_BrushEditData *gso) /* Find visible strokes, and perform operations on those if hit */ - float diff_mat[4][4]; - bool parented = false; - CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - if (gpf == NULL) + /* If no active frame, don't do anything... */ + if (gpl->actframe == NULL) { continue; - - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - parented = true; - } - else { - parented = false; } - for (bGPDstroke *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; - } + /* calculate difference matrix */ + float diff_mat[4][4]; + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); - 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; - } + /* Active Frame or MultiFrame? */ + if (gso->is_multiframe) { + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; - case GP_EDITBRUSH_TYPE_THICKNESS: /* Adjust stroke thickness */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_thickness_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; - } + if (gso->use_multiframe_falloff) { + BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); + } - 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); + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + /* Always do active frame; Otherwise, only include selected frames */ + if ((gpf == gpl->actframe) || (gpf->flag & GP_FRAME_SELECT)) { + /* compute multiframe falloff factor */ + if (gso->use_multiframe_falloff) { + /* Faloff depends on distance to active frame (relative to the overall frame range) */ + gso->mf_falloff = BKE_gpencil_multiframe_falloff_calc( + gpf, gpl->actframe->framenum, + f_init, f_end, + ts->gp_sculpt.cur_falloff); } else { - /* Apply effect to the stored points */ - gp_brush_grab_apply_cached(gso, gps, parented, diff_mat); - changed |= true; + /* No falloff */ + gso->mf_falloff = 1.0f; } - break; - } - case GP_EDITBRUSH_TYPE_PUSH: /* Push points */ - { - changed |= gpsculpt_brush_do_stroke(gso, gps, parented, diff_mat, gp_brush_push_apply); - break; + /* affect strokes in this frame */ + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpf, diff_mat); } - - 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; - } - /* Triangulation must be calculated if changed */ - if (changed) { - gps->flag |= GP_STROKE_RECALC_CACHES; - gps->tot_triangles = 0; } } + else { + /* Apply to active frame's strokes */ + gso->mf_falloff = 1.0f; + changed |= gpsculpt_brush_do_frame(C, gso, gpl, gpl->actframe, diff_mat); + } } CTX_DATA_END; @@ -1529,6 +1737,7 @@ static void gpsculpt_brush_apply(bContext *C, wmOperator *op, PointerRNA *itempt /* Updates */ if (changed) { + DEG_id_tag_update(&gso->gpd->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } @@ -1852,6 +2061,7 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even /* Redraw toolsettings (brush settings)? */ if (redraw_toolsettings) { + DEG_id_tag_update(&gso->gpd->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); } @@ -1860,6 +2070,19 @@ static int gpsculpt_brush_modal(bContext *C, wmOperator *op, const wmEvent *even /* Operator --------------------------------------------- */ +static const EnumPropertyItem prop_gpencil_sculpt_brush_items[] = { + {GP_EDITBRUSH_TYPE_SMOOTH, "SMOOTH", 0, "Smooth", "Smooth stroke points" }, + {GP_EDITBRUSH_TYPE_THICKNESS, "THICKNESS", 0, "Thickness", "Adjust thickness of strokes" }, + {GP_EDITBRUSH_TYPE_STRENGTH, "STRENGTH", 0, "Strength", "Adjust color strength of strokes" }, + {GP_EDITBRUSH_TYPE_GRAB, "GRAB", 0, "Grab", "Translate the set of points initially within the brush circle" }, + {GP_EDITBRUSH_TYPE_PUSH, "PUSH", 0, "Push", "Move points out of the way, as if combing them" }, + {GP_EDITBRUSH_TYPE_TWIST, "TWIST", 0, "Twist", "Rotate points around the midpoint of the brush" }, + {GP_EDITBRUSH_TYPE_PINCH, "PINCH", 0, "Pinch", "Pull points towards the midpoint of the brush" }, + {GP_EDITBRUSH_TYPE_RANDOMIZE, "RANDOMIZE", 0, "Randomize", "Introduce jitter/randomness into strokes" }, + {GP_EDITBRUSH_TYPE_CLONE, "CLONE", 0, "Clone", "Paste copies of the strokes stored on the clipboard" }, + {GP_EDITBRUSH_TYPE_WEIGHT, "WEIGHT", 0, "Weight", "Weight Paint" }, + {0, NULL, 0, NULL, NULL } +}; void GPENCIL_OT_brush_paint(wmOperatorType *ot) { @@ -1879,13 +2102,20 @@ void GPENCIL_OT_brush_paint(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", prop_gpencil_sculpt_brush_items, 0, "Mode", "Brush mode"); + RNA_def_property_flag(ot->prop, PROP_HIDDEN | PROP_SKIP_SAVE); + PropertyRNA *prop; 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", "Enter a mini 'sculpt-mode' if enabled, otherwise, exit after drawing a single stroke"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "keep_brush", false, "Keep Brush", + "Keep current brush activated"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ************************************************ */ diff --git a/source/blender/editors/gpencil/gpencil_convert.c b/source/blender/editors/gpencil/gpencil_convert.c index ccdbcba71f9..f662e9b42be 100644 --- a/source/blender/editors/gpencil/gpencil_convert.c +++ b/source/blender/editors/gpencil/gpencil_convert.c @@ -56,7 +56,6 @@ #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_gpencil_types.h" -#include "DNA_workspace_types.h" #include "BKE_collection.h" #include "BKE_context.h" @@ -74,6 +73,7 @@ #include "BKE_tracking.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "UI_interface.h" @@ -149,28 +149,24 @@ static const EnumPropertyItem *rna_GPConvert_mode_items(bContext *UNUSED(C), Poi * - assumes that the active space is the 3D-View */ static void gp_strokepoint_convertcoords( - bContext *C, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *source_pt, + bContext *C, bGPdata *gpd, 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); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(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); - } + /* apply parent transform */ + float fpt[3]; + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + mul_v3_m4v3(fpt, diff_mat, &source_pt->x); + copy_v3_v3(&pt->x, fpt); if (gps->flag & GP_STROKE_3DSPACE) { @@ -591,7 +587,7 @@ static void gp_stroke_to_path_add_point(tGpTimingData *gtd, BPoint *bp, const fl } } -static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, +static void gp_stroke_to_path(bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, const bool add_end_point, tGpTimingData *gtd) { @@ -655,7 +651,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, gpl, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); if (prev_bp) { interp_v3_v3v3(p1, bp->vec, prev_bp->vec, -GAP_DFAC); if (do_gtd) { @@ -676,7 +672,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, gpl, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpd, 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); @@ -697,9 +693,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, gpl, gps, gps->points, p, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, gps->points, p, subrect); if (gps->totpoints > 1) { - gp_strokepoint_convertcoords(C, gpl, gps, gps->points + 1, next_p, subrect); + gp_strokepoint_convertcoords(C, gpd, 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); @@ -728,10 +724,10 @@ static void gp_stroke_to_path(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curv i++, pt++, bp++) { float p[3]; - float width = pt->pressure * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; /* get coordinates to add at */ - gp_strokepoint_convertcoords(C, gpl, gps, pt, p, subrect); + gp_strokepoint_convertcoords(C, gpd, 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); @@ -801,7 +797,7 @@ static void gp_stroke_to_bezier_add_point(tGpTimingData *gtd, BezTriple *bezt, } } -static void gp_stroke_to_bezier(bContext *C, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, +static void gp_stroke_to_bezier(bContext *C, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, Curve *cu, rctf *subrect, Nurb **curnu, float minmax_weights[2], const float rad_fac, bool stitch, const bool add_start_point, const bool add_end_point, tGpTimingData *gtd) { @@ -843,12 +839,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, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt, (stitch) ? p3d_prev : p3d_cur, subrect); if (tot > 1) { - gp_strokepoint_convertcoords(C, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 1, (stitch) ? p3d_cur : p3d_next, subrect); } if (stitch && tot > 2) { - gp_strokepoint_convertcoords(C, gpl, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); } } @@ -967,7 +963,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 * (gps->thickness + gpl->thickness) * WIDTH_CORR_FAC; + float width = pt->pressure * (gps->thickness + gpl->line_change) * WIDTH_CORR_FAC; if (i || old_nbezt) { interp_v3_v3v3(h1, p3d_cur, p3d_prev, BEZT_HANDLE_FAC); @@ -991,7 +987,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, gpl, gps, pt + 2, p3d_next, subrect); + gp_strokepoint_convertcoords(C, gpd, gpl, gps, pt + 2, p3d_next, subrect); } prev_bezt = bezt; @@ -1130,10 +1126,12 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG const bool norm_weights, const float rad_fac, const bool link_strokes, tGpTimingData *gtd) { struct Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); Collection *collection = CTX_data_collection(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); bGPDstroke *gps, *prev_gps = NULL; Object *ob; Curve *cu; @@ -1192,12 +1190,12 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG switch (mode) { case GP_STROKECONVERT_PATH: - gp_stroke_to_path(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, + gp_stroke_to_path(C, gpd, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, add_start_point, add_end_point, gtd); break; case GP_STROKECONVERT_CURVE: case GP_STROKECONVERT_POLY: /* convert after */ - gp_stroke_to_bezier(C, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, + gp_stroke_to_bezier(C, gpd, gpl, gps, cu, subrect_ptr, &nu, minmax_weights, rad_fac, stitch, add_start_point, add_end_point, gtd); break; default: @@ -1238,7 +1236,9 @@ static void gp_layer_to_curve(bContext *C, ReportList *reports, bGPdata *gpd, bG */ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDframe *gpf = NULL; bGPDstroke *gps = NULL; bGPDspoint *pt; @@ -1246,7 +1246,7 @@ static bool gp_convert_check_has_valid_timing(bContext *C, bGPDlayer *gpl, wmOpe int i; bool valid = true; - if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) || !(gps = gpf->strokes.first)) + if (!gpl || !(gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0)) || !(gps = gpf->strokes.first)) return false; do { @@ -1294,10 +1294,12 @@ static void gp_convert_set_end_frame(struct Main *UNUSED(main), struct Scene *UN static bool gp_convert_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDlayer *gpl = NULL; bGPDframe *gpf = NULL; ScrArea *sa = CTX_wm_area(C); - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); /* only if the current view is 3D View, if there's valid data (i.e. at least one stroke!), @@ -1305,7 +1307,7 @@ static bool gp_convert_poll(bContext *C) */ return ((sa && sa->spacetype == SPACE_VIEW3D) && (gpl = BKE_gpencil_layer_getactive(gpd)) && - (gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0)) && + (gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0)) && (gpf->strokes.first) && (OBEDIT_FROM_VIEW_LAYER(view_layer) == NULL)); } @@ -1382,8 +1384,9 @@ static int gp_convert_layer_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static bool gp_convert_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +static bool gp_convert_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop) { + PointerRNA *ptr = op->ptr; const char *prop_id = RNA_property_identifier(prop); const bool link_strokes = RNA_boolean_get(ptr, "use_link_strokes"); int timing_mode = RNA_enum_get(ptr, "timing_mode"); @@ -1446,18 +1449,6 @@ static bool gp_convert_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) return false; } -static void gp_convert_ui(bContext *C, wmOperator *op) -{ - uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - /* Main auto-draw call */ - uiDefAutoButsRNA(layout, &ptr, gp_convert_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); -} - void GPENCIL_OT_convert(wmOperatorType *ot) { PropertyRNA *prop; @@ -1471,7 +1462,7 @@ void GPENCIL_OT_convert(wmOperatorType *ot) ot->invoke = WM_menu_invoke; ot->exec = gp_convert_layer_exec; ot->poll = gp_convert_poll; - ot->ui = gp_convert_ui; + ot->poll_property = gp_convert_poll_property; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index c28fea0fc41..01cf8aeb7f1 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -46,18 +46,31 @@ #include "BLT_translation.h" +#include "DNA_anim_types.h" +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" -#include "DNA_gpencil_types.h" -#include "BKE_colortools.h" +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_animsys.h" #include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_fcurve.h" +#include "BKE_global.h" #include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" #include "BKE_library.h" -#include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_object.h" +#include "BKE_material.h" +#include "BKE_paint.h" #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" @@ -72,8 +85,13 @@ #include "RNA_define.h" #include "RNA_enum_types.h" +#include "ED_object.h" #include "ED_gpencil.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ************************************************ */ @@ -84,9 +102,9 @@ /* add new datablock - wrapper around API */ static int gp_data_add_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - ToolSettings *ts = CTX_data_tool_settings(C); + PointerRNA gpd_owner = {{NULL}}; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); + bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); if (gpd_ptr == NULL) { BKE_report(op->reports, RPT_ERROR, "Nowhere for grease pencil data to go"); @@ -94,19 +112,36 @@ static int gp_data_add_exec(bContext *C, wmOperator *op) } else { /* decrement user count and add new datablock */ - bGPdata *gpd = (*gpd_ptr); + /* TODO: if a datablock exists, we should make a copy of it instead of starting fresh (as in other areas) */ + Main *bmain = CTX_data_main(C); - id_us_min(&gpd->id); - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + /* decrement user count of old GP datablock */ + if (*gpd_ptr) { + bGPdata *gpd = (*gpd_ptr); + id_us_min(&gpd->id); + } - /* if not exist brushes, create a new set */ - if (ts) { - if (BLI_listbase_is_empty(&ts->gp_brushes)) { - /* create new brushes */ - BKE_gpencil_brush_init_presets(ts); - } + /* add new datablock, with a single layer ready to use (so users don't have to perform an extra step) */ + if (is_annotation) { + bGPdata *gpd = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + *gpd_ptr = gpd; + + /* tag for annotations */ + gpd->flag |= GP_DATA_ANNOTATIONS; + + /* add new layer (i.e. a "note") */ + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); } + else { + /* GP Object Case - This shouldn't happen! */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + + /* add default sets of colors and brushes */ + ED_gpencil_add_defaults(C); + /* add new layer */ + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + } } /* notifiers */ @@ -185,28 +220,42 @@ void GPENCIL_OT_data_unlink(wmOperatorType *ot) /* add new layer - wrapper around API */ static int gp_layer_add_exec(bContext *C, wmOperator *op) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); - ToolSettings *ts = CTX_data_tool_settings(C); + PointerRNA gpd_owner = {{NULL}}; + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, &gpd_owner); + bool is_annotation = ED_gpencil_data_owner_is_annotation(&gpd_owner); /* 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 = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - /* if not exist brushes, create a new set */ - if (ts) { - if (BLI_listbase_is_empty(&ts->gp_brushes)) { - /* create new brushes */ - BKE_gpencil_brush_init_presets(ts); + if (*gpd_ptr == NULL) { + Main *bmain = CTX_data_main(C); + if (is_annotation) { + /* Annotations */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("Annotations")); + + /* mark as annotation */ + (*gpd_ptr)->flag |= GP_DATA_ANNOTATIONS; + } + else { + /* GP Object */ + /* NOTE: This shouldn't actually happen in practice */ + *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); + + /* add default sets of colors and brushes */ + ED_gpencil_add_defaults(C); } } /* add new layer now */ - BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + if (is_annotation) { + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("Note"), true); + } + else { + BKE_gpencil_layer_addnew(*gpd_ptr, DATA_("GP_Layer"), true); + } /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -219,7 +268,7 @@ void GPENCIL_OT_layer_add(wmOperatorType *ot) /* identifiers */ ot->name = "Add New Layer"; ot->idname = "GPENCIL_OT_layer_add"; - ot->description = "Add new Grease Pencil layer for the active Grease Pencil data-block"; + ot->description = "Add new layer or note for the active data-block"; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -257,6 +306,7 @@ static int gp_layer_remove_exec(bContext *C, wmOperator *op) BKE_gpencil_layer_delete(gpd, gpl); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -296,6 +346,7 @@ static int gp_layer_move_exec(bContext *C, wmOperator *op) BLI_assert(ELEM(direction, -1, 0, 1)); /* we use value below */ if (BLI_listbase_link_move(&gpd->layers, gpl, direction)) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); } @@ -346,6 +397,7 @@ static int gp_layer_copy_exec(bContext *C, wmOperator *UNUSED(op)) BKE_gpencil_layer_setactive(gpd, new_layer); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -366,6 +418,153 @@ void GPENCIL_OT_layer_duplicate(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ********************* Duplicate Frame ************************** */ +enum { + GP_FRAME_DUP_ACTIVE = 0, + GP_FRAME_DUP_ALL = 1 +}; + +static int gp_frame_duplicate_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + int mode = RNA_enum_get(op->ptr, "mode"); + + /* sanity checks */ + if (ELEM(NULL, gpd, gpl)) + return OPERATOR_CANCELLED; + + if (mode == 0) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + else { + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + if ((gpl->flag & GP_LAYER_LOCKED) == 0) { + BKE_gpencil_frame_addcopy(gpl, cfra_eval); + } + } + + } + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_frame_duplicate(wmOperatorType *ot) +{ + static const EnumPropertyItem duplicate_mode[] = { + { GP_FRAME_DUP_ACTIVE, "ACTIVE", 0, "Active", "Duplicate frame in active layer only" }, + { GP_FRAME_DUP_ALL, "ALL", 0, "All", "Duplicate active frames in all layers" }, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Duplicate Frame"; + ot->idname = "GPENCIL_OT_frame_duplicate"; + ot->description = "Make a copy of the active Grease Pencil Frame"; + + /* callbacks */ + ot->exec = gp_frame_duplicate_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); +} + +/* ********************* Clean Fill Boundaries on Frame ************************** */ +enum { + GP_FRAME_CLEAN_FILL_ACTIVE = 0, + GP_FRAME_CLEAN_FILL_ALL = 1 +}; + +static int gp_frame_clean_fill_exec(bContext *C, wmOperator *op) +{ + bool changed = false; + bGPdata *gpd = ED_gpencil_data_get_active(C); + int mode = RNA_enum_get(op->ptr, "mode"); + + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *init_gpf = gpl->actframe; + if (mode == GP_FRAME_CLEAN_FILL_ALL) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || (mode == GP_FRAME_CLEAN_FILL_ALL)) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) + continue; + + /* simply delete strokes which are no fill */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; + + /* free stroke */ + if (gps->flag & GP_STROKE_NOFILL) { + /* free stroke memory arrays, then stroke itself */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + MEM_SAFE_FREE(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + + changed = true; + } + } + } + } + } + CTX_DATA_END; + + /* notifiers */ + if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + } + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_frame_clean_fill(wmOperatorType *ot) +{ + static const EnumPropertyItem duplicate_mode[] = { + { GP_FRAME_CLEAN_FILL_ACTIVE, "ACTIVE", 0, "Active Frame Only", "Clean active frame only" }, + { GP_FRAME_CLEAN_FILL_ALL, "ALL", 0, "All Frames", "Clean all frames in all layers" }, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Clean Fill Boundaries"; + ot->idname = "GPENCIL_OT_frame_clean_fill"; + ot->description = "Remove 'no fill' boundary strokes"; + + /* callbacks */ + ot->exec = gp_frame_clean_fill_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + ot->prop = RNA_def_enum(ot->srna, "mode", duplicate_mode, GP_FRAME_DUP_ACTIVE, "Mode", ""); +} + /* *********************** Hide Layers ******************************** */ static int gp_hide_exec(bContext *C, wmOperator *op) @@ -394,6 +593,7 @@ static int gp_hide_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -481,6 +681,7 @@ static int gp_reveal_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -521,6 +722,7 @@ static int gp_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -558,6 +760,7 @@ static int gp_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -630,6 +833,7 @@ static int gp_isolate_layer_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -673,22 +877,28 @@ static int gp_merge_layer_exec(bContext *C, wmOperator *op) BLI_ghash_insert(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum), gpf); } - /* read all frames from next layer */ + /* read all frames from next layer and add any missing in current layer */ for (bGPDframe *gpf = gpl_next->frames.first; gpf; gpf = gpf->next) { - /* try to find frame in active layer */ + /* try to find frame in current layer */ bGPDframe *frame = BLI_ghash_lookup(gh_frames_cur, SET_INT_IN_POINTER(gpf->framenum)); if (!frame) { - /* nothing found, create new */ + bGPDframe *actframe = BKE_gpencil_layer_getframe(gpl_current, gpf->framenum, GP_GETFRAME_USE_PREV); frame = BKE_gpencil_frame_addnew(gpl_current, gpf->framenum); + /* duplicate strokes of current active frame */ + if (actframe) { + BKE_gpencil_frame_copy_strokes(actframe, frame); + } } /* add to tail all strokes */ BLI_movelisttolist(&frame->strokes, &gpf->strokes); } + /* Now delete next layer */ BKE_gpencil_layer_delete(gpd, gpl_next); BLI_ghash_free(gh_frames_cur, NULL, NULL); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -750,6 +960,7 @@ static int gp_layer_change_exec(bContext *C, wmOperator *op) BKE_gpencil_layer_setactive(gpd, gpl); /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -788,6 +999,7 @@ enum { static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) { + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); bGPDstroke *gps; @@ -797,79 +1009,95 @@ static int gp_stroke_arrange_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - bGPDframe *gpf = gpl->actframe; - /* temp listbase to store selected strokes */ - ListBase selected = {NULL}; const int direction = RNA_enum_get(op->ptr, "direction"); - /* 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) { - return OPERATOR_CANCELLED; + for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* temp listbase to store selected strokes by layer */ + ListBase selected = { NULL }; + bGPDframe *gpf = gpl->actframe; + if (gpl->flag & GP_LAYER_LOCKED) { + continue; + } + + if (gpf == NULL) { + continue; + } + bool gpf_lock = false; + /* 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; } - } - /* some stroke is already at botom */ - if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { - if (gps == gpf->strokes.first) { - return OPERATOR_CANCELLED; + /* check if the color is editable */ + if (ED_gpencil_stroke_color_use(ob, 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) { + gpf_lock = true; + continue; + } + } + /* some stroke is already at botom */ + if ((direction == GP_STROKE_MOVE_BOTTOM) || (direction == GP_STROKE_MOVE_DOWN)) { + if (gps == gpf->strokes.first) { + gpf_lock = true; + continue; + } + } + /* add to list (if not locked) */ + if (!gpf_lock) { + BLI_addtail(&selected, BLI_genericNodeN(gps)); } } - /* 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_addtail(&gpf->strokes, gps); - } - break; - /* Bring Forward */ - case GP_STROKE_MOVE_UP: - for (LinkData *link = selected.last; link; link = link->prev) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, 1); - } - break; - /* Send Backward */ - case GP_STROKE_MOVE_DOWN: - for (LinkData *link = selected.first; link; link = link->next) { - gps = link->data; - BLI_listbase_link_move(&gpf->strokes, gps, -1); - } - 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_addhead(&gpf->strokes, gps); + /* Now do the movement of the stroke */ + if (!gpf_lock) { + 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_addtail(&gpf->strokes, gps); + } + break; + /* Bring Forward */ + case GP_STROKE_MOVE_UP: + for (LinkData *link = selected.last; link; link = link->prev) { + gps = link->data; + BLI_listbase_link_move(&gpf->strokes, gps, 1); + } + break; + /* Send Backward */ + case GP_STROKE_MOVE_DOWN: + for (LinkData *link = selected.first; link; link = link->next) { + gps = link->data; + BLI_listbase_link_move(&gpf->strokes, gps, -1); + } + 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_addhead(&gpf->strokes, gps); + } + break; + default: + BLI_assert(0); + break; } - break; - default: - BLI_assert(0); - break; + } + BLI_freelistN(&selected); } - BLI_freelistN(&selected); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -890,58 +1118,70 @@ void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) ot->idname = "GPENCIL_OT_stroke_arrange"; ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; - /* api callbacks */ + /* callbacks */ ot->exec = gp_stroke_arrange_exec; ot->poll = gp_active_layer_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* properties */ 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; + Object *ob = CTX_data_active_object(C); + Material *ma = give_current_material(ob, ob->actcol); + int idx = BKE_object_material_slot_find_index(ob, ma) - 1; /* sanity checks */ if (ELEM(NULL, gpd)) { return OPERATOR_CANCELLED; } - palette = BKE_gpencil_palette_getactive(gpd); - color = BKE_gpencil_palettecolor_getactive(palette); - if (ELEM(NULL, palette, color)) { + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + if (ELEM(NULL, ma)) { 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; + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - /* asign new color (only if different) */ - if ((STREQ(gps->colorname, color->info) == false) || (gps->palcolor != color)) { - BLI_strncpy(gps->colorname, color->info, sizeof(gps->colorname)); - gps->palcolor = color; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf == NULL) + continue; + + for (bGPDstroke *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(ob, gpl, gps) == false) + continue; + + /* asign new color */ + gps->mat_nr = idx; } } } } } + CTX_DATA_END; + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -952,11 +1192,14 @@ 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"; + ot->description = "Move selected strokes to active material"; - /* api callbacks */ + /* callbacks */ ot->exec = gp_stroke_change_color_exec; ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /* ******************* Lock color of non selected Strokes colors ************************** */ @@ -964,19 +1207,21 @@ void GPENCIL_OT_stroke_change_color(wmOperatorType *ot) static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette; + + Object *ob = CTX_data_active_object(C); + + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); /* sanity checks */ if (ELEM(NULL, gpd)) return OPERATOR_CANCELLED; - palette = BKE_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; + for (short i = 0; i < *totcol; i++) { + Material *tmp_ma = (*matar)[i]; + tmp_ma->gp_style->flag |= GP_STYLE_COLOR_LOCKED; + } /* loop all selected strokes and unlock any color */ @@ -991,14 +1236,14 @@ static int gp_stroke_lock_color_exec(bContext *C, wmOperator *UNUSED(op)) continue; } /* unlock color */ - if (gps->palcolor != NULL) { - gps->palcolor->flag &= ~PC_COLOR_LOCKED; - } + Material *tmp_ma = (*matar)[gps->mat_nr]; + tmp_ma->gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; } } } } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1014,25 +1259,18 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot) /* api callbacks */ ot->exec = gp_stroke_lock_color_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) +/* ******************* Brush create presets ************************** */ +static int gp_brush_presets_create_exec(bContext *C, wmOperator *UNUSED(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 */ - BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + BKE_brush_gpencil_presets(C); /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); @@ -1040,513 +1278,759 @@ static int gp_brush_add_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_brush_add(wmOperatorType *ot) +void GPENCIL_OT_brush_presets_create(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 data-block"; + 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; - /* callbacks */ - ot->exec = gp_brush_add_exec; - ot->poll = gp_add_poll; } -/* ******************* Remove Active Brush ************************* */ +/* ***************** Select Brush ************************ */ -static int gp_brush_remove_exec(bContext *C, wmOperator *op) +static int gp_brush_select_exec(bContext *C, wmOperator *op) { ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); - - /* sanity checks */ - if (ELEM(NULL, ts, brush)) - return OPERATOR_CANCELLED; + Main *bmain = CTX_data_main(C); - if (BLI_listbase_count_at_most(&ts->gp_brushes, 2) < 2) { - BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a brush, unable to delete the last one"); + /* 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"); - /* 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) - BKE_gpencil_brush_setactive(ts, brush->prev); - else - BKE_gpencil_brush_setactive(ts, brush->next); - - /* delete the brush now... */ - BKE_gpencil_brush_delete(ts, brush); + Paint *paint = BKE_brush_get_gpencil_paint(ts); + int i = 0; + for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { + if (brush->ob_mode == OB_MODE_GPENCIL_PAINT) { + if (i == index) { + BKE_paint_brush_set(paint, brush); - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + i++; + } + } - return OPERATOR_FINISHED; + return OPERATOR_CANCELLED; } -void GPENCIL_OT_brush_remove(wmOperatorType *ot) +void GPENCIL_OT_brush_select(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; + ot->name = "Select Brush"; + ot->idname = "GPENCIL_OT_brush_select"; + ot->description = "Select a Grease Pencil drawing brush"; /* callbacks */ - ot->exec = gp_brush_remove_exec; + ot->exec = gp_brush_select_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); + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - return OPERATOR_INTERFACE; + /* properties */ + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Drawing Brush", 0, INT_MAX); } -static int gp_brush_change_exec(bContext *C, wmOperator *op) +/* ***************** Select Sculpt Brush ************************ */ + +static int gp_sculpt_select_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 = BKE_gpencil_brush_addnew(ts, DATA_("GP_Brush"), true); + /* if there's no existing container */ + if (ts == NULL) { + BKE_report(op->reports, RPT_ERROR, "Nowhere to go"); + return OPERATOR_CANCELLED; } - 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; - } + const int index = RNA_int_get(op->ptr, "index"); + GP_BrushEdit_Settings *gp_sculpt = &ts->gp_sculpt; + /* sanity checks */ + if (ELEM(NULL, gp_sculpt)) { + return OPERATOR_CANCELLED; } - /* Set active brush */ - BKE_gpencil_brush_setactive(ts, brush); - - /* updates */ + if (index < TOT_GP_EDITBRUSH_TYPES - 1) { + gp_sculpt->brushtype = index; + } + /* notifiers */ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_brush_change(wmOperatorType *ot) +void GPENCIL_OT_sculpt_select(wmOperatorType *ot) { /* identifiers */ - ot->name = "Change Brush"; - ot->idname = "GPENCIL_OT_brush_change"; - ot->description = "Change active Grease Pencil drawing brush"; + ot->name = "Select Sculpt Brush"; + ot->idname = "GPENCIL_OT_sculpt_select"; + ot->description = "Select a Grease Pencil sculpt brush"; /* callbacks */ - ot->invoke = gp_brush_change_invoke; - ot->exec = gp_brush_change_exec; - ot->poll = gp_active_brush_poll; + ot->exec = gp_sculpt_select_exec; + ot->poll = gp_add_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); + /* properties */ + RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "Index of Sculpt Brush", 0, INT_MAX); } -/* ******************* Move Brush Up/Down ************************** */ +/*********************** Vertex Groups ***********************************/ -enum { - GP_BRUSH_MOVE_UP = -1, - GP_BRUSH_MOVE_DOWN = 1 -}; +static bool gpencil_vertex_group_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + + if ((ob) && (ob->type == OB_GPENCIL)) { + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + if (ELEM(ob->mode, + OB_MODE_GPENCIL_EDIT, + OB_MODE_GPENCIL_SCULPT)) + { + return true; + } + } + } -static int gp_brush_move_exec(bContext *C, wmOperator *op) + return false; +} + +static bool gpencil_vertex_group_weight_poll(bContext *C) { - ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); + Object *ob = CTX_data_active_object(C); - int direction = RNA_enum_get(op->ptr, "type"); + if ((ob) && (ob->type == OB_GPENCIL)) { + if (!ID_IS_LINKED(ob) && !ID_IS_LINKED(ob->data) && ob->defbase.first) { + if (ob->mode == OB_MODE_GPENCIL_WEIGHT) { + return true; + } + } + } + + return false; +} + +static int gpencil_vertex_group_assign_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); /* sanity checks */ - if (ELEM(NULL, ts, brush)) { + if (ELEM(NULL, ts, ob, ob->data)) 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); - } + ED_gpencil_vgroup_assign(C, ob, ts->vgroup_weight); /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_brush_move(wmOperatorType *ot) +void GPENCIL_OT_vertex_group_assign(wmOperatorType *ot) { - static const 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"; + ot->name = "Assign to Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_assign"; + ot->description = "Assign the selected vertices to the active vertex group"; /* api callbacks */ - ot->exec = gp_brush_move_exec; - ot->poll = gp_active_brush_poll; + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_assign_exec; /* 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)) +/* remove point from vertex group */ +static int gpencil_vertex_group_remove_from_exec(bContext *C, wmOperator *UNUSED(op)) { - ToolSettings *ts = CTX_data_tool_settings(C); - BKE_gpencil_brush_init_presets(ts); + Object *ob = CTX_data_active_object(C); + + /* sanity checks */ + if (ELEM(NULL, ob, ob->data)) + return OPERATOR_CANCELLED; + + ED_gpencil_vgroup_remove(C, ob); /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_brush_presets_create(wmOperatorType *ot) +void GPENCIL_OT_vertex_group_remove_from(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"; + ot->name = "Remove from Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_remove_from"; + ot->description = "Remove the selected vertices from active or all vertex group(s)"; /* api callbacks */ - ot->exec = gp_brush_presets_create_exec; + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_remove_from_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ***************** Copy Brush ************************ */ - -static int gp_brush_copy_exec(bContext *C, wmOperator *op) +static int gpencil_vertex_group_select_exec(bContext *C, wmOperator *UNUSED(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 = BKE_gpencil_brush_getactive(ts); - bGPDbrush *newbrush; + Object *ob = CTX_data_active_object(C); /* sanity checks */ - if (ELEM(NULL, brush)) + if (ELEM(NULL, ob, ob->data)) return OPERATOR_CANCELLED; - /* create a brush and duplicate data */ - newbrush = BKE_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); - - BKE_gpencil_brush_setactive(ts, newbrush); + ED_gpencil_vgroup_select(C, ob); + /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_brush_copy(wmOperatorType *ot) +void GPENCIL_OT_vertex_group_select(wmOperatorType *ot) { /* identifiers */ - ot->name = "Copy Brush"; - ot->idname = "GPENCIL_OT_brush_copy"; - ot->description = "Copy current Grease Pencil drawing brush"; + ot->name = "Select Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_select"; + ot->description = "Select all the vertices assigned to the active vertex group"; - /* callbacks */ - ot->exec = gp_brush_copy_exec; - ot->poll = gp_active_brush_poll; + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_select_exec; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ***************** Select Brush ************************ */ - -static int gp_brush_select_exec(bContext *C, wmOperator *op) +static int gpencil_vertex_group_deselect_exec(bContext *C, wmOperator *UNUSED(op)) { - ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(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)) { + if (ELEM(NULL, ob, ob->data)) return OPERATOR_CANCELLED; - } - BKE_gpencil_brush_setactive(ts, brush); + ED_gpencil_vgroup_deselect(C, ob); /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_brush_select(wmOperatorType *ot) +void GPENCIL_OT_vertex_group_deselect(wmOperatorType *ot) { /* identifiers */ - ot->name = "Select Brush"; - ot->idname = "GPENCIL_OT_brush_select"; - ot->description = "Select a Grease Pencil drawing brush"; + ot->name = "Deselect Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_deselect"; + ot->description = "Deselect all selected vertices assigned to the active vertex group"; - /* callbacks */ - ot->exec = gp_brush_select_exec; - ot->poll = gp_active_brush_poll; + /* api callbacks */ + ot->poll = gpencil_vertex_group_poll; + ot->exec = gpencil_vertex_group_deselect_exec; /* 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) +/* invert */ +static int gpencil_vertex_group_invert_exec(bContext *C, wmOperator *UNUSED(op)) { - Main *bmain = CTX_data_main(C); - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); - /* 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"); + /* sanity checks */ + if (ELEM(NULL, ts, ob, ob->data)) return OPERATOR_CANCELLED; - } - if (*gpd_ptr == NULL) - *gpd_ptr = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - /* add new palette now */ - BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); + MDeformVert *dvert; + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return OPERATOR_CANCELLED; + + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + dvert = &gps->dvert[i]; + if (dvert->dw == NULL) { + BKE_gpencil_vgroup_add_point_weight(dvert, def_nr, 1.0f); + } + else if (dvert->dw->weight == 1.0f) { + BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); + } + else { + dvert->dw->weight = 1.0f - dvert->dw->weight; + } + } + } + CTX_DATA_END; /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_palette_add(wmOperatorType *ot) +void GPENCIL_OT_vertex_group_invert(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 data-block"; + ot->name = "Invert Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_invert"; + ot->description = "Invert weights to the active vertex group"; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_invert_exec; - /* callbacks */ - ot->exec = gp_palette_add_exec; - ot->poll = gp_add_poll; + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ******************* Remove Active Palette ************************* */ - -static int gp_palette_remove_exec(bContext *C, wmOperator *op) +/* smooth */ +static int gpencil_vertex_group_smooth_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); + const float fac = RNA_float_get(op->ptr, "factor"); + const int repeat = RNA_int_get(op->ptr, "repeat"); + + ToolSettings *ts = CTX_data_tool_settings(C); + Object *ob = CTX_data_active_object(C); /* sanity checks */ - if (ELEM(NULL, gpd, palette)) + if (ELEM(NULL, ts, ob, ob->data)) return OPERATOR_CANCELLED; - if (BLI_listbase_count_at_most(&gpd->palettes, 2) < 2) { - BKE_report(op->reports, RPT_ERROR, "Grease Pencil needs a palette, unable to delete the last one"); - return OPERATOR_CANCELLED; - } + bGPDspoint *pta, *ptb, *ptc; + MDeformVert *dverta, *dvertb; + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + 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) - BKE_gpencil_palette_setactive(gpd, palette->prev); - else - BKE_gpencil_palette_setactive(gpd, palette->next); + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int s = 0; s < repeat; s++) { + for (int i = 0; i < gps->totpoints; i++) { + /* previous point */ + if (i > 0) { + pta = &gps->points[i - 1]; + dverta = &gps->dvert[i - 1]; + } + else { + pta = &gps->points[i]; + dverta = &gps->dvert[i]; + } + /* current */ + ptb = &gps->points[i]; + dvertb = &gps->dvert[i]; + /* next point */ + if (i + 1 < gps->totpoints) { + ptc = &gps->points[i + 1]; + } + else { + ptc = &gps->points[i]; + } - /* delete the palette now... */ - BKE_gpencil_palette_delete(gpd, palette); + float wa = BKE_gpencil_vgroup_use_index(dverta, def_nr); + float wb = BKE_gpencil_vgroup_use_index(dvertb, def_nr); + CLAMP_MIN(wa, 0.0f); + CLAMP_MIN(wb, 0.0f); + + /* the optimal value is the corresponding to the interpolation of the weight + * at the distance of point b + */ + const float opfac = line_point_factor_v3(&ptb->x, &pta->x, &ptc->x); + const float optimal = interpf(wa, wb, opfac); + /* Based on influence factor, blend between original and optimal */ + wb = interpf(wb, optimal, fac); + BKE_gpencil_vgroup_add_point_weight(dvertb, def_nr, wb); + } + } + } + CTX_DATA_END; /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED | ND_SPACE_PROPERTIES, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_palette_remove(wmOperatorType *ot) +void GPENCIL_OT_vertex_group_smooth(wmOperatorType *ot) { /* identifiers */ - ot->name = "Remove palette"; - ot->idname = "GPENCIL_OT_palette_remove"; - ot->description = "Remove active Grease Pencil palette"; + ot->name = "Smooth Vertex Group"; + ot->idname = "GPENCIL_OT_vertex_group_smooth"; + ot->description = "Smooth weights to the active vertex group"; + + /* api callbacks */ + ot->poll = gpencil_vertex_group_weight_poll; + ot->exec = gpencil_vertex_group_smooth_exec; + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - /* callbacks */ - ot->exec = gp_palette_remove_exec; - ot->poll = gp_active_palette_poll; + RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 1.0, "Factor", "", 0.0f, 1.0f); + RNA_def_int(ot->srna, "repeat", 1, 1, 10000, "Iterations", "", 1, 200); } -/* ********************** Change Palette ***************************** */ +/****************************** Join ***********************************/ -static int gp_palette_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +/* userdata for joined_gpencil_fix_animdata_cb() */ +typedef struct tJoinGPencil_AdtFixData { + bGPdata *src_gpd; + bGPdata *tar_gpd; + + GHash *names_map; +} tJoinGPencil_AdtFixData; + +/* Callback to pass to BKE_fcurves_main_cb() for RNA Paths attached to each F-Curve used in the AnimData */ +static void joined_gpencil_fix_animdata_cb(ID *id, FCurve *fcu, void *user_data) { - uiPopupMenu *pup; - uiLayout *layout; + tJoinGPencil_AdtFixData *afd = (tJoinGPencil_AdtFixData *)user_data; + ID *src_id = &afd->src_gpd->id; + ID *dst_id = &afd->tar_gpd->id; - /* 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); + GHashIterator gh_iter; - return OPERATOR_INTERFACE; + /* Fix paths - If this is the target datablock, it will have some "dirty" paths */ + if ((id == src_id) && fcu->rna_path && strstr(fcu->rna_path, "layers[")) { + GHASH_ITER(gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed; this still means there will be some waste if there aren't many drivers/keys */ + if (!STREQ(old_name, new_name) && strstr(fcu->rna_path, old_name)) { + fcu->rna_path = BKE_animsys_fix_rna_path_rename(id, fcu->rna_path, "layers", + old_name, new_name, 0, 0, false); + + /* we don't want to apply a second remapping on this F-Curve now, + * so stop trying to fix names names + */ + break; + } + } + } + + /* Fix driver targets */ + if (fcu->driver) { + /* Fix driver references to invalid ID's */ + for (DriverVar *dvar = fcu->driver->variables.first; dvar; dvar = dvar->next) { + /* only change the used targets, since the others will need fixing manually anyway */ + DRIVER_TARGETS_USED_LOOPER(dvar) + { + /* change the ID's used... */ + if (dtar->id == src_id) { + dtar->id = dst_id; + + /* also check on the subtarget... + * XXX: We duplicate the logic from drivers_path_rename_fix() here, with our own + * little twists so that we know that it isn't going to clobber the wrong data + */ + if (dtar->rna_path && strstr(dtar->rna_path, "layers[")) { + GHASH_ITER(gh_iter, afd->names_map) { + const char *old_name = BLI_ghashIterator_getKey(&gh_iter); + const char *new_name = BLI_ghashIterator_getValue(&gh_iter); + + /* only remap if changed */ + if (!STREQ(old_name, new_name)) { + if ((dtar->rna_path) && strstr(dtar->rna_path, old_name)) { + /* Fix up path */ + dtar->rna_path = BKE_animsys_fix_rna_path_rename(id, dtar->rna_path, "layers", + old_name, new_name, 0, 0, false); + break; /* no need to try any more names for layer path */ + } + } + } + } + } + } + DRIVER_TARGETS_LOOPER_END + } + } } -static int gp_palette_change_exec(bContext *C, wmOperator *op) +/* join objects called from OBJECT_OT_join */ +int ED_gpencil_join_objects_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDpalette *palette = NULL; - int palette_num = RNA_enum_get(op->ptr, "palette"); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *obact = CTX_data_active_object(C); + bGPdata *gpd_dst = NULL; + bool ok = false; + + /* Ensure we're in right mode and that the active object is correct */ + if (!obact || obact->type != OB_GPENCIL) + return OPERATOR_CANCELLED; - /* Get palette or create new one */ - if (palette_num == -1) { - /* Create palette */ - palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); + bGPdata *gpd = (bGPdata *)obact->data; + if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { + return OPERATOR_CANCELLED; } - 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; + /* Ensure all rotations are applied before */ + // XXX: Why don't we apply them here instead of warning? + CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) + { + if (base->object->type == OB_GPENCIL) { + if ((base->object->rot[0] != 0) || + (base->object->rot[1] != 0) || + (base->object->rot[2] != 0)) + { + BKE_report(op->reports, RPT_ERROR, "Apply all rotations before join objects"); + return OPERATOR_CANCELLED; + } } } + CTX_DATA_END; - /* Set active palette */ - BKE_gpencil_palette_setactive(gpd, palette); + CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) + { + if (base->object == obact) { + ok = true; + break; + } + } + CTX_DATA_END; - /* updates */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + /* that way the active object is always selected */ + if (ok == false) { + BKE_report(op->reports, RPT_WARNING, "Active object is not a selected grease pencil"); + return OPERATOR_CANCELLED; + } - return OPERATOR_FINISHED; -} + gpd_dst = obact->data; + Object *ob_dst = obact; + + /* loop and join all data */ + CTX_DATA_BEGIN(C, Base *, base, selected_editable_bases) + { + if ((base->object->type == OB_GPENCIL) && (base->object != obact)) { + /* we assume that each datablock is not already used in active object */ + if (obact->data != base->object->data) { + Object *ob_src = base->object; + bGPdata *gpd_src = base->object->data; + + /* Apply all GP modifiers before */ + for (GpencilModifierData *md = base->object->greasepencil_modifiers.first; md; md = md->next) { + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + if (mti->bakeModifier) { + mti->bakeModifier(bmain, depsgraph, md, base->object); + } + } -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"; + /* copy vertex groups to the base one's */ + int old_idx = 0; + for (bDeformGroup *dg = base->object->defbase.first; dg; dg = dg->next) { + bDeformGroup *vgroup = MEM_dupallocN(dg); + int idx = BLI_listbase_count(&obact->defbase); + defgroup_unique_name(vgroup, obact); + BLI_addtail(&obact->defbase, vgroup); + /* update vertex groups in strokes in original data */ + for (bGPDlayer *gpl_src = gpd->layers.first; gpl_src; gpl_src = gpl_src->next) { + for (bGPDframe *gpf = gpl_src->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + MDeformVert *dvert; + int i; + for (i = 0, dvert = gps->dvert; i < gps->totpoints; i++, dvert++) { + if ((dvert->dw) && (dvert->dw->def_nr == old_idx)) { + dvert->dw->def_nr = idx; + } + } + } + } + } + old_idx++; + } + if (obact->defbase.first && obact->actdef == 0) + obact->actdef = 1; + + /* add missing materials reading source materials and checking in destination object */ + Material ***matar = give_matarar(ob_src); + short *totcol = give_totcolp(ob_src); + + for (short i = 0; i < *totcol; i++) { + Material *tmp_ma = (*matar)[i]; + if (BKE_object_material_slot_find_index(ob_dst, tmp_ma) == 0) { + BKE_object_material_slot_add(bmain, ob_dst); + assign_material(bmain, ob_dst, tmp_ma, ob_dst->totcol, BKE_MAT_ASSIGN_EXISTING); + } + } - /* callbacks */ - ot->invoke = gp_palette_change_invoke; - ot->exec = gp_palette_change_exec; - ot->poll = gp_active_palette_poll; + /* duplicate bGPDlayers */ + tJoinGPencil_AdtFixData afd = {0}; + afd.src_gpd = gpd_src; + afd.tar_gpd = gpd_dst; + afd.names_map = BLI_ghash_str_new("joined_gp_layers_map"); + + float imat[3][3], bmat[3][3]; + float offset_global[3]; + float offset_local[3]; + + sub_v3_v3v3(offset_global, obact->loc, base->object->obmat[3]); + copy_m3_m4(bmat, obact->obmat); + invert_m3_m3(imat, bmat); + mul_m3_v3(imat, offset_global); + mul_v3_m3v3(offset_local, imat, offset_global); + + + for (bGPDlayer *gpl_src = gpd_src->layers.first; gpl_src; gpl_src = gpl_src->next) { + bGPDlayer *gpl_new = BKE_gpencil_layer_duplicate(gpl_src); + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* recalculate all stroke points */ + ED_gpencil_parent_location(depsgraph, base->object, gpd_src, gpl_src, diff_mat); + invert_m4_m4(inverse_diff_mat, diff_mat); + + Material *ma_src = NULL; + int idx; + for (bGPDframe *gpf = gpl_new->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + + /* reasign material. Look old material and try to find in dst */ + ma_src = give_current_material(ob_src, gps->mat_nr + 1); + if (ma_src != NULL) { + idx = BKE_object_material_slot_find_index(ob_dst, ma_src); + if (idx > 0) { + gps->mat_nr = idx - 1; + } + else { + gps->mat_nr = 0; + } + } + else { + gps->mat_nr = 0; + } + + bGPDspoint *pt; + int i; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float mpt[3]; + mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); + sub_v3_v3(mpt, offset_local); + mul_v3_m4v3(&pt->x, diff_mat, mpt); + } + } + } - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + /* be sure name is unique in new object */ + BLI_uniquename(&gpd_dst->layers, gpl_new, DATA_("GP_Layer"), '.', offsetof(bGPDlayer, info), sizeof(gpl_new->info)); + BLI_ghash_insert(afd.names_map, BLI_strdup(gpl_src->info), gpl_new->info); + + /* add to destination datablock */ + BLI_addtail(&gpd_dst->layers, gpl_new); + } + + /* Fix all the animation data */ + BKE_fcurves_main_cb(bmain, joined_gpencil_fix_animdata_cb, &afd); + BLI_ghash_free(afd.names_map, MEM_freeN, NULL); + + /* Only copy over animdata now, after all the remapping has been done, + * so that we don't have to worry about ambiguities re which datablock + * a layer came from! + */ + if (base->object->adt) { + if (obact->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + obact->adt = BKE_animdata_copy(bmain, base->object->adt, false, true); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(bmain, &obact->id, &base->object->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + + if (gpd_src->adt) { + if (gpd_dst->adt == NULL) { + /* no animdata, so just use a copy of the whole thing */ + gpd_dst->adt = BKE_animdata_copy(bmain, gpd_src->adt, false, true); + } + else { + /* merge in data - we'll fix the drivers manually */ + BKE_animdata_merge_copy(bmain, &gpd_dst->id, &gpd_src->id, ADT_MERGECOPY_KEEP_DST, false); + } + } + } - /* 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); + /* Free the old object */ + ED_object_base_free_and_unlink(bmain, scene, base->object); + } + } + CTX_DATA_END; + + DEG_relations_tag_update(bmain); /* because we removed object(s) */ + + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + return OPERATOR_FINISHED; } -/* ******************* Lock and hide any color non used in current layer ************************** */ +/* Color Handle operator */ +static bool gpencil_active_color_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if (ob && ob->data && (ob->type == OB_GPENCIL)) { + short *totcolp = give_totcolp(ob); + return *totcolp > 0; + } + return false; +} -static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) + +/* ******************* Lock and hide any color non used in current layer ************************** */ +static int gpencil_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette; + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *gp_style = NULL; /* sanity checks */ if (ELEM(NULL, gpd)) return OPERATOR_CANCELLED; - palette = BKE_gpencil_palette_getactive(gpd); - if (ELEM(NULL, palette)) + /* first lock and hide all colors */ + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + if ((totcol == 0) || (matar == NULL)) 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; + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag |= GP_STYLE_COLOR_LOCKED; + gp_style->flag |= GP_STYLE_COLOR_HIDE; } /* loop all selected strokes and unlock any color used in active layer */ @@ -1558,140 +2042,49 @@ static int gp_palette_lock_layer_exec(bContext *C, wmOperator *UNUSED(op)) if (ED_gpencil_stroke_can_use(C, gps) == false) continue; + gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); /* unlock/unhide color if not unlocked before */ - if (gps->palcolor != NULL) { - gps->palcolor->flag &= ~PC_COLOR_LOCKED; - gps->palcolor->flag &= ~PC_COLOR_HIDE; + if (gp_style != NULL) { + gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; + gp_style->flag &= ~GP_STYLE_COLOR_HIDE; } } } } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_palette_lock_layer(wmOperatorType *ot) +void GPENCIL_OT_lock_layer(wmOperatorType *ot) { /* identifiers */ ot->name = "Disable Unused Layer Colors"; - ot->idname = "GPENCIL_OT_palette_lock_layer"; + ot->idname = "GPENCIL_OT_lock_layer"; ot->description = "Lock and hide any color not used in any layer"; /* api callbacks */ - ot->exec = gp_palette_lock_layer_exec; + ot->exec = gpencil_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) -{ - Main *bmain = CTX_data_main(C); - 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 = BKE_gpencil_data_addnew(bmain, DATA_("GPencil")); - - /* verify palette */ - bGPDpalette *palette = BKE_gpencil_palette_getactive(*gpd_ptr); - if (palette == NULL) - palette = BKE_gpencil_palette_addnew(*gpd_ptr, DATA_("GP_Palette"), true); - - /* add new palette color now */ - BKE_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 data-block"; - - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* callbacks */ - ot->exec = gp_palettecolor_add_exec; - ot->poll = gp_add_poll; -} - -/* ******************* Remove Active Palette color ************************* */ +/* ********************** Isolate gpencil_ color **************************** */ -static int gp_palettecolor_remove_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_color_isolate_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *color = BKE_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) - BKE_gpencil_palettecolor_setactive(palette, color->prev); - else - BKE_gpencil_palettecolor_setactive(palette, color->next); - - /* delete the strokes */ - BKE_gpencil_palettecolor_delete_strokes(gpd, color->info); - - /* delete the palette color now... */ - BKE_gpencil_palettecolor_delete(palette, color); - - /* notifiers */ - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + Object *ob = CTX_data_active_object(C); + Material *active_ma = give_current_material(ob, ob->actcol); + MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); + MaterialGPencilStyle *gp_style; - 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 = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *active_color = BKE_gpencil_palettecolor_getactive(palette); - bGPDpalettecolor *palcolor; - - int flags = PC_COLOR_LOCKED; + int flags = GP_STYLE_COLOR_LOCKED; bool isolate = false; if (RNA_boolean_get(op->ptr, "affect_visibility")) - flags |= PC_COLOR_HIDE; + flags |= GP_STYLE_COLOR_HIDE; if (ELEM(NULL, gpd, active_color)) { BKE_report(op->reports, RPT_ERROR, "No active color to isolate"); @@ -1699,15 +2092,20 @@ static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) } /* Test whether to isolate or clear all flags */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; /* Skip if this is the active one */ - if (palcolor == active_color) + if (ma == active_ma) 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) { + * not alone, so we have some colors to isolate still + */ + gp_style = ma->gp_style; + if ((gp_style->flag & flags) == 0) { isolate = true; break; } @@ -1716,72 +2114,79 @@ static int gp_isolate_palettecolor_exec(bContext *C, wmOperator *op) /* 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) + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + if (gp_style == active_color) continue; else - palcolor->flag |= flags; + gp_style->flag |= flags; } } else { /* Clear flags - Restore everything else */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~flags; + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag &= ~flags; } } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_isolate(wmOperatorType *ot) +void GPENCIL_OT_color_isolate(wmOperatorType *ot) { /* identifiers */ - ot->name = "Isolate Palette Color"; - ot->idname = "GPENCIL_OT_palettecolor_isolate"; + ot->name = "Isolate Color"; + ot->idname = "GPENCIL_OT_color_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; + ot->exec = gpencil_color_isolate_exec; + ot->poll = gpencil_active_color_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"); + "the editability, also affect the visibility"); } -/* *********************** Hide Palette colors ******************************** */ +/* *********************** Hide colors ******************************** */ -static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) +static int gpencil_color_hide_exec(bContext *C, wmOperator *op) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *active_color = BKE_material_gpencil_settings_get(ob, ob->actcol); bool unselected = RNA_boolean_get(op->ptr, "unselected"); - /* sanity checks */ - if (ELEM(NULL, gpd, palette, palcolor)) + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + if ((totcol == 0) || (matar == NULL)) 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; + MaterialGPencilStyle *color = NULL; + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + color = ma->gp_style; + if (active_color != color) { + color->flag |= GP_STYLE_COLOR_HIDE; } } } else { /* hide selected/active */ - palcolor->flag |= PC_COLOR_HIDE; + active_color->flag |= GP_STYLE_COLOR_HIDE; } /* notifiers */ @@ -1790,18 +2195,18 @@ static int gp_palettecolor_hide_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) +void GPENCIL_OT_color_hide(wmOperatorType *ot) { /* identifiers */ ot->name = "Hide Color(s)"; - ot->idname = "GPENCIL_OT_palettecolor_hide"; + ot->idname = "GPENCIL_OT_color_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 */ + ot->exec = gpencil_color_hide_exec; + ot->poll = gpencil_active_color_poll; /* NOTE: we need an active color to play with */ - /* flags */ + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* props */ @@ -1810,25 +2215,23 @@ void GPENCIL_OT_palettecolor_hide(wmOperatorType *ot) /* ********************** Show All Colors ***************************** */ -/* poll callback for showing colors */ -static bool 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)) +static int gpencil_color_reveal_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor; + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); - /* sanity checks */ - if (ELEM(NULL, gpd, palette)) + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; /* make all colors visible */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~PC_COLOR_HIDE; + MaterialGPencilStyle *gp_style = NULL; + + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag &= ~GP_STYLE_COLOR_HIDE; } /* notifiers */ @@ -1837,36 +2240,41 @@ static int gp_palettecolor_reveal_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_reveal(wmOperatorType *ot) +void GPENCIL_OT_color_reveal(wmOperatorType *ot) { /* identifiers */ ot->name = "Show All Colors"; - ot->idname = "GPENCIL_OT_palettecolor_reveal"; - ot->description = "Unhide all hidden Grease Pencil palette colors"; + ot->idname = "GPENCIL_OT_color_reveal"; + ot->description = "Unhide all hidden Grease Pencil gpencil_ colors"; /* callbacks */ - ot->exec = gp_palettecolor_reveal_exec; - ot->poll = gp_palettecolor_reveal_poll; + ot->exec = gpencil_color_reveal_exec; + ot->poll = gpencil_active_color_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ***************** Lock/Unlock All Palette colors ************************ */ +/* ***************** Lock/Unlock All colors ************************ */ -static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_color_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor; - /* sanity checks */ - if (ELEM(NULL, gpd, palette)) + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); + + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; /* make all layers non-editable */ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag |= PC_COLOR_LOCKED; + MaterialGPencilStyle *gp_style = NULL; + + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag |= GP_STYLE_COLOR_LOCKED; } /* notifiers */ @@ -1875,16 +2283,16 @@ static int gp_palettecolor_lock_all_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) +void GPENCIL_OT_color_lock_all(wmOperatorType *ot) { /* identifiers */ ot->name = "Lock All Colors"; - ot->idname = "GPENCIL_OT_palettecolor_lock_all"; + ot->idname = "GPENCIL_OT_color_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 */ + ot->exec = gpencil_color_lock_all_exec; + ot->poll = gpencil_active_color_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1892,19 +2300,23 @@ void GPENCIL_OT_palettecolor_lock_all(wmOperatorType *ot) /* -------------------------- */ -static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_color_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) { - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor; + Object *ob = CTX_data_active_object(C); + Material *ma = NULL; + Material ***matar = give_matarar(ob); + short *totcol = give_totcolp(ob); - /* sanity checks */ - if (ELEM(NULL, gpd, palette)) + if ((totcol == 0) || (matar == NULL)) return OPERATOR_CANCELLED; /* make all layers editable again*/ - for (palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { - palcolor->flag &= ~PC_COLOR_LOCKED; + MaterialGPencilStyle *gp_style = NULL; + + for (short i = 0; i < *totcol; i++) { + ma = (*matar)[i]; + gp_style = ma->gp_style; + gp_style->flag &= ~GP_STYLE_COLOR_LOCKED; } /* notifiers */ @@ -1913,94 +2325,32 @@ static int gp_palettecolor_unlock_all_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_unlock_all(wmOperatorType *ot) +void GPENCIL_OT_color_unlock_all(wmOperatorType *ot) { /* identifiers */ ot->name = "Unlock All Colors"; - ot->idname = "GPENCIL_OT_palettecolor_unlock_all"; + ot->idname = "GPENCIL_OT_color_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 */ + ot->exec = gpencil_color_unlock_all_exec; + ot->poll = gpencil_active_color_poll; /* 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 = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_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 const 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 */ +/* ***************** Select all strokes using color ************************ */ - /* 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)) +static int gpencil_color_select_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + Object *ob = CTX_data_active_object(C); + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, ob->actcol); /* sanity checks */ - if (ELEM(NULL, gpd, palette, palcolor)) + if (ELEM(NULL, gpd, gp_style)) return OPERATOR_CANCELLED; /* read all strokes and select*/ @@ -2013,11 +2363,11 @@ static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) 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) + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) continue; /* select */ - if (strcmp(palcolor->info, gps->colorname) == 0) { + if (ob->actcol == gps->mat_nr) { bGPDspoint *pt; int i; @@ -2035,56 +2385,16 @@ static int gp_palettecolor_select_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -void GPENCIL_OT_palettecolor_select(wmOperatorType *ot) +void GPENCIL_OT_color_select(wmOperatorType *ot) { /* identifiers */ ot->name = "Select Color"; - ot->idname = "GPENCIL_OT_palettecolor_select"; + ot->idname = "GPENCIL_OT_color_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 = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_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 = BKE_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; + ot->exec = gpencil_color_select_exec; + ot->poll = gpencil_active_color_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 ec67b2da161..56d1d53ea40 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -47,6 +47,7 @@ #include "BLT_translation.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -54,12 +55,18 @@ #include "DNA_view3d_types.h" #include "DNA_gpencil_types.h" +#include "BKE_main.h" #include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_brush.h" #include "BKE_gpencil.h" +#include "BKE_paint.h" #include "BKE_library.h" -#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_workspace.h" #include "UI_interface.h" #include "UI_resources.h" @@ -80,31 +87,70 @@ #include "ED_space_api.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" #include "gpencil_intern.h" /* ************************************************ */ /* Stroke Edit Mode Management */ - static bool gpencil_editmode_toggle_poll(bContext *C) { + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } return ED_gpencil_data_get_active(C) != NULL; } -static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op) { + const int back = RNA_boolean_get(op->ptr, "back"); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } - if (gpd == NULL) + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No active GP data"); return OPERATOR_CANCELLED; + } /* Just toggle editmode flag... */ gpd->flag ^= GP_DATA_STROKE_EDITMODE; /* recalculate parent matrix */ if (gpd->flag & GP_DATA_STROKE_EDITMODE) { - ED_gpencil_reset_layers_parent(gpd); + ED_gpencil_reset_layers_parent(depsgraph, ob, gpd); + } + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_EDITMODE) { + mode = OB_MODE_GPENCIL_EDIT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; } + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL); WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); @@ -114,6 +160,8 @@ static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *UNUSED(op)) void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ ot->name = "Strokes Edit Mode Toggle"; ot->idname = "GPENCIL_OT_editmode_toggle"; @@ -125,6 +173,259 @@ void GPENCIL_OT_editmode_toggle(wmOperatorType *ot) /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +/* Stroke Paint Mode Management */ + +static bool gpencil_paintmode_toggle_poll(bContext *C) +{ + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op) +{ + const bool back = RNA_boolean_get(op->ptr, "back"); + + bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle paintmode flag... */ + gpd->flag ^= GP_DATA_STROKE_PAINTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + mode = OB_MODE_GPENCIL_PAINT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* be sure we have brushes */ + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + BKE_brush_gpencil_presets(C); + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + 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_paintmode_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Strokes Paint Mode Toggle"; + ot->idname = "GPENCIL_OT_paintmode_toggle"; + ot->description = "Enter/Exit paint mode for Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_paintmode_toggle_exec; + ot->poll = gpencil_paintmode_toggle_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +/* Stroke Sculpt Mode Management */ + +static bool gpencil_sculptmode_toggle_poll(bContext *C) +{ + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op) +{ + const bool back = RNA_boolean_get(op->ptr, "back"); + + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle sculptmode flag... */ + gpd->flag ^= GP_DATA_STROKE_SCULPTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) { + mode = OB_MODE_GPENCIL_SCULPT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + 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_sculptmode_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Strokes Sculpt Mode Toggle"; + ot->idname = "GPENCIL_OT_sculptmode_toggle"; + ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_sculptmode_toggle_exec; + ot->poll = gpencil_sculptmode_toggle_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); +} + +/* Stroke Weight Paint Mode Management */ + +static bool gpencil_weightmode_toggle_poll(bContext *C) +{ + /* if using gpencil object, use this gpd */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + return ob->data != NULL; + } + return ED_gpencil_data_get_active(C) != NULL; +} + +static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op) +{ + const bool back = RNA_boolean_get(op->ptr, "back"); + + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_object = false; + short mode; + /* if using a gpencil object, use this datablock */ + Object *ob = CTX_data_active_object(C); + if ((ob) && (ob->type == OB_GPENCIL)) { + gpd = ob->data; + is_object = true; + } + + if (gpd == NULL) + return OPERATOR_CANCELLED; + + /* Just toggle weightmode flag... */ + gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE; + /* set mode */ + if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) { + mode = OB_MODE_GPENCIL_WEIGHT; + } + else { + mode = OB_MODE_OBJECT; + } + + if (is_object) { + /* try to back previous mode */ + if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) { + mode = ob->restore_mode; + } + ob->restore_mode = ob->mode; + ob->mode = mode; + } + + /* setup other modes */ + ED_gpencil_setup_modes(C, gpd, mode); + /* set cache as dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + 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_weightmode_toggle(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Strokes Weight Mode Toggle"; + ot->idname = "GPENCIL_OT_weightmode_toggle"; + ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes"; + + /* callbacks */ + ot->exec = gpencil_weightmode_toggle_exec; + ot->poll = gpencil_weightmode_toggle_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* properties */ + prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } /* ************************************************ */ @@ -137,21 +438,30 @@ static bool gp_stroke_edit_poll(bContext *C) return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0; } +/* poll callback to verify edit mode in 3D view only */ +static bool gp_strokes_edit3d_poll(bContext *C) +{ + /* 2 Requirements: + * - 1) Editable GP data + * - 2) 3D View only + */ + return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); +} + /* ************ 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) + View3D *v3d = CTX_wm_view3d(C); + if (v3d == NULL) return OPERATOR_CANCELLED; /* Just toggle alpha... */ - if (ts->gp_sculpt.alpha > 0.0f) { - ts->gp_sculpt.alpha = 0.0f; + if (v3d->vertex_opacity > 0.0f) { + v3d->vertex_opacity = 0.0f; } else { - ts->gp_sculpt.alpha = 1.0f; + v3d->vertex_opacity = 1.0f; } WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); @@ -220,7 +530,7 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, co /* make a stupid copy first of the entire stroke (to get the flags too) */ gpsd = MEM_dupallocN(gps); - BLI_strncpy(gpsd->tmp_layerinfo, layername, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ + BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo)); /* saves original layer name */ /* initialize triangle memory - will be calculated on next redraw */ gpsd->triangles = NULL; @@ -232,6 +542,20 @@ static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, co memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len); gpsd->totpoints = len; + if (gps->dvert != NULL) { + gpsd->dvert = MEM_callocN(sizeof(MDeformVert) * len, "gps stroke weights copy"); + memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len); + + /* Copy weights */ + int e = start_idx; + for (int j = 0; j < gpsd->totpoints; j++) { + MDeformVert *dvert_dst = &gps->dvert[e]; + MDeformVert *dvert_src = &gps->dvert[j]; + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + e++; + } + } + /* add to temp buffer */ gpsd->next = gpsd->prev = NULL; BLI_addtail(new_strokes, gpsd); @@ -252,6 +576,11 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + /* for each visible (and editable) layer's selected strokes, * copy the strokes into a temporary buffer, then append * once all done @@ -279,8 +608,12 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); - BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); + BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); gpsd->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + gpsd->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsd); + } /* triangle information - will be calculated on next redraw */ gpsd->flag |= GP_STROKE_RECALC_CACHES; @@ -309,6 +642,7 @@ static int gp_duplicate_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -342,7 +676,7 @@ void GPENCIL_OT_duplicate(wmOperatorType *ot) /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */ ListBase gp_strokes_copypastebuf = {NULL, NULL}; -/* Hash for hanging on to all the palette colors used by strokes in the buffer +/* Hash for hanging on to all the colors used by strokes in the buffer * * This is needed to prevent dangling and unsafe pointers when pasting across datablocks, * or after a color used by a stroke in the buffer gets deleted (via user action or undo). @@ -354,11 +688,11 @@ void ED_gpencil_strokes_copybuf_free(void) { bGPDstroke *gps, *gpsn; - /* Free the palettes buffer - * NOTE: This is done before the strokes so that the name ptrs (keys) are still safe + /* Free the colors buffer + * NOTE: This is done before the strokes so that the ptrs are still safe */ if (gp_strokes_copypastebuf_colors) { - BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN); + BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, NULL); gp_strokes_copypastebuf_colors = NULL; } @@ -366,8 +700,15 @@ void ED_gpencil_strokes_copybuf_free(void) for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) { gpsn = gps->next; - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + MEM_SAFE_FREE(gps->triangles); BLI_freelinkN(&gp_strokes_copypastebuf, gps); } @@ -378,38 +719,26 @@ void ED_gpencil_strokes_copybuf_free(void) /* Ensure that destination datablock has all the colours the pasted strokes need * Helper function for copy-pasting strokes */ -GHash *gp_copybuf_validate_colormap(bGPdata *gpd) +GHash *gp_copybuf_validate_colormap(bContext *C) { + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); GHash *new_colors = BLI_ghash_str_new("GPencil Paste Dst Colors"); GHashIterator gh_iter; - /* If there's no active palette yet (i.e. new datablock), add one */ - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(gpd, "Pasted Palette", true); - } - - /* For each color, figure out what to map to... */ + /* For each color, check if exist and add if not */ GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) { - bGPDpalettecolor *palcolor; - char *name = BLI_ghashIterator_getKey(&gh_iter); - - /* Look for existing color to map to */ - /* XXX: What to do if same name but different color? Behaviour here should depend on a property? */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, name); - if (palcolor == NULL) { - /* Doesn't Exist - Create new matching color for this palette */ - /* XXX: This still doesn't fix the pasting across file boundaries problem... */ - bGPDpalettecolor *src_color = BLI_ghashIterator_getValue(&gh_iter); - palcolor = MEM_dupallocN(src_color); - BLI_addtail(&palette->colors, palcolor); + int *key = BLI_ghashIterator_getKey(&gh_iter); + Material *ma = BLI_ghashIterator_getValue(&gh_iter); - BLI_uniquename(&palette->colors, palcolor, DATA_("GP Color"), '.', offsetof(bGPDpalettecolor, info), sizeof(palcolor->info)); + if (BKE_object_material_slot_find_index(ob, ma) == 0) { + BKE_object_material_slot_add(bmain, ob); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); } /* Store this mapping (for use later when pasting) */ - BLI_ghash_insert(new_colors, name, palcolor); + BLI_ghash_insert(new_colors, key, ma); } return new_colors; @@ -420,6 +749,7 @@ GHash *gp_copybuf_validate_colormap(bGPdata *gpd) static int gp_strokes_copy_exec(bContext *C, wmOperator *op) { + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); if (gpd == NULL) { @@ -427,6 +757,11 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + /* clear the buffer first */ ED_gpencil_strokes_copybuf_free(); @@ -455,8 +790,12 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) /* make direct copies of the stroke and its points */ gpsd = MEM_dupallocN(gps); - BLI_strncpy(gpsd->tmp_layerinfo, gpl->info, sizeof(gpsd->tmp_layerinfo)); /* saves original layer name */ + BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo)); /* saves original layer name */ gpsd->points = MEM_dupallocN(gps->points); + if (gps->dvert != NULL) { + gpsd->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, gpsd); + } /* triangles cache - will be recalculated on next redraw */ gpsd->flag |= GP_STROKE_RECALC_CACHES; @@ -476,17 +815,15 @@ static int gp_strokes_copy_exec(bContext *C, wmOperator *op) } CTX_DATA_END; - /* Build up hash of colors used in these strokes, making copies of these to protect against dangling pointers */ + /* Build up hash of material colors used in these strokes */ if (gp_strokes_copypastebuf.first) { gp_strokes_copypastebuf_colors = BLI_ghash_str_new("GPencil CopyBuf Colors"); - + Material *ma = NULL; for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { - if (BLI_ghash_haskey(gp_strokes_copypastebuf_colors, gps->colorname) == false) { - bGPDpalettecolor *color = MEM_dupallocN(gps->palcolor); - - BLI_ghash_insert(gp_strokes_copypastebuf_colors, gps->colorname, color); - gps->palcolor = color; + ma = give_current_material(ob, gps->mat_nr + 1); + if (BLI_ghash_haskey(gp_strokes_copypastebuf_colors, &gps->mat_nr) == false) { + BLI_ghash_insert(gp_strokes_copypastebuf_colors, &gps->mat_nr, ma); } } } @@ -534,9 +871,11 @@ typedef enum eGP_PasteMode { static int gp_strokes_paste_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */ + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); bGPDframe *gpf; eGP_PasteMode type = RNA_enum_get(op->ptr, "type"); @@ -547,6 +886,10 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); return OPERATOR_CANCELLED; } + else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) { BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again"); return OPERATOR_CANCELLED; @@ -599,14 +942,14 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* Ensure that all the necessary colors exist */ - new_colors = gp_copybuf_validate_colormap(gpd); + new_colors = gp_copybuf_validate_colormap(C); /* Copy over the strokes from the buffer (and adjust the colors) */ for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) { if (ED_gpencil_stroke_can_use(C, gps)) { /* Need to verify if layer exists */ if (type != GP_COPY_MERGE) { - gpl = BLI_findstring(&gpd->layers, gps->tmp_layerinfo, offsetof(bGPDlayer, info)); + gpl = BLI_findstring(&gpd->layers, gps->runtime.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); @@ -618,28 +961,33 @@ 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 = BKE_gpencil_layer_getframe(gpl, CFRA, true); + gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, true); if (gpf) { /* Create new stroke */ bGPDstroke *new_stroke = MEM_dupallocN(gps); - new_stroke->tmp_layerinfo[0] = '\0'; + new_stroke->runtime.tmp_layerinfo[0] = '\0'; new_stroke->points = MEM_dupallocN(gps->points); - + if (gps->dvert != NULL) { + new_stroke->dvert = MEM_dupallocN(gps->dvert); + BKE_gpencil_stroke_weights_duplicate(gps, new_stroke); + } new_stroke->flag |= GP_STROKE_RECALC_CACHES; new_stroke->triangles = NULL; new_stroke->next = new_stroke->prev = NULL; BLI_addtail(&gpf->strokes, new_stroke); - /* Fix color references */ - BLI_assert(new_stroke->colorname[0] != '\0'); - new_stroke->palcolor = BLI_ghash_lookup(new_colors, new_stroke->colorname); - - BLI_assert(new_stroke->palcolor != NULL); - BLI_strncpy(new_stroke->colorname, new_stroke->palcolor->info, sizeof(new_stroke->colorname)); + /* Remap material */ + Material *ma = BLI_ghash_lookup(new_colors, &new_stroke->mat_nr); + if ((ma) && (BKE_object_material_slot_find_index(ob, ma) > 0)) { + gps->mat_nr = BKE_object_material_slot_find_index(ob, ma) - 1; + CLAMP_MIN(gps->mat_nr, 0); + } + else { + gps->mat_nr = 0; /* only if the color is not found */ + } - /*new_stroke->flag |= GP_STROKE_RECALC_COLOR; */ } } } @@ -648,6 +996,7 @@ static int gp_strokes_paste_exec(bContext *C, wmOperator *op) BLI_ghash_free(new_colors, NULL, NULL); /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -697,10 +1046,17 @@ static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *U static int gp_move_to_layer_exec(bContext *C, wmOperator *op) { bGPdata *gpd = CTX_data_gpencil_data(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; int layer_num = RNA_enum_get(op->ptr, "layer"); + if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { + BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); + return OPERATOR_CANCELLED; + } + /* Get layer or create new one */ if (layer_num == -1) { /* Create layer */ @@ -748,14 +1104,14 @@ static int gp_move_to_layer_exec(bContext *C, wmOperator *op) /* Paste them all in one go */ if (strokes.first) { - Scene *scene = CTX_data_scene(C); - bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, CFRA, true); + bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, true); BLI_movelisttolist(&gpf->strokes, &strokes); BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); } /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -804,8 +1160,10 @@ static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C) static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd); const bool all_layers = RNA_boolean_get(op->ptr, "all_layers"); @@ -826,7 +1184,7 @@ static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) } /* 1) Check for an existing frame on the current frame */ - bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, CFRA); + bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra_eval); if (gpf) { /* Shunt all frames after (and including) the existing one later by 1-frame */ for (; gpf; gpf = gpf->next) { @@ -835,11 +1193,12 @@ static int gp_blank_frame_add_exec(bContext *C, wmOperator *op) } /* 2) Now add a new frame, with nothing in it */ - gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_NEW); + gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW); } CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -876,10 +1235,13 @@ static bool gp_actframe_delete_poll(bContext *C) /* delete active frame - wrapper around API calls */ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); + + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); /* if there's no existing Grease-Pencil data there, add some */ if (gpd == NULL) { @@ -895,6 +1257,7 @@ static int gp_actframe_delete_exec(bContext *C, wmOperator *op) BKE_gpencil_layer_delframe(gpl, gpf); /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -928,13 +1291,16 @@ static bool gp_actframe_delete_all_poll(bContext *C) static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + bool success = false; CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { /* try to get the "active" frame - but only if it actually occurs on this frame */ - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); if (gpf == NULL) continue; @@ -949,6 +1315,7 @@ static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op) /* updates */ if (success) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -983,43 +1350,69 @@ typedef enum eGP_DeleteMode { GP_DELETEOP_FRAME = 2, } eGP_DeleteMode; +typedef enum eGP_DissolveMode { + /* dissolve all selected points */ + GP_DISSOLVE_POINTS = 0, + /* dissolve between selected points */ + GP_DISSOLVE_BETWEEN = 1, + /* dissolve unselected points */ + GP_DISSOLVE_UNSELECT = 2, +} eGP_DissolveMode; + /* ----------------------------------- */ /* Delete selected strokes */ static int gp_delete_selected_strokes(bContext *C) { bool changed = false; + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - if (gpf == NULL) - continue; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; - /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + if (gpf == NULL) + continue; - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) - continue; + /* simply delete strokes which are selected */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; - /* free stroke if selected */ - if (gps->flag & GP_STROKE_SELECT) { - /* free stroke memory arrays, then stroke itself */ - if (gps->points) MEM_freeN(gps->points); - if (gps->triangles) MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + /* skip strokes that are invalid for current view */ + if (ED_gpencil_stroke_can_use(C, gps) == false) + continue; - changed = true; + /* free stroke if selected */ + if (gps->flag & GP_STROKE_SELECT) { + /* free stroke memory arrays, then stroke itself */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + MEM_SAFE_FREE(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + + changed = true; + } + } } } } CTX_DATA_END; if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1031,88 +1424,238 @@ static int gp_delete_selected_strokes(bContext *C) /* ----------------------------------- */ /* Delete selected points but keep the stroke */ -static int gp_dissolve_selected_points(bContext *C) +static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode) { + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); bool changed = false; + int first = 0; + int last = 0; CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - if (gpf == NULL) - continue; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { - /* simply delete points from selected strokes - * NOTE: we may still have to remove the stroke if it ends up having no points! - */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + bGPDstroke *gps, *gpsn; - /* 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; + if (gpf == NULL) + continue; - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; + /* simply delete points from selected strokes + * NOTE: we may still have to remove the stroke if it ends up having no points! + */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = 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(ob, gpl, gps) == false) + continue; + + /* the stroke must have at least one point selected for any operator */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + MDeformVert *dvert = NULL; + int i; + + int tot = gps->totpoints; /* number of points in new buffer */ + + /* first pass: count points to remove */ + switch (mode) { + case GP_DISSOLVE_POINTS: + /* Count how many points are selected (i.e. how many to remove) */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + /* selected point - one of the points to remove */ + tot--; + } + } + break; + case GP_DISSOLVE_BETWEEN: + /* need to find first and last point selected */ + first = -1; + last = 0; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + if (first < 0) { + first = i; + } + last = i; + } + } + /* count unselected points in the range */ + for (i = first, pt = gps->points + first; i < last; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + tot--; + } + } + break; + case GP_DISSOLVE_UNSELECT: + /* count number of unselected points */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + tot--; + } + } + break; + default: + return false; + break; + } - int tot = gps->totpoints; /* number of points in new buffer */ + /* if no points are left, we simply delete the entire stroke */ + if (tot <= 0) { + /* remove the entire stroke */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + if (gps->triangles) { + MEM_freeN(gps->triangles); + } + BLI_freelinkN(&gpf->strokes, gps); + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + else { + /* just copy all points to keep into a smaller buffer */ + bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); + bGPDspoint *npt = new_points; - /* First Pass: Count how many points are selected (i.e. how many to remove) */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) { - /* selected point - one of the points to remove */ - tot--; - } - } + MDeformVert *new_dvert = NULL; + MDeformVert *ndvert = NULL; - /* if no points are left, we simply delete the entire stroke */ - if (tot <= 0) { - /* remove the entire stroke */ - MEM_freeN(gps->points); - if (gps->triangles) { - MEM_freeN(gps->triangles); - } - BLI_freelinkN(&gpf->strokes, gps); - } - else { - /* just copy all unselected into a smaller buffer */ - bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy"); - bGPDspoint *npt = new_points; + if (gps->dvert != NULL) { + new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy"); + ndvert = new_dvert; + } - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((pt->flag & GP_SPOINT_SELECT) == 0) { - *npt = *pt; - npt++; - } - } + switch (mode) { + case GP_DISSOLVE_POINTS: + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if ((pt->flag & GP_SPOINT_SELECT) == 0) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + break; + case GP_DISSOLVE_BETWEEN: + /* copy first segment */ + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < first; i++, pt++) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + /* copy segment (selected points) */ + (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL; + for (i = first, pt = gps->points + first; i < last; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + /* copy last segment */ + (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL; + for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + + break; + case GP_DISSOLVE_UNSELECT: + /* copy any selected point */ + (gps->dvert != NULL) ? dvert = gps->dvert : NULL; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) { + *npt = *pt; + npt++; + + if (gps->dvert != NULL) { + *ndvert = *dvert; + ndvert->dw = MEM_dupallocN(dvert->dw); + ndvert++; + dvert++; + } + } + } + break; + } - /* free the old buffer */ - MEM_freeN(gps->points); + /* free the old buffer */ + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } - /* save the new buffer */ - gps->points = new_points; - gps->totpoints = tot; + /* save the new buffer */ + gps->points = new_points; + gps->dvert = new_dvert; + gps->totpoints = tot; - /* triangles cache needs to be recalculated */ - gps->flag |= GP_STROKE_RECALC_CACHES; - gps->tot_triangles = 0; + /* triangles cache needs to be recalculated */ + gps->flag |= GP_STROKE_RECALC_CACHES; + gps->tot_triangles = 0; - /* deselect the stroke, since none of its selected points will still be selected */ - gps->flag &= ~GP_STROKE_SELECT; - } + /* deselect the stroke, since none of its selected points will still be selected */ + gps->flag &= ~GP_STROKE_SELECT; + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + pt->flag &= ~GP_SPOINT_SELECT; + } + } - changed = true; + changed = true; + } + } } } } CTX_DATA_END; if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1146,17 +1689,17 @@ typedef struct tGPDeleteIsland { * becomes much less * 2) Each island gets converted to a new stroke */ -void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags) +void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, + int tag_flags, bool select) { tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands"); bool in_island = false; int num_islands = 0; - bGPDspoint *pt; - int i; /* First Pass: Identify start/end of islands */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + bGPDspoint *pt = gps->points; + for (int i = 0; i < gps->totpoints; i++, pt++) { if (pt->flag & tag_flags) { /* selected - stop accumulating to island */ in_island = false; @@ -1198,11 +1741,26 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */ new_stroke->totpoints = island->end_idx - island->start_idx + 1; - new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); - /* Copy over the relevant points */ + /* Copy over the relevant point data */ + new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment"); memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints); + /* Copy over vertex weight data (if available) */ + if (new_stroke->dvert != NULL) { + /* Copy over the relevant vertex-weight points */ + new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, "gp delete stroke fragment weight"); + memcpy(new_stroke->dvert, gps->dvert + island->start_idx, sizeof(MDeformVert) * new_stroke->totpoints); + + /* Copy weights */ + int e = island->start_idx; + for (int i = 0; i < new_stroke->totpoints; i++) { + MDeformVert *dvert_dst = &gps->dvert[e]; + MDeformVert *dvert_src = &new_stroke->dvert[i]; + dvert_dst->dw = MEM_dupallocN(dvert_src->dw); + e++; + } + } /* Each island corresponds to a new stroke. We must adjust the * timings of these new strokes: @@ -1222,6 +1780,11 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke pts = new_stroke->points; for (j = 0; j < new_stroke->totpoints; j++, pts++) { pts->time -= delta; + /* set flag for select again later */ + if (select == true) { + pts->flag &= ~GP_SPOINT_SELECT; + pts->flag |= GP_SPOINT_TAG; + } } } @@ -1239,53 +1802,70 @@ void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke MEM_freeN(islands); /* Delete the old stroke */ - MEM_freeN(gps->points); + if (gps->points) { + MEM_freeN(gps->points); + } + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } if (gps->triangles) { MEM_freeN(gps->triangles); } BLI_freelinkN(&gpf->strokes, gps); } - /* Split selected strokes into segments, splitting on selected points */ static int gp_delete_selected_points(bContext *C) { + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); bool changed = false; CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - bGPDstroke *gps, *gpsn; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - if (gpf == NULL) - continue; + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; - /* simply delete strokes which are selected */ - for (gps = gpf->strokes.first; gps; gps = gpsn) { - gpsn = gps->next; + if (gpf == NULL) + continue; - /* 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; + /* simply delete strokes which are selected */ + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = 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(ob, gpl, gps) == false) + continue; - if (gps->flag & GP_STROKE_SELECT) { - /* deselect old stroke, since it will be used as template for the new strokes */ - gps->flag &= ~GP_STROKE_SELECT; - /* delete unwanted points by splitting stroke into several smaller ones */ - gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT); + if (gps->flag & GP_STROKE_SELECT) { + /* deselect old stroke, since it will be used as template for the new strokes */ + gps->flag &= ~GP_STROKE_SELECT; - changed = true; + /* delete unwanted points by splitting stroke into several smaller ones */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false); + + changed = true; + } + } } } } CTX_DATA_END; if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1294,6 +1874,12 @@ static int gp_delete_selected_points(bContext *C) } } +/* simple wrapper to external call */ +int gp_delete_selected_point_wrap(bContext *C) +{ + return gp_delete_selected_points(C); +} + /* ----------------------------------- */ static int gp_delete_exec(bContext *C, wmOperator *op) @@ -1344,24 +1930,37 @@ void GPENCIL_OT_delete(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data"); } -static int gp_dissolve_exec(bContext *C, wmOperator *UNUSED(op)) +static int gp_dissolve_exec(bContext *C, wmOperator *op) { - return gp_dissolve_selected_points(C); + eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type"); + + return gp_dissolve_selected_points(C, mode); } void GPENCIL_OT_dissolve(wmOperatorType *ot) { + static EnumPropertyItem prop_gpencil_dissolve_types[] = { + { GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points" }, + { GP_DISSOLVE_BETWEEN, "BETWEEN", 0, "Dissolve Between", "Dissolve points between selected points" }, + { GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points" }, + { 0, NULL, 0, NULL, NULL } + }; + /* identifiers */ ot->name = "Dissolve"; ot->idname = "GPENCIL_OT_dissolve"; ot->description = "Delete selected points without splitting strokes"; /* callbacks */ + ot->invoke = WM_menu_invoke; ot->exec = gp_dissolve_exec; ot->poll = gp_stroke_edit_poll; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER; + + /* props */ + ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_dissolve_types, 0, "Type", "Method used for disolving Stroke points"); } /* ****************** Snapping - Strokes <-> Cursor ************************ */ @@ -1384,6 +1983,8 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); RegionView3D *rv3d = CTX_wm_region_data(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(C); const float gridf = rv3d->gridview; for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { @@ -1392,10 +1993,8 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix object */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { bGPDspoint *pt; @@ -1405,37 +2004,32 @@ static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op)) 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) + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) continue; // TODO: 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); + /* 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); + 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); - } + /* return data */ + copy_v3_v3(&pt->x, fpt); + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); } } } } } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + DEG_id_tag_update(&obact->id, DEG_TAG_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1463,6 +2057,8 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(C); \ const bool use_offset = RNA_boolean_get(op->ptr, "use_offset"); const float *cursor_global = ED_view3d_cursor3d_get(scene, v3d)->location; @@ -1473,10 +2069,8 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { bGPDspoint *pt; @@ -1486,7 +2080,7 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) 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) + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) continue; /* only continue if this stroke is selected (editable doesn't guarantee this)... */ if ((gps->flag & GP_STROKE_SELECT) == 0) @@ -1509,9 +2103,7 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) 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); - } + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); } } } @@ -1520,6 +2112,8 @@ static int gp_snap_to_cursor(bContext *C, wmOperator *op) } } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + DEG_id_tag_update(&obact->id, DEG_TAG_COPY_ON_WRITE); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -1551,6 +2145,8 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ + Object *obact = CTX_data_active_object(C); \ float *cursor = ED_view3d_cursor3d_get(scene, v3d)->location; float centroid[3] = {0.0f}; @@ -1566,10 +2162,8 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) bGPDframe *gpf = gpl->actframe; float diff_mat[4][4]; - /* calculate difference matrix if parent object */ - if (gpl->parent != NULL) { - ED_gpencil_parent_location(gpl, diff_mat); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { bGPDspoint *pt; @@ -1579,7 +2173,7 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) 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) + if (ED_gpencil_stroke_color_use(obact, gpl, gps) == false) continue; /* only continue if this stroke is selected (editable doesn't guarantee this)... */ if ((gps->flag & GP_STROKE_SELECT) == 0) @@ -1587,18 +2181,13 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) 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); + /* 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); - } + minmax_v3v3_v3(min, max, fpt); + count++; } } @@ -1616,7 +2205,9 @@ static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op)) } - WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); + return OPERATOR_FINISHED; } @@ -1650,13 +2241,20 @@ static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op)) 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; + if ((gps->thickness == 0) && (gpl->line_change == 0)) { + gps->thickness = gpl->thickness; + } + else { + gps->thickness = gps->thickness + gpl->line_change; + } } } /* clear value */ gpl->thickness = 0.0f; + gpl->line_change = 0; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1685,6 +2283,8 @@ enum { static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + const int type = RNA_enum_get(op->ptr, "type"); /* sanity checks */ @@ -1698,13 +2298,13 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) continue; for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) { - bGPDpalettecolor *palcolor = gps->palcolor; + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); /* 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)) + if (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_COLOR_LOCKED)) continue; switch (type) { @@ -1729,6 +2329,7 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1809,15 +2410,18 @@ static void gpencil_flip_stroke(bGPDstroke *gps) } /* Helper: copy point between strokes */ -static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3], +static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, int idx, float delta[3], float pressure, float strength, float deltatime) { bGPDspoint *newpoint; gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + if (gps->dvert != NULL) { + gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (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]; @@ -1825,6 +2429,14 @@ static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float newpoint->pressure = pressure; newpoint->strength = strength; newpoint->time = point->time + deltatime; + + if (gps->dvert != NULL) { + MDeformVert *dvert = &gps->dvert[idx]; + MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1]; + + newdvert->totweight = dvert->totweight; + newdvert->dw = MEM_dupallocN(dvert->dw); + } } /* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */ @@ -1870,18 +2482,18 @@ static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, co /* 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); + gpencil_stroke_copy_point(gps_a, &point, gps_a->totpoints - 1, 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); + gpencil_stroke_copy_point(gps_a, &point, 0, 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); + gpencil_stroke_copy_point(gps_a, pt, i, delta, pt->pressure, pt->strength, deltatime); } } } @@ -1891,8 +2503,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd); bGPDstroke *gps, *gpsn; - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); + Object *ob = CTX_data_active_object(C); bGPDframe *gpf_a = NULL; bGPDstroke *stroke_a = NULL; @@ -1928,7 +2539,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { continue; } @@ -1948,15 +2559,17 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) if (new_stroke == NULL) { new_stroke = MEM_dupallocN(stroke_a); new_stroke->points = MEM_dupallocN(stroke_a->points); + if (stroke_a->dvert != NULL) { + new_stroke->dvert = MEM_dupallocN(stroke_a->dvert); + BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke); + } 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; - BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname)); - new_stroke->flag |= GP_STROKE_RECALC_COLOR; + new_stroke->mat_nr = stroke_a->mat_nr; } } @@ -1995,6 +2608,7 @@ static int gp_stroke_join_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -2030,6 +2644,7 @@ void GPENCIL_OT_stroke_join(wmOperatorType *ot) static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) { bGPdata *gpd = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); /* sanity checks */ if (ELEM(NULL, gpd)) @@ -2049,7 +2664,7 @@ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { continue; } @@ -2061,6 +2676,7 @@ static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -2084,33 +2700,42 @@ void GPENCIL_OT_stroke_flip(wmOperatorType *ot) /* ***************** Reproject Strokes ********************** */ typedef enum eGP_ReprojectModes { + /* Axis (equal to lock axis) */ + GP_REPROJECT_AXIS = 0, /* On same plane, parallel to viewplane */ - GP_REPROJECT_PLANAR = 0, + GP_REPROJECT_PLANAR, /* Reprojected on to the scene geometry */ GP_REPROJECT_SURFACE, } eGP_ReprojectModes; -static bool gp_strokes_reproject_poll(bContext *C) -{ - /* 2 Requirements: - * - 1) Editable GP data - * - 2) 3D View only (2D editors don't have projection issues) - */ - return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C)); -} - static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = ar->regiondata; + View3D *v3d = sa->spacedata.first; + GP_SpaceConversion gsc = {NULL}; - eGP_ReprojectModes mode = RNA_boolean_get(op->ptr, "type"); + eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); + + int lock_axis = ts->gp_sculpt.lock_axis; + float origin[3]; + + if ((mode == GP_REPROJECT_AXIS) && (lock_axis == GP_LOCKAXIS_NONE)) { + BKE_report(op->reports, RPT_ERROR, "To reproject by axis, a lock axis must be set before"); + return OPERATOR_CANCELLED; + } /* init space conversion stuff */ gp_point_conversion_init(C, &gsc); /* init autodist for geometry projection */ if (mode == GP_REPROJECT_SURFACE) { - struct Depsgraph *depsgraph = CTX_data_depsgraph(C); view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar); ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0); } @@ -2127,9 +2752,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) /* Compute inverse matrix for unapplying parenting once instead of doing per-point */ /* TODO: add this bit to the iteration macro? */ - if (gpl->parent) { - invert_m4_m4(inverse_diff_mat, diff_mat); - } + invert_m4_m4(inverse_diff_mat, diff_mat); /* Adjust each point */ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { @@ -2140,19 +2763,28 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) * coordinates, resulting in lost precision, which in turn causes stairstepping * artifacts in the final points. */ - if (gpl->parent == NULL) { - gp_point_to_xy_fl(&gsc, gps, pt, &xy[0], &xy[1]); - } - else { - bGPDspoint pt2; - gp_point_to_parent_space(pt, diff_mat, &pt2); - gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + bGPDspoint pt2; + gp_point_to_parent_space(pt, diff_mat, &pt2); + gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]); + + /* Project stroke in the axis locked */ + if (mode == GP_REPROJECT_AXIS) { + if (lock_axis > GP_LOCKAXIS_NONE) { + ED_gp_get_drawing_reference(v3d, scene, ob, gpl, + ts->gpencil_v3d_align, origin); + ED_gp_project_point_to_plane(ob, rv3d, origin, + lock_axis - 1, &pt2); + + copy_v3_v3(&pt->x, &pt2.x); + + /* apply parent again */ + gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt); + } } - /* Project screenspace back to 3D space (from current perspective) * so that all points have been treated the same way */ - if (mode == GP_REPROJECT_PLANAR) { + else if (mode == GP_REPROJECT_PLANAR) { /* Planar - All on same plane parallel to the viewplane */ gp_point_xy_to_3d(&gsc, scene, xy, &pt->x); } @@ -2175,7 +2807,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) } /* Unapply parent corrections */ - if (gpl->parent) { + if (mode != GP_REPROJECT_AXIS) { mul_m4_v3(inverse_diff_mat, &pt->x); } } @@ -2183,6 +2815,7 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) } GP_EDITABLE_STROKES_END; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; } @@ -2190,6 +2823,9 @@ static int gp_strokes_reproject_exec(bContext *C, wmOperator *op) void GPENCIL_OT_reproject(wmOperatorType *ot) { static const EnumPropertyItem reproject_type[] = { + { GP_REPROJECT_AXIS, "AXIS", 0, "Axis", + "Reproject the strokes using the current lock axis configuration. This is the same projection using while" + "drawing new strokes" }, {GP_REPROJECT_PLANAR, "PLANAR", 0, "Planar", "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint " "using 'Cursor' Stroke Placement"}, @@ -2208,7 +2844,7 @@ void GPENCIL_OT_reproject(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = gp_strokes_reproject_exec; - ot->poll = gp_strokes_reproject_poll; + ot->poll = gp_strokes_edit3d_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2229,7 +2865,7 @@ static int gp_count_subdivision_cuts(bGPDstroke *gps) if (pt->flag & GP_SPOINT_SELECT) { if (i + 1 < gps->totpoints) { if (gps->points[i + 1].flag & GP_SPOINT_SELECT) { - ++totnewpoints; + totnewpoints++; } } } @@ -2237,6 +2873,7 @@ static int gp_count_subdivision_cuts(bGPDstroke *gps) return totnewpoints; } + static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) { bGPdata *gpd = ED_gpencil_data_get_active(C); @@ -2267,6 +2904,9 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) /* resize the points arrys */ gps->totpoints += totnewpoints; gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + if (gps->dvert != NULL) { + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + } gps->flag |= GP_STROKE_RECALC_CACHES; /* loop and interpolate */ @@ -2275,19 +2915,27 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) bGPDspoint *pt = &temp_points[i]; bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert_final = &gps->dvert[i2]; + /* copy current point */ copy_v3_v3(&pt_final->x, &pt->x); pt_final->pressure = pt->pressure; pt_final->strength = pt->strength; pt_final->time = pt->time; pt_final->flag = pt->flag; - ++i2; + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + i2++; /* if next point is selected add a half way point */ if (pt->flag & GP_SPOINT_SELECT) { if (i + 1 < oldtotpoints) { if (temp_points[i + 1].flag & GP_SPOINT_SELECT) { pt_final = &gps->points[i2]; + if (gps->dvert != NULL) { + dvert_final = &gps->dvert[i2]; + } /* Interpolate all values */ bGPDspoint *next = &temp_points[i + 1]; interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); @@ -2296,7 +2944,11 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt_final->time = interpf(pt->time, next->time, 0.5f); pt_final->flag |= GP_SPOINT_SELECT; - ++i2; + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + + i2++; } } } @@ -2309,6 +2961,7 @@ static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op) GP_EDITABLE_STROKES_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -2328,7 +2981,7 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) ot->poll = gp_active_layer_poll; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5); @@ -2336,3 +2989,414 @@ void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } + +/* ** simplify stroke *** */ +static int gp_stroke_simplify_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + float factor = RNA_float_get(op->ptr, "factor"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + /* simplify stroke using Ramer-Douglas-Peucker algorithm */ + BKE_gpencil_simplify_stroke(gps, factor); + } + } + GP_EDITABLE_STROKES_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_simplify(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Simplify Stroke"; + ot->idname = "GPENCIL_OT_stroke_simplify"; + ot->description = "Simplify selected stroked reducing number of points"; + + /* api callbacks */ + ot->exec = gp_stroke_simplify_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f); + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/* ** simplify stroke using fixed algorith *** */ +static int gp_stroke_simplify_fixed_exec(bContext *C, wmOperator *op) +{ + bGPdata *gpd = ED_gpencil_data_get_active(C); + int steps = RNA_int_get(op->ptr, "step"); + + /* sanity checks */ + if (ELEM(NULL, gpd)) + return OPERATOR_CANCELLED; + + /* Go through each editable + selected stroke */ + GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) + { + if (gps->flag & GP_STROKE_SELECT) { + for (int i = 0; i < steps; i++) { + BKE_gpencil_simplify_fixed(gps); + } + } + } + GP_EDITABLE_STROKES_END; + + /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Simplify Fixed Stroke"; + ot->idname = "GPENCIL_OT_stroke_simplify_fixed"; + ot->description = "Simplify selected stroked reducing number of points using fixed algorithm"; + + /* api callbacks */ + ot->exec = gp_stroke_simplify_fixed_exec; + ot->poll = gp_active_layer_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10); + + /* avoid re-using last var */ + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + +} + +/* ***************** Separate Strokes ********************** */ +typedef enum eGP_SeparateModes { + /* Points */ + GP_SEPARATE_POINT = 0, + /* Selected Strokes */ + GP_SEPARATE_STROKE, + /* Current Layer */ + GP_SEPARATE_LAYER, +} eGP_SeparateModes; + +static int gp_stroke_separate_exec(bContext *C, wmOperator *op) +{ + Base *base_new; + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Base *base_old = CTX_data_active_base(C); + bGPdata *gpd_src = ED_gpencil_data_get_active(C); + Object *ob = CTX_data_active_object(C); + + Object *ob_dst = NULL; + bGPdata *gpd_dst = NULL; + bGPDlayer *gpl_dst = NULL; + bGPDframe *gpf_dst = NULL; + bGPDspoint *pt; + Material *ma = NULL; + int i, idx; + + eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode"); + + /* sanity checks */ + if (ELEM(NULL, gpd_src)) { + return OPERATOR_CANCELLED; + } + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src); + + /* create a new object */ + base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, 0); + ob_dst = base_new->object; + + /* create new grease pencil datablock */ + // XXX: check usercounts + gpd_dst = BKE_gpencil_data_addnew(bmain, "GPencil"); + ob_dst->data = (bGPdata *)gpd_dst; + + int totslots = ob_dst->totcol; + int totadd = 0; + + /* loop old datablock and separate parts */ + if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) { + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + gpl_dst = NULL; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) { + continue; + } + + gpf_dst = NULL; + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = 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(ob, gpl, gps) == false) { + continue; + } + /* separate selected strokes */ + if (gps->flag & GP_STROKE_SELECT) { + /* add layer if not created before */ + if (gpl_dst == NULL) { + gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false); + } + + /* add frame if not created before */ + if (gpf_dst == NULL) { + gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW); + } + + /* add duplicate materials */ + ma = give_current_material(ob, gps->mat_nr + 1); + idx = BKE_object_material_slot_find_index(ob_dst, ma); + if (idx == 0) { + + totadd++; + ob_dst->actcol = totadd; + ob_dst->totcol = totadd; + + if (totadd > totslots) { + BKE_object_material_slot_add(bmain, ob_dst); + } + + assign_material(bmain, ob_dst, ma, ob_dst->totcol, BKE_MAT_ASSIGN_EXISTING); + idx = totadd; + } + + /* selected points mode */ + if (mode == GP_SEPARATE_POINT) { + /* make copy of source stroke */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + + /* reasign material */ + gps_dst->mat_nr = idx - 1; + + /* link to destination frame */ + BLI_addtail(&gpf_dst->strokes, gps_dst); + + /* Invert selection status of all points in destination stroke */ + for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { + pt->flag ^= GP_SPOINT_SELECT; + } + + /* delete selected points from destination stroke */ + gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false); + + /* delete selected points from origin stroke */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false); + } + /* selected strokes mode */ + else if (mode == GP_SEPARATE_STROKE) { + /* deselect old stroke */ + gps->flag &= ~GP_STROKE_SELECT; + /* unlink from source frame */ + BLI_remlink(&gpf->strokes, gps); + gps->prev = gps->next = NULL; + /* relink to destination frame */ + BLI_addtail(&gpf_dst->strokes, gps); + /* reasign material */ + gps->mat_nr = idx - 1; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + } + else if (mode == GP_SEPARATE_LAYER) { + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + if (gpl) { + /* try to set a new active layer in source datablock */ + if (gpl->prev) { + BKE_gpencil_layer_setactive(gpd_src, gpl->prev); + } + else if (gpl->next) { + BKE_gpencil_layer_setactive(gpd_src, gpl->next); + } + /* unlink from source datablock */ + BLI_remlink(&gpd_src->layers, gpl); + gpl->prev = gpl->next = NULL; + /* relink to destination datablock */ + BLI_addtail(&gpd_dst->layers, gpl); + } + } + DEG_id_tag_update(&gpd_src->id, OB_RECALC_OB | OB_RECALC_DATA); + DEG_id_tag_update(&gpd_dst->id, OB_RECALC_OB | OB_RECALC_DATA); + + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_separate(wmOperatorType *ot) +{ + static const EnumPropertyItem separate_type[] = { + {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points" }, + {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"}, + {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer" }, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Separate Strokes"; + ot->idname = "GPENCIL_OT_stroke_separate"; + ot->description = "Separate the selected strokes or layer in a new grease pencil object"; + + /* callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gp_stroke_separate_exec; + ot->poll = gp_strokes_edit3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", ""); +} + +/* ***************** Split Strokes ********************** */ +static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); + bGPDspoint *pt; + int i; + + /* sanity checks */ + if (ELEM(NULL, gpd)) { + return OPERATOR_CANCELLED; + } + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + + /* loop strokes and split parts */ + CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) + { + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } + + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + bGPDstroke *gps, *gpsn; + + if (gpf == NULL) { + continue; + } + + for (gps = gpf->strokes.first; gps; gps = gpsn) { + gpsn = 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(ob, gpl, gps) == false) { + continue; + } + /* split selected strokes */ + if (gps->flag & GP_STROKE_SELECT) { + /* make copy of source stroke */ + bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps); + + /* link to same frame */ + BLI_addtail(&gpf->strokes, gps_dst); + + /* invert selection status of all points in destination stroke */ + for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) { + pt->flag ^= GP_SPOINT_SELECT; + } + + /* delete selected points from destination stroke */ + gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true); + + /* delete selected points from origin stroke */ + gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false); + } + } + /* select again tagged points */ + for (gps = gpf->strokes.first; gps; gps = gps->next) { + bGPDspoint *ptn = gps->points; + for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) { + if (ptn->flag & GP_SPOINT_TAG) { + ptn->flag |= GP_SPOINT_SELECT; + ptn->flag &= ~GP_SPOINT_TAG; + } + } + } + } + + /* if not multiedit, exit loop*/ + if (!is_multiedit) { + break; + } + } + } + CTX_DATA_END; + + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_stroke_split(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Split Strokes"; + ot->idname = "GPENCIL_OT_stroke_split"; + ot->description = "Split selected points as new stroke on same frame"; + + /* callbacks */ + ot->exec = gp_stroke_split_exec; + ot->poll = gp_strokes_edit3d_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c new file mode 100644 index 00000000000..253f0db865e --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -0,0 +1,1250 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation, Joshua Leung + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez, Joshua Leung + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/gpencil/gpencil_fill.c + * \ingroup edgpencil + */ + +#include <stdio.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_utildefines.h" +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_stack.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_image_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_image.h" +#include "BKE_gpencil.h" +#include "BKE_material.h" +#include "BKE_context.h" +#include "BKE_screen.h" +#include "BKE_paint.h" + +#include "ED_gpencil.h" +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_view3d.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "GPU_immediate.h" +#include "GPU_draw.h" +#include "GPU_matrix.h" +#include "GPU_framebuffer.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +#define LEAK_HORZ 0 +#define LEAK_VERT 1 + + +/* Temporary fill operation data (op->customdata) */ +typedef struct tGPDfill { + struct Main *bmain; + struct Depsgraph *depsgraph; + struct wmWindow *win; /* window where painting originated */ + struct Scene *scene; /* current scene from context */ + struct Object *ob; /* current active gp object */ + struct ScrArea *sa; /* area where painting originated */ + struct RegionView3D *rv3d; /* region where painting originated */ + struct View3D *v3d; /* view3 where painting originated */ + struct ARegion *ar; /* region where painting originated */ + struct bGPdata *gpd; /* current GP datablock */ + struct Material *mat; /* current material */ + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *gpf; /* frame */ + + short flag; /* flags */ + short oldkey; /* avoid too fast events */ + bool on_back; /* send to back stroke */ + + int center[2]; /* mouse fill center position */ + int sizex; /* windows width */ + int sizey; /* window height */ + int lock_axis; /* lock to viewport axis */ + + short fill_leak; /* number of pixel to consider the leak is too small (x 2) */ + float fill_threshold; /* factor for transparency */ + int fill_simplylvl; /* number of simplify steps */ + int fill_draw_mode; /* boundary limits drawing mode */ + + short sbuffer_size; /* number of elements currently in cache */ + void *sbuffer; /* temporary points */ + float *depth_arr; /* depth array for reproject */ + + Image *ima; /* temp image */ + BLI_Stack *stack; /* temp points data */ + void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ +} tGPDfill; + + + /* draw a given stroke using same thickness and color for all points */ +static void gp_draw_basic_stroke( + tGPDfill *tgpf, bGPDstroke *gps, const float diff_mat[4][4], + bool cyclic, float ink[4], int flag, float thershold) +{ + bGPDspoint *points = gps->points; + + Material *ma = tgpf->mat; + MaterialGPencilStyle *gp_style = ma->gp_style; + + int totpoints = gps->totpoints; + float fpt[3]; + float col[4]; + + copy_v4_v4(col, ink); + + /* if cyclic needs more vertex */ + int cyclic_add = (cyclic) ? 1 : 0; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint color = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR); + + /* draw stroke curve */ + glLineWidth(1.0f); + immBeginAtMost(GPU_PRIM_LINE_STRIP, totpoints + cyclic_add); + const bGPDspoint *pt = points; + + for (int i = 0; i < totpoints; i++, pt++) { + + if (flag & GP_BRUSH_FILL_HIDE) { + float alpha = gp_style->stroke_rgba[3] * pt->strength; + CLAMP(alpha, 0.0f, 1.0f); + col[3] = alpha <= thershold ? 0.0f : 1.0f; + } + else { + col[3] = 1.0f; + } + /* set point */ + immAttrib4fv(color, col); + mul_v3_m4v3(fpt, diff_mat, &pt->x); + immVertex3fv(pos, fpt); + } + + if (cyclic && totpoints > 2) { + /* draw line to first point to complete the cycle */ + immAttrib4fv(color, col); + mul_v3_m4v3(fpt, diff_mat, &points->x); + immVertex3fv(pos, fpt); + } + + immEnd(); + immUnbindProgram(); +} + +/* loop all layers */ +static void gp_draw_datablock(tGPDfill *tgpf, float ink[4]) +{ + /* duplicated: etempFlags */ + enum { + GP_DRAWFILLS_NOSTATUS = (1 << 0), /* don't draw status info */ + GP_DRAWFILLS_ONLY3D = (1 << 1), /* only draw 3d-strokes */ + }; + + Object *ob = tgpf->ob; + bGPdata *gpd = tgpf->gpd; + int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); + + tGPDdraw tgpw; + tgpw.rv3d = tgpf->rv3d; + tgpw.depsgraph = tgpf->depsgraph; + tgpw.ob = ob; + tgpw.gpd = gpd; + tgpw.offsx = 0; + tgpw.offsy = 0; + tgpw.winx = tgpf->ar->winx; + tgpw.winy = tgpf->ar->winy; + tgpw.dflag = 0; + tgpw.disable_fill = 1; + tgpw.dflag |= (GP_DRAWFILLS_ONLY3D | GP_DRAWFILLS_NOSTATUS); + + glEnable(GL_BLEND); + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* calculate parent position */ + ED_gpencil_parent_location(tgpw.depsgraph, ob, gpd, gpl, tgpw.diff_mat); + + /* do not draw layer if hidden */ + if (gpl->flag & GP_LAYER_HIDE) + continue; + + /* get frame to draw */ + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); + if (gpf == NULL) + continue; + + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if stroke can be drawn */ + if ((gps->points == NULL) || (gps->totpoints < 2)) { + continue; + } + /* check if the color is visible */ + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + if ((gp_style == NULL) || (gp_style->flag & GP_STYLE_COLOR_HIDE)) { + continue; + } + + tgpw.gps = gps; + tgpw.gpl = gpl; + tgpw.gpf = gpf; + tgpw.t_gpf = gpf; + + /* reduce thickness to avoid gaps */ + tgpw.lthick = gpl->line_change - 4; + tgpw.opacity = 1.0; + copy_v4_v4(tgpw.tintcolor, ink); + tgpw.onion = true; + tgpw.custonion = true; + + /* normal strokes */ + if ((tgpf->fill_draw_mode == GP_FILL_DMODE_STROKE) || + (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) + { + ED_gp_draw_fill(&tgpw); + + } + + /* 3D Lines with basic shapes and invisible lines */ + if ((tgpf->fill_draw_mode == GP_FILL_DMODE_CONTROL) || + (tgpf->fill_draw_mode == GP_FILL_DMODE_BOTH)) + { + gp_draw_basic_stroke(tgpf, gps, tgpw.diff_mat, gps->flag & GP_STROKE_CYCLIC, ink, + tgpf->flag, tgpf->fill_threshold); + } + } + } + + glDisable(GL_BLEND); +} + + /* draw strokes in offscreen buffer */ +static void gp_render_offscreen(tGPDfill *tgpf) +{ + bool is_ortho = false; + float winmat[4][4]; + + if (!tgpf->gpd) { + return; + } + + char err_out[256] = "unknown"; + GPUOffScreen *offscreen = GPU_offscreen_create(tgpf->sizex, tgpf->sizey, 0, true, false, err_out); + GPU_offscreen_bind(offscreen, true); + uint flag = IB_rect | IB_rectfloat; + ImBuf *ibuf = IMB_allocImBuf(tgpf->sizex, tgpf->sizey, 32, flag); + + rctf viewplane; + float clipsta, clipend; + + is_ortho = ED_view3d_viewplane_get(tgpf->depsgraph, tgpf->v3d, tgpf->rv3d, tgpf->sizex, tgpf->sizey, &viewplane, &clipsta, &clipend, NULL); + if (is_ortho) { + orthographic_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, -clipend, clipend); + } + else { + perspective_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend); + } + + /* set temporary new size */ + int bwinx = tgpf->ar->winx; + int bwiny = tgpf->ar->winy; + rcti brect = tgpf->ar->winrct; + + tgpf->ar->winx = (short) tgpf->sizex; + tgpf->ar->winy = (short) tgpf->sizey; + tgpf->ar->winrct.xmin = 0; + tgpf->ar->winrct.ymin = 0; + tgpf->ar->winrct.xmax = tgpf->sizex; + tgpf->ar->winrct.ymax = tgpf->sizey; + + GPU_matrix_push_projection(); + GPU_matrix_identity_set(); + GPU_matrix_push(); + GPU_matrix_identity_set(); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + ED_view3d_update_viewmat( + tgpf->depsgraph, tgpf->scene, tgpf->v3d, tgpf->ar, + NULL, winmat, NULL); + /* set for opengl */ + GPU_matrix_projection_set(tgpf->rv3d->winmat); + GPU_matrix_set(tgpf->rv3d->viewmat); + + /* draw strokes */ + float ink[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + gp_draw_datablock(tgpf, ink); + + /* restore size */ + tgpf->ar->winx = (short)bwinx; + tgpf->ar->winy = (short)bwiny; + tgpf->ar->winrct = brect; + + GPU_matrix_pop_projection(); + GPU_matrix_pop(); + + /* create a image to see result of template */ + if (ibuf->rect_float) { + GPU_offscreen_read_pixels(offscreen, GL_FLOAT, ibuf->rect_float); + } + else if (ibuf->rect) { + GPU_offscreen_read_pixels(offscreen, GL_UNSIGNED_BYTE, ibuf->rect); + } + if (ibuf->rect_float && ibuf->rect) { + IMB_rect_from_float(ibuf); + } + + tgpf->ima = BKE_image_add_from_imbuf(tgpf->bmain, ibuf, "GP_fill"); + tgpf->ima->id.tag |= LIB_TAG_DOIT; + + BKE_image_release_ibuf(tgpf->ima, ibuf, NULL); + + /* switch back to window-system-provided framebuffer */ + GPU_offscreen_unbind(offscreen, true); + GPU_offscreen_free(offscreen); +} + +/* return pixel data (rgba) at index */ +static void get_pixel(ImBuf *ibuf, int idx, float r_col[4]) +{ + if (ibuf->rect_float) { + float *frgba = &ibuf->rect_float[idx * 4]; + copy_v4_v4(r_col, frgba); + } + else { + /* XXX: This case probably doesn't happen, as we only write to the float buffer, + * but we get compiler warnings about uninitialised vars otherwise + */ + BLI_assert(!"gpencil_fill.c - get_pixel() non-float case is used!"); + zero_v4(r_col); + } +} + +/* set pixel data (rgba) at index */ +static void set_pixel(ImBuf *ibuf, int idx, const float col[4]) +{ + //BLI_assert(idx <= ibuf->x * ibuf->y); + if (ibuf->rect) { + uint *rrect = &ibuf->rect[idx]; + uchar ccol[4]; + + rgba_float_to_uchar(ccol, col); + *rrect = *((uint *)ccol); + } + + if (ibuf->rect_float) { + float *rrectf = &ibuf->rect_float[idx * 4]; + copy_v4_v4(rrectf, col); + } +} + +/* check if the size of the leak is narrow to determine if the stroke is closed + * this is used for strokes with small gaps between them to get a full fill + * and do not get a full screen fill. + * + * \param ibuf Image pixel data + * \param maxpixel Maximum index + * \param limit Limit of pixels to analize + * \param index Index of current pixel + * \param type 0-Horizontal 1-Vertical + */ +static bool is_leak_narrow(ImBuf *ibuf, const int maxpixel, int limit, int index, int type) +{ + float rgba[4]; + int i; + int pt; + bool t_a = false; + bool t_b = false; + + /* Horizontal leak (check vertical pixels) + * X + * X + * xB7 + * X + * X + */ + if (type == LEAK_HORZ) { + /* pixels on top */ + for (i = 1; i <= limit; i++) { + pt = index + (ibuf->x * i); + if (pt <= maxpixel) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_a = true; + break; + } + } + else { + t_a = true; /* edge of image*/ + break; + } + } + /* pixels on bottom */ + for (i = 1; i <= limit; i++) { + pt = index - (ibuf->x * i); + if (pt >= 0) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_b = true; + break; + } + } + else { + t_b = true; /* edge of image*/ + break; + } + } + } + + /* Vertical leak (check horizontal pixels) + * + * XXXxB7XX + * + */ + if (type == LEAK_VERT) { + /* get pixel range of the row */ + int row = index / ibuf->x; + int lowpix = row * ibuf->x; + int higpix = lowpix + ibuf->x - 1; + + /* pixels to right */ + for (i = 0; i < limit; i++) { + pt = index - (limit - i); + if (pt >= lowpix) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_a = true; + break; + } + } + else { + t_a = true; /* edge of image*/ + break; + } + } + /* pixels to left */ + for (i = 0; i < limit; i++) { + pt = index + (limit - i); + if (pt <= higpix) { + get_pixel(ibuf, pt, rgba); + if (rgba[0] == 1.0f) { + t_b = true; + break; + } + } + else { + t_b = true; /* edge of image */ + break; + } + } + } + return (bool)(t_a && t_b); +} + +/* Boundary fill inside strokes + * Fills the space created by a set of strokes using the stroke color as the boundary + * of the shape to fill. + * + * \param tgpf Temporary fill data + */ +static void gpencil_boundaryfill_area(tGPDfill *tgpf) +{ + ImBuf *ibuf; + float rgba[4]; + void *lock; + const float fill_col[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + const int maxpixel = (ibuf->x * ibuf->y) - 1; + + BLI_Stack *stack = BLI_stack_new(sizeof(int), __func__); + + /* calculate index of the seed point using the position of the mouse */ + int index = (tgpf->sizex * tgpf->center[1]) + tgpf->center[0]; + if ((index >= 0) && (index < maxpixel)) { + BLI_stack_push(stack, &index); + } + + /* the fill use a stack to save the pixel list instead of the common recursive + * 4-contact point method. + * The problem with recursive calls is that for big fill areas, we can get max limit + * of recursive calls and STACK_OVERFLOW error. + * + * The 4-contact point analyze the pixels to the left, right, bottom and top + * ----------- + * | X | + * | XoX | + * | X | + * ----------- + */ + while (!BLI_stack_is_empty(stack)) { + int v; + BLI_stack_pop(stack, &v); + + get_pixel(ibuf, v, rgba); + + if (true) { /* Was: 'rgba' */ + /* check if no border(red) or already filled color(green) */ + if ((rgba[0] != 1.0f) && (rgba[1] != 1.0f)) { + /* fill current pixel */ + set_pixel(ibuf, v, fill_col); + + /* add contact pixels */ + /* pixel left */ + if (v - 1 >= 0) { + index = v - 1; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { + BLI_stack_push(stack, &index); + } + } + /* pixel right */ + if (v + 1 < maxpixel) { + index = v + 1; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_HORZ)) { + BLI_stack_push(stack, &index); + } + } + /* pixel top */ + if (v + tgpf->sizex < maxpixel) { + index = v + tgpf->sizex; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { + BLI_stack_push(stack, &index); + } + } + /* pixel bottom */ + if (v - tgpf->sizex >= 0) { + index = v - tgpf->sizex; + if (!is_leak_narrow(ibuf, maxpixel, tgpf->fill_leak, v, LEAK_VERT)) { + BLI_stack_push(stack, &index); + } + } + } + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; + /* free temp stack data */ + BLI_stack_free(stack); +} + +/* clean external border of image to avoid infinite loops */ +static void gpencil_clean_borders(tGPDfill *tgpf) +{ + ImBuf *ibuf; + void *lock; + const float fill_col[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + int idx; + int pixel = 0; + + /* horizontal lines */ + for (idx = 0; idx < ibuf->x - 1; idx++) { + /* bottom line */ + set_pixel(ibuf, idx, fill_col); + /* top line */ + pixel = idx + (ibuf->x * (ibuf->y - 1)); + set_pixel(ibuf, pixel, fill_col); + } + /* vertical lines */ + for (idx = 0; idx < ibuf->y; idx++) { + /* left line */ + set_pixel(ibuf, ibuf->x * idx, fill_col); + /* right line */ + pixel = ibuf->x * idx + (ibuf->x - 1); + set_pixel(ibuf, pixel, fill_col); + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } + + tgpf->ima->id.tag |= LIB_TAG_DOIT; +} + +/* Get the outline points of a shape using Moore Neighborhood algorithm + * + * This is a Blender customized version of the general algorithm described + * in https://en.wikipedia.org/wiki/Moore_neighborhood + */ +static void gpencil_get_outline_points(tGPDfill *tgpf) +{ + ImBuf *ibuf; + float rgba[4]; + void *lock; + int v[2]; + int boundary_co[2]; + int start_co[2]; + int backtracked_co[2]; + int current_check_co[2]; + int prev_check_co[2]; + int backtracked_offset[1][2] = {{0, 0}}; + // bool boundary_found = false; + bool start_found = false; + const int NEIGHBOR_COUNT = 8; + + int offset[8][2] = { + { -1, -1 }, + { 0, -1 }, + { 1, -1 }, + { 1, 0 }, + { 1, 1 }, + { 0, 1 }, + { -1, 1 }, + { -1, 0 } + }; + + tgpf->stack = BLI_stack_new(sizeof(int[2]), __func__); + + ibuf = BKE_image_acquire_ibuf(tgpf->ima, NULL, &lock); + int imagesize = ibuf->x * ibuf->y; + + /* find the initial point to start outline analysis */ + for (int idx = imagesize; idx >= 0; idx--) { + get_pixel(ibuf, idx, rgba); + if (rgba[1] == 1.0f) { + boundary_co[0] = idx % ibuf->x; + boundary_co[1] = idx / ibuf->x; + copy_v2_v2_int(start_co, boundary_co); + backtracked_co[0] = (idx - 1) % ibuf->x; + backtracked_co[1] = (idx - 1) / ibuf->x; + backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; + backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; + copy_v2_v2_int(prev_check_co, start_co); + + BLI_stack_push(tgpf->stack, &boundary_co); + start_found = true; + break; + } + } + + while (start_found) { + int cur_back_offset = -1; + for (int i = 0; i < NEIGHBOR_COUNT; i++) { + if (backtracked_offset[0][0] == offset[i][0] && + backtracked_offset[0][1] == offset[i][1]) + { + /* Finding the bracktracked pixel offset index */ + cur_back_offset = i; + break; + } + } + + int loop = 0; + while (loop < (NEIGHBOR_COUNT - 1) && cur_back_offset != -1) { + int offset_idx = (cur_back_offset + 1) % NEIGHBOR_COUNT; + current_check_co[0] = boundary_co[0] + offset[offset_idx][0]; + current_check_co[1] = boundary_co[1] + offset[offset_idx][1]; + + int image_idx = ibuf->x * current_check_co[1] + current_check_co[0]; + get_pixel(ibuf, image_idx, rgba); + + /* find next boundary pixel */ + if (rgba[1] == 1.0f) { + copy_v2_v2_int(boundary_co, current_check_co); + copy_v2_v2_int(backtracked_co, prev_check_co); + backtracked_offset[0][0] = backtracked_co[0] - boundary_co[0]; + backtracked_offset[0][1] = backtracked_co[1] - boundary_co[1]; + + BLI_stack_push(tgpf->stack, &boundary_co); + + break; + } + copy_v2_v2_int(prev_check_co, current_check_co); + cur_back_offset++; + loop++; + } + /* current pixel is equal to starting pixel */ + if (boundary_co[0] == start_co[0] && + boundary_co[1] == start_co[1]) + { + BLI_stack_pop(tgpf->stack, &v); + // boundary_found = true; + break; + } + } + + /* release ibuf */ + if (ibuf) { + BKE_image_release_ibuf(tgpf->ima, ibuf, lock); + } +} + +/* get z-depth array to reproject on surface */ +static void gpencil_get_depth_array(tGPDfill *tgpf) +{ + tGPspoint *ptc; + ToolSettings *ts = tgpf->scene->toolsettings; + int totpoints = tgpf->sbuffer_size; + int i = 0; + + if (totpoints == 0) { + return; + } + + /* for surface sketching, need to set the right OpenGL context stuff so that + * the conversions will project the values correctly... + */ + if (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) { + /* need to restore the original projection settings before packing up */ + view3d_region_operator_needs_opengl(tgpf->win, tgpf->ar); + ED_view3d_autodist_init(tgpf->depsgraph, tgpf->ar, tgpf->v3d, 0); + + /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ + int depth_margin = 0; + + /* get an array of depths, far depths are blended */ + int mval[2], mval_prev[2] = { 0 }; + int interp_depth = 0; + int found_depth = 0; + + tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points"); + + for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) { + copy_v2_v2_int(mval, &ptc->x); + + if ((ED_view3d_autodist_depth( + tgpf->ar, mval, depth_margin, tgpf->depth_arr + i) == 0) && + (i && (ED_view3d_autodist_depth_seg( + tgpf->ar, mval, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) + { + interp_depth = true; + } + else { + found_depth = true; + } + + copy_v2_v2_int(mval_prev, mval); + } + + if (found_depth == false) { + /* eeh... not much we can do.. :/, ignore depth in this case */ + for (i = totpoints - 1; i >= 0; i--) + tgpf->depth_arr[i] = 0.9999f; + } + else { + if (interp_depth) { + interp_sparse_array(tgpf->depth_arr, totpoints, FLT_MAX); + } + } + } +} + +/* create array of points using stack as source */ +static void gpencil_points_from_stack(tGPDfill *tgpf) +{ + tGPspoint *point2D; + int totpoints = BLI_stack_count(tgpf->stack); + if (totpoints == 0) { + return; + } + + tgpf->sbuffer_size = (short)totpoints; + tgpf->sbuffer = MEM_callocN(sizeof(tGPspoint) * totpoints, __func__); + + point2D = tgpf->sbuffer; + while (!BLI_stack_is_empty(tgpf->stack)) { + int v[2]; + BLI_stack_pop(tgpf->stack, &v); + point2D->x = v[0]; + point2D->y = v[1]; + + point2D->pressure = 1.0f; + point2D->strength = 1.0f;; + point2D->time = 0.0f; + point2D++; + } +} + +/* create a grease pencil stroke using points in buffer */ +static void gpencil_stroke_from_buffer(tGPDfill *tgpf) +{ + ToolSettings *ts = tgpf->scene->toolsettings; + int cfra_eval = (int)DEG_get_ctime(tgpf->depsgraph); + + Brush *brush; + brush = BKE_brush_getactive_gpencil(ts); + if (brush == NULL) { + return; + } + + bGPDspoint *pt; + MDeformVert *dvert; + tGPspoint *point2D; + + if (tgpf->sbuffer_size == 0) { + return; + } + + /* get frame or create a new one */ + tgpf->gpf = BKE_gpencil_layer_getframe(tgpf->gpl, cfra_eval, GP_GETFRAME_ADD_NEW); + + /* create new stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "bGPDstroke"); + gps->thickness = brush->size; + gps->inittime = 0.0f; + + /* the polygon must be closed, so enabled cyclic */ + gps->flag |= GP_STROKE_CYCLIC; + gps->flag |= GP_STROKE_3DSPACE; + + gps->mat_nr = BKE_object_material_slot_find_index(tgpf->ob, tgpf->mat) - 1; + + /* allocate memory for storage points */ + gps->totpoints = tgpf->sbuffer_size; + gps->points = MEM_callocN(sizeof(bGPDspoint) * tgpf->sbuffer_size, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * tgpf->sbuffer_size, "gp_stroke_weights"); + + /* initialize triangle memory to dummy data */ + gps->tot_triangles = 0; + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* add stroke to frame */ + if ((ts->gpencil_flags & GP_TOOL_FLAG_PAINT_ONBACK) || (tgpf->on_back == true)) { + BLI_addhead(&tgpf->gpf->strokes, gps); + } + else { + BLI_addtail(&tgpf->gpf->strokes, gps); + } + + /* add points */ + pt = gps->points; + dvert = gps->dvert; + point2D = (tGPspoint *)tgpf->sbuffer; + for (int i = 0; i < tgpf->sbuffer_size && point2D; i++, point2D++, pt++, dvert++) { + /* convert screen-coordinates to 3D coordinates */ + gp_stroke_convertcoords_tpoint( + tgpf->scene, tgpf->ar, tgpf->v3d, tgpf->ob, + tgpf->gpl, point2D, + tgpf->depth_arr ? tgpf->depth_arr + i : NULL, + &pt->x); + + pt->pressure = 1.0f; + pt->strength = 1.0f;; + pt->time = 0.0f; + + dvert->totweight = 0; + dvert->dw = NULL; + } + + /* smooth stroke */ + float reduce = 0.0f; + float smoothfac = 1.0f; + for (int r = 0; r < 1; r++) { + for (int i = 0; i < gps->totpoints; i++) { + BKE_gpencil_smooth_stroke(gps, i, smoothfac - reduce); + } + reduce += 0.25f; // reduce the factor + } + + /* if axis locked, reproject to plane locked */ + if ((tgpf->lock_axis > GP_LOCKAXIS_NONE) && ((ts->gpencil_v3d_align & GP_PROJECT_DEPTH_VIEW) == 0)) { + float origin[3]; + ED_gp_get_drawing_reference(tgpf->v3d, tgpf->scene, tgpf->ob, tgpf->gpl, + ts->gpencil_v3d_align, origin); + ED_gp_project_stroke_to_plane(tgpf->ob, tgpf->rv3d, gps, origin, + tgpf->lock_axis - 1); + } + + /* if parented change position relative to parent object */ + for (int a = 0; a < tgpf->sbuffer_size; a++) { + pt = &gps->points[a]; + gp_apply_parent_point(tgpf->depsgraph, tgpf->ob, tgpf->gpd, tgpf->gpl, pt); + } + + /* simplify stroke */ + for (int b = 0; b < tgpf->fill_simplylvl; b++) { + BKE_gpencil_simplify_fixed(gps); + } +} + +/* ----------------------- */ +/* Drawing */ +/* Helper: Draw status message while the user is running the operator */ +static void gpencil_fill_status_indicators(bContext *C, tGPDfill *UNUSED(tgpf)) +{ + const char *status_str = IFACE_("Fill: ESC/RMB cancel, LMB Fill, Shift Draw on Back"); + ED_workspace_status_text(C, status_str); +} + +/* draw boundary lines to see fill limits */ +static void gpencil_draw_boundary_lines(const bContext *UNUSED(C), tGPDfill *tgpf) +{ + if (!tgpf->gpd) { + return; + } + float ink[4] = { 1.0f, 0.0f, 0.0f, 1.0f }; + gp_draw_datablock(tgpf, ink); +} + +/* Drawing callback for modal operator in 3d mode */ +static void gpencil_fill_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) +{ + tGPDfill *tgpf = (tGPDfill *)arg; + /* draw only in the region that originated operator. This is required for multiwindow */ + ARegion *ar = CTX_wm_region(C); + if (ar != tgpf->ar) { + return; + } + + gpencil_draw_boundary_lines(C, tgpf); +} + +/* check if context is suitable for filling */ +static bool gpencil_fill_poll(bContext *C) +{ + if (ED_operator_regionactive(C)) { + ScrArea *sa = CTX_wm_area(C); + if (sa->spacetype == SPACE_VIEW3D) { + return 1; + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not valid for filling operator"); + return 0; + } + } + else { + CTX_wm_operator_poll_msg_set(C, "Active region not set"); + return 0; + } +} + +/* Allocate memory and initialize values */ +static tGPDfill *gp_session_init_fill(bContext *C, wmOperator *UNUSED(op)) +{ + tGPDfill *tgpf = MEM_callocN(sizeof(tGPDfill), "GPencil Fill Data"); + + /* define initial values */ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Main *bmain = CTX_data_main(C); + + /* set current scene and window info */ + tgpf->bmain = CTX_data_main(C); + tgpf->scene = CTX_data_scene(C); + tgpf->ob = CTX_data_active_object(C); + tgpf->sa = CTX_wm_area(C); + tgpf->ar = CTX_wm_region(C); + tgpf->rv3d = tgpf->ar->regiondata; + tgpf->v3d = tgpf->sa->spacedata.first; + tgpf->depsgraph = CTX_data_depsgraph(C); + tgpf->win = CTX_wm_window(C); + + /* set GP datablock */ + tgpf->gpd = gpd; + tgpf->gpl = BKE_gpencil_layer_getactive(gpd); + + tgpf->lock_axis = ts->gp_sculpt.lock_axis; + + tgpf->oldkey = -1; + tgpf->sbuffer_size = 0; + tgpf->sbuffer = NULL; + tgpf->depth_arr = NULL; + + /* save filling parameters */ + Brush *brush = BKE_brush_getactive_gpencil(ts); + tgpf->flag = brush->gpencil_settings->flag; + tgpf->fill_leak = brush->gpencil_settings->fill_leak; + tgpf->fill_threshold = brush->gpencil_settings->fill_threshold; + tgpf->fill_simplylvl = brush->gpencil_settings->fill_simplylvl; + tgpf->fill_draw_mode = brush->gpencil_settings->fill_draw_mode; + + /* get color info */ + Material *ma = BKE_gpencil_get_material_from_brush(brush); + /* if no brush defaults, get material and color info */ + if ((ma == NULL) || (ma->gp_style == NULL)) { + ma = BKE_gpencil_material_ensure(bmain, tgpf->ob); + /* assign always the first material to the brush */ + brush->gpencil_settings->material = give_current_material(tgpf->ob, 1); + } + + tgpf->mat = ma; + + /* init undo */ + gpencil_undo_init(tgpf->gpd); + + /* return context data for running operator */ + return tgpf; +} + +/* end operator */ +static void gpencil_fill_exit(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + + /* clear undo stack */ + gpencil_undo_finish(); + + /* restore cursor to indicate end of fill */ + WM_cursor_modal_restore(CTX_wm_window(C)); + + tGPDfill *tgpf = op->customdata; + + /* don't assume that operator data exists at all */ + if (tgpf) { + /* clear status message area */ + ED_workspace_status_text(C, NULL); + + MEM_SAFE_FREE(tgpf->sbuffer); + MEM_SAFE_FREE(tgpf->depth_arr); + + /* remove drawing handler */ + if (tgpf->draw_handle_3d) { + ED_region_draw_cb_exit(tgpf->ar->type, tgpf->draw_handle_3d); + } + + /* delete temp image */ + if (tgpf->ima) { + for (Image *ima = bmain->image.first; ima; ima = ima->id.next) { + if (ima == tgpf->ima) { + BLI_remlink(&bmain->image, ima); + BKE_image_free(tgpf->ima); + MEM_SAFE_FREE(tgpf->ima); + break; + } + } + } + + /* finally, free memory used by temp data */ + MEM_freeN(tgpf); + } + + /* clear pointer */ + op->customdata = NULL; + + /* drawing batch cache is dirty now */ + if ((ob) && (ob->type == OB_GPENCIL) && (ob->data)) { + bGPdata *gpd2 = ob->data; + DEG_id_tag_update(&gpd2->id, OB_RECALC_OB | OB_RECALC_DATA); + gpd2->flag |= GP_DATA_CACHE_IS_DIRTY; + } + + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); +} + +static void gpencil_fill_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_fill_exit(C, op); +} + +/* Init: Allocate memory and set init values */ +static int gpencil_fill_init(bContext *C, wmOperator *op) +{ + tGPDfill *tgpf; + + /* check context */ + tgpf = op->customdata = gp_session_init_fill(C, op); + if (tgpf == NULL) { + /* something wasn't set correctly in context */ + gpencil_fill_exit(C, op); + return 0; + } + + /* everything is now setup ok */ + return 1; +} + +/* start of interactive part of operator */ +static int gpencil_fill_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + tGPDfill *tgpf = NULL; + + /* try to initialize context data needed */ + if (!gpencil_fill_init(C, op)) { + gpencil_fill_exit(C, op); + if (op->customdata) + MEM_freeN(op->customdata); + return OPERATOR_CANCELLED; + } + else { + tgpf = op->customdata; + } + + /* Enable custom drawing handlers to show help lines */ + if (tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) { + tgpf->draw_handle_3d = ED_region_draw_cb_activate(tgpf->ar->type, gpencil_fill_draw_3d, tgpf, REGION_DRAW_POST_VIEW); + } + + WM_cursor_modal_set(CTX_wm_window(C), BC_PAINTBRUSHCURSOR); + + gpencil_fill_status_indicators(C, tgpf); + + DEG_id_tag_update(&tgpf->gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator*/ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* events handling during interactive part of operator */ +static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPDfill *tgpf = op->customdata; + + int estate = OPERATOR_PASS_THROUGH; /* default exit state - pass through */ + + switch (event->type) { + case ESCKEY: + case RIGHTMOUSE: + estate = OPERATOR_CANCELLED; + break; + case LEFTMOUSE: + tgpf->on_back = RNA_boolean_get(op->ptr, "on_back"); + /* first time the event is not enabled to show help lines */ + if ((tgpf->oldkey != -1) || ((tgpf->flag & GP_BRUSH_FILL_SHOW_HELPLINES) == 0)) { + ARegion *ar = BKE_area_find_region_xy(CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y); + if (ar) { + bool in_bounds = false; + + /* Perform bounds check */ + in_bounds = BLI_rcti_isect_pt(&ar->winrct, event->x, event->y); + + if ((in_bounds) && (ar->regiontype == RGN_TYPE_WINDOW)) { + /* TODO GPXX: Verify the mouse click is right for any window size */ + tgpf->center[0] = event->mval[0]; + tgpf->center[1] = event->mval[1]; + + /* save size */ + tgpf->sizex = ar->winx; + tgpf->sizey = ar->winy; + + /* render screen to temp image */ + gp_render_offscreen(tgpf); + + /* apply boundary fill */ + gpencil_boundaryfill_area(tgpf); + + /* clean borders to avoid infinite loops */ + gpencil_clean_borders(tgpf); + + /* analyze outline */ + gpencil_get_outline_points(tgpf); + + /* create array of points from stack */ + gpencil_points_from_stack(tgpf); + + /* create z-depth array for reproject */ + gpencil_get_depth_array(tgpf); + + /* create stroke and reproject */ + gpencil_stroke_from_buffer(tgpf); + + /* free temp stack data */ + if (tgpf->stack) { + BLI_stack_free(tgpf->stack); + } + + /* push undo data */ + gpencil_undo_push(tgpf->gpd); + + estate = OPERATOR_FINISHED; + } + else { + estate = OPERATOR_CANCELLED; + } + } + else { + estate = OPERATOR_CANCELLED; + } + } + tgpf->oldkey = event->type; + break; + } + /* process last operations before exiting */ + switch (estate) { + case OPERATOR_FINISHED: + gpencil_fill_exit(C, op); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + break; + + case OPERATOR_CANCELLED: + gpencil_fill_exit(C, op); + break; + + case OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH: + break; + } + + /* return status code */ + return estate; +} + +void GPENCIL_OT_fill(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Grease Pencil Fill"; + ot->idname = "GPENCIL_OT_fill"; + ot->description = "Fill with color the shape formed by strokes"; + + /* api callbacks */ + ot->invoke = gpencil_fill_invoke; + ot->modal = gpencil_fill_modal; + ot->poll = gpencil_fill_poll; + ot->cancel = gpencil_fill_cancel; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + prop = RNA_def_boolean(ot->srna, "on_back", false, "Draw On Back", "Send new stroke to Back"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index 90ff1e0bb25..52e7d1e1f30 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -34,25 +34,145 @@ #include "DNA_vec_types.h" +#include "ED_numinput.h" + /* internal exports only */ struct bGPdata; struct bGPDstroke; struct bGPDspoint; +struct tGPspoint; +struct Material; struct GHash; struct RNG; +struct Brush; +struct Scene; struct ARegion; +struct View3D; struct View2D; struct wmOperatorType; +struct Depsgraph; + struct PointerRNA; struct PropertyRNA; struct EnumPropertyItem; /* ***************************************************** */ +/* Modal Operator Geometry Preview + * + * Several modal operators (Fill, Interpolate, Primitive) + * need to run some drawing code to display previews, or + * to perform screen-space/image-based analysis routines. + * The following structs + function prototypes are used + * by these operators so that the operator code + * (in gpencil_<opname>.c) can communicate with the drawing + * code (in drawgpencil.c). + * + * NOTE: All this is within the gpencil module, so nothing needs + * to be exported to other modules. + */ + +/* Internal Operator-State Data ------------------------ */ + +/* Temporary draw data (no draw manager mode) */ +typedef struct tGPDdraw { + struct RegionView3D *rv3d; /* region to draw */ + struct Depsgraph *depsgraph; /* depsgraph */ + struct Object *ob; /* GP object */ + struct bGPdata *gpd; /* current GP datablock */ + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *gpf; /* frame */ + struct bGPDframe *t_gpf; /* temporal frame */ + struct bGPDstroke *gps; /* stroke */ + int disable_fill; /* disable fill */ + int offsx; /* windows offset x */ + int offsy; /* windows offset y */ + int winx; /* windows width */ + int winy; /* windows height */ + int dflag; /* flags datablock */ + short lthick; /* layer thickness */ + float opacity; /* opacity */ + float tintcolor[4]; /* tint color */ + bool onion; /* onion flag */ + bool custonion; /* use custom onion colors */ + float diff_mat[4][4]; /* matrix */ +} tGPDdraw; + + +/* Temporary interpolate operation data */ +typedef struct tGPDinterpolate_layer { + struct tGPDinterpolate_layer *next, *prev; + + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *prevFrame; /* frame before current frame (interpolate-from) */ + struct bGPDframe *nextFrame; /* frame after current frame (interpolate-to) */ + struct bGPDframe *interFrame; /* interpolated frame */ + float factor; /* interpolate factor */ + +} tGPDinterpolate_layer; + +typedef struct tGPDinterpolate { + struct Scene *scene; /* current scene from context */ + struct ScrArea *sa; /* area where painting originated */ + struct ARegion *ar; /* region where painting originated */ + struct bGPdata *gpd; /* current GP datablock */ + struct Material *mat; /* current material */ + + int cframe; /* current frame number */ + ListBase ilayers; /* (tGPDinterpolate_layer) layers to be interpolated */ + float shift; /* value for determining the displacement influence */ + float init_factor; /* initial interpolation factor for active layer */ + float low_limit; /* shift low limit (-100%) */ + float high_limit; /* shift upper limit (200%) */ + int flag; /* flag from toolsettings */ + + NumInput num; /* numeric input */ + void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ + void *draw_handle_screen; /* handle for drawing strokes while operator is running screen stuff */ +} tGPDinterpolate; + + +/* Temporary primitive operation data */ +typedef struct tGPDprimitive { + struct Depsgraph *depsgraph; + struct wmWindow *win; /* window where painting originated */ + struct Scene *scene; /* current scene from context */ + struct Object *ob; /* current active gp object */ + struct ScrArea *sa; /* area where painting originated */ + struct RegionView3D *rv3d; /* region where painting originated */ + struct View3D *v3d; /* view3d where painting originated */ + struct ARegion *ar; /* region where painting originated */ + struct bGPdata *gpd; /* current GP datablock */ + struct Material *mat; /* current material */ + struct Brush *brush; /* current brush */ + + int cframe; /* current frame number */ + struct bGPDlayer *gpl; /* layer */ + struct bGPDframe *gpf; /* frame */ + int type; /* type of primitive */ + int tot_edges; /* number of polygon edges */ + int top[2]; /* first box corner */ + int bottom[2]; /* last box corner */ + int flag; /* flag to determine operations in progress */ + + int lock_axis; /* lock to viewport axis */ + + NumInput num; /* numeric input */ + void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ +} tGPDprimitive; + + +/* Modal Operator Drawing Callbacks ------------------------ */ + +void ED_gp_draw_interpolation(const struct bContext *C, struct tGPDinterpolate *tgpi, const int type); +void ED_gp_draw_primitives(const struct bContext *C, struct tGPDprimitive *tgpi, const int type); +void ED_gp_draw_fill(struct tGPDdraw *tgpw); + +/* ***************************************************** */ /* Internal API */ /* Stroke Coordinates API ------------------------------ */ @@ -84,21 +204,30 @@ void gp_point_to_xy_fl(GP_SpaceConversion *gsc, bGPDstroke *gps, bGPDspoint *pt, float *r_x, float *r_y); void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint *r_pt); - -void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps); - -void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt); +/** + * Change points position relative to parent object + */ +void gp_apply_parent(struct Depsgraph *depsgraph, struct Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps); +/** + * Change point position relative to parent object + */ +void gp_apply_parent_point(struct Depsgraph *depsgraph, struct Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDspoint *pt); bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, struct Scene *scene, const float screen_co[2], float r_out[3]); +/* helper to convert 2d to 3d */ +void gp_stroke_convertcoords_tpoint( + struct Scene *scene, struct ARegion *ar, + struct View3D *v3d, struct Object *ob, + bGPDlayer *gpl, const struct tGPspoint *point2D, + float *depth, float out[3]); + /* Poll Callbacks ------------------------------------ */ /* gpencil_utils.c */ bool gp_add_poll(struct bContext *C); bool gp_active_layer_poll(struct bContext *C); bool gp_active_brush_poll(struct bContext *C); -bool gp_active_palette_poll(struct bContext *C); -bool gp_active_palettecolor_poll(struct bContext *C); bool gp_brush_crt_presets_poll(bContext *C); /* Copy/Paste Buffer --------------------------------- */ @@ -107,18 +236,20 @@ bool gp_brush_crt_presets_poll(bContext *C); extern ListBase gp_strokes_copypastebuf; /* Build a map for converting between old colornames and destination-color-refs */ -struct GHash *gp_copybuf_validate_colormap(bGPdata *gpd); +struct GHash *gp_copybuf_validate_colormap(struct bContext *C); /* Stroke Editing ------------------------------------ */ -void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, int tag_flags); - +void gp_stroke_delete_tagged_points( + bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke, + int tag_flags, bool select); +int gp_delete_selected_point_wrap(bContext *C); bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure); bool gp_smooth_stroke_strength(bGPDstroke *gps, int i, float inf); bool gp_smooth_stroke_thickness(bGPDstroke *gps, int i, float inf); -void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints); -void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, struct RNG *rng); +void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide); +void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, struct RNG *rng); /* Layers Enums -------------------------------------- */ @@ -129,22 +260,18 @@ const 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 */ -const EnumPropertyItem *ED_gpencil_brushes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free); - -/* Enums of GP palettes */ -const EnumPropertyItem *ED_gpencil_palettes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free); - /* ***************************************************** */ /* Operator Defines */ +/* annotations ------ */ + +void GPENCIL_OT_annotate(struct wmOperatorType *ot); + + /* drawing ---------- */ void GPENCIL_OT_draw(struct wmOperatorType *ot); +void GPENCIL_OT_fill(struct wmOperatorType *ot); /* Paint Modes for operator */ typedef enum eGPencil_PaintModes { @@ -160,6 +287,9 @@ typedef enum eGPencil_PaintModes { /* stroke editing ----- */ void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_paintmode_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_sculptmode_toggle(struct wmOperatorType *ot); +void GPENCIL_OT_weightmode_toggle(struct wmOperatorType *ot); void GPENCIL_OT_selection_opacity_toggle(struct wmOperatorType *ot); void GPENCIL_OT_select(struct wmOperatorType *ot); @@ -174,6 +304,7 @@ void GPENCIL_OT_select_more(struct wmOperatorType *ot); void GPENCIL_OT_select_less(struct wmOperatorType *ot); void GPENCIL_OT_select_first(struct wmOperatorType *ot); void GPENCIL_OT_select_last(struct wmOperatorType *ot); +void GPENCIL_OT_select_alternate(struct wmOperatorType *ot); void GPENCIL_OT_duplicate(struct wmOperatorType *ot); void GPENCIL_OT_delete(struct wmOperatorType *ot); @@ -218,6 +349,8 @@ void GPENCIL_OT_blank_frame_add(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_frame_duplicate(struct wmOperatorType *ot); +void GPENCIL_OT_frame_clean_fill(struct wmOperatorType *ot); void GPENCIL_OT_convert(struct wmOperatorType *ot); @@ -226,6 +359,13 @@ enum { GP_STROKE_JOINCOPY = 1 }; +enum { + GP_STROKE_BOX = -1, + GP_STROKE_LINE = 1, + GP_STROKE_CIRCLE = 2 +}; + + 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); @@ -234,30 +374,15 @@ 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_stroke_subdivide(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_simplify(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_simplify_fixed(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_separate(struct wmOperatorType *ot); +void GPENCIL_OT_stroke_split(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); +void GPENCIL_OT_sculpt_select(struct wmOperatorType *ot); /* undo stack ---------- */ @@ -271,6 +396,30 @@ void GPENCIL_OT_interpolate(struct wmOperatorType *ot); void GPENCIL_OT_interpolate_sequence(struct wmOperatorType *ot); void GPENCIL_OT_interpolate_reverse(struct wmOperatorType *ot); +/* primitives ---------- */ + +void GPENCIL_OT_primitive(struct wmOperatorType *ot); + +/* vertex groups ------------ */ +void GPENCIL_OT_vertex_group_assign(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_remove_from(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_select(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_deselect(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_invert(struct wmOperatorType *ot); +void GPENCIL_OT_vertex_group_smooth(struct wmOperatorType *ot); + +/* color handle */ +void GPENCIL_OT_lock_layer(struct wmOperatorType *ot); +void GPENCIL_OT_color_isolate(struct wmOperatorType *ot); +void GPENCIL_OT_color_hide(struct wmOperatorType *ot); +void GPENCIL_OT_color_reveal(struct wmOperatorType *ot); +void GPENCIL_OT_color_lock_all(struct wmOperatorType *ot); +void GPENCIL_OT_color_unlock_all(struct wmOperatorType *ot); +void GPENCIL_OT_color_select(struct wmOperatorType *ot); + +/* convert old 2.7 files to 2.8 */ +void GPENCIL_OT_convert_old_files(struct wmOperatorType *ot); + /* ****************************************************** */ /* FILTERED ACTION DATA - TYPES ---> XXX DEPRECEATED OLD ANIM SYSTEM CODE! */ @@ -331,24 +480,37 @@ typedef enum ACTCONT_TYPES { */ #define GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) \ { \ + Depsgraph *depsgraph_ = CTX_data_depsgraph(C); \ + Object *obact_ = CTX_data_active_object(C); \ + bGPdata *gpd_ = CTX_data_gpencil_data(C); \ + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_); \ 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 ... */ + bGPDframe *init_gpf = gpl->actframe; \ + if (is_multiedit) { \ + init_gpf = gpl->frames.first; \ + } \ + for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) { \ + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { \ + /* calculate difference matrix */ \ + float diff_mat[4][4]; \ + ED_gpencil_parent_location(depsgraph_, obact_, gpd_, gpl, diff_mat); \ + /* loop over strokes */ \ + for (bGPDstroke *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(obact_, gpl, gps) == false) \ + continue; \ + /* ... Do Stuff With Strokes ... */ #define GP_EDITABLE_STROKES_END \ + } \ + } \ + if (!is_multiedit) { \ + break; \ + } \ } \ } \ CTX_DATA_END; \ diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index cc30d7ec266..6541e9f012a 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -47,6 +47,7 @@ #include "DNA_color_types.h" #include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -60,6 +61,7 @@ #include "BKE_library.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_deform.h" #include "UI_interface.h" #include "UI_resources.h" @@ -79,6 +81,9 @@ #include "ED_view3d.h" #include "ED_space_api.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ************************************************ */ @@ -108,11 +113,13 @@ static bool gpencil_view3d_poll(bContext *C) static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_to, bGPDstroke *new_stroke, float factor) { bGPDspoint *prev, *pt, *next; + MDeformVert *dvert; /* update points */ for (int i = 0; i < new_stroke->totpoints; i++) { prev = &gps_from->points[i]; pt = &new_stroke->points[i]; + dvert = &new_stroke->dvert[i]; next = &gps_to->points[i]; /* Interpolate all values */ @@ -120,6 +127,9 @@ static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_t pt->pressure = interpf(prev->pressure, next->pressure, 1.0f - factor); pt->strength = interpf(prev->strength, next->strength, 1.0f - factor); CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); + + dvert->totweight = 0; + dvert->dw = NULL; } } @@ -128,6 +138,7 @@ static void gp_interpolate_update_points(bGPDstroke *gps_from, bGPDstroke *gps_t /* Helper: Update all strokes interpolated */ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) { + bGPdata *gpd = tgpi->gpd; tGPDinterpolate_layer *tgpil; const float shift = tgpi->shift; @@ -156,12 +167,14 @@ static void gp_interpolate_update_strokes(bContext *C, tGPDinterpolate *tgpi) } } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } /* Helper: Verify valid strokes for interpolation */ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) { + Object *ob = CTX_data_active_object(C); ToolSettings *ts = CTX_data_tool_settings(C); eGP_Interpolate_SettingsFlag flag = ts->gp_interpolate.flag; @@ -190,7 +203,7 @@ static bool gp_interpolate_check_todo(bContext *C, bGPdata *gpd) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { continue; } @@ -213,6 +226,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) bGPdata *gpd = tgpi->gpd; bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); bGPDframe *actframe = active_gpl->actframe; + Object *ob = CTX_data_active_object(C); /* save initial factor for active layer to define shift limits */ tgpi->init_factor = (float)(tgpi->cframe - actframe->framenum) / (actframe->next->framenum - actframe->framenum + 1); @@ -255,7 +269,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) bGPDstroke *gps_to; int fFrame; - bGPDstroke *new_stroke; + bGPDstroke *new_stroke = NULL; bool valid = true; @@ -269,7 +283,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(tgpil->gpl, gps_from) == false) { + if (ED_gpencil_stroke_color_use(ob, tgpil->gpl, gps_from) == false) { valid = false; } @@ -281,16 +295,13 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) } /* create new stroke */ - new_stroke = MEM_dupallocN(gps_from); - new_stroke->points = MEM_dupallocN(gps_from->points); - new_stroke->triangles = MEM_dupallocN(gps_from->triangles); - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); if (valid) { /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert) * gps_to->totpoints); new_stroke->totpoints = gps_to->totpoints; new_stroke->tot_triangles = 0; new_stroke->flag |= GP_STROKE_RECALC_CACHES; @@ -302,6 +313,7 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) /* need an empty stroke to keep index correct for lookup, but resize to smallest size */ new_stroke->totpoints = 0; new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points)); + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert)); new_stroke->tot_triangles = 0; new_stroke->triangles = MEM_recallocN(new_stroke->triangles, sizeof(*new_stroke->triangles)); new_stroke->flag |= GP_STROKE_RECALC_CACHES; @@ -317,17 +329,17 @@ static void gp_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) /* Drawing Callbacks */ /* Drawing callback for modal operator in screen mode */ -static void gpencil_interpolate_draw_screen(const struct bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +static void gpencil_interpolate_draw_screen(const struct bContext *C, ARegion *UNUSED(ar), void *arg) { tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_PIXEL); + ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_PIXEL); } /* Drawing callback for modal operator in 3d mode */ -static void gpencil_interpolate_draw_3d(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) +static void gpencil_interpolate_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) { tGPDinterpolate *tgpi = (tGPDinterpolate *)arg; - ED_gp_draw_interpolation(tgpi, REGION_DRAW_POST_VIEW); + ED_gp_draw_interpolation(C, tgpi, REGION_DRAW_POST_VIEW); } /* ----------------------- */ @@ -392,6 +404,7 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) { tGPDinterpolate *tgpi = op->customdata; tGPDinterpolate_layer *tgpil; + bGPdata *gpd = tgpi->gpd; /* don't assume that operator data exists at all */ if (tgpi) { @@ -416,6 +429,7 @@ static void gpencil_interpolate_exit(bContext *C, wmOperator *op) BLI_freelistN(&tgpi->ilayers); MEM_freeN(tgpi); } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); /* clear pointer */ @@ -483,9 +497,10 @@ static int gpencil_interpolate_init(bContext *C, wmOperator *op) static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { wmWindow *win = CTX_wm_window(C); - Scene *scene = CTX_data_scene(C); bGPdata *gpd = CTX_data_gpencil_data(C); bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); bGPDframe *actframe = gpl->actframe; tGPDinterpolate *tgpi = NULL; @@ -496,7 +511,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent } /* cannot interpolate in extremes */ - if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); return OPERATOR_CANCELLED; } @@ -529,6 +544,7 @@ static int gpencil_interpolate_invoke(bContext *C, wmOperator *op, const wmEvent /* update shift indicator in header */ gpencil_interpolate_status_indicators(C, tgpi); + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); /* add a modal handler for this operator */ @@ -571,6 +587,8 @@ static int gpencil_interpolate_modal(bContext *C, wmOperator *op, const wmEvent /* make copy of source stroke, then adjust pointer to points too */ gps_dst = MEM_dupallocN(gps_src); gps_dst->points = MEM_dupallocN(gps_src->points); + gps_dst->dvert = MEM_dupallocN(gps_src->dvert); + BKE_gpencil_stroke_weights_duplicate(gps_src, gps_dst); gps_dst->triangles = MEM_dupallocN(gps_src->triangles); gps_dst->flag |= GP_STROKE_RECALC_CACHES; BLI_addtail(&gpf_dst->strokes, gps_dst); @@ -902,8 +920,11 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) bGPDlayer *active_gpl = CTX_data_active_gpencil_layer(C); bGPDframe *actframe = active_gpl->actframe; - Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + GP_Interpolate_Settings *ipo_settings = &ts->gp_interpolate; eGP_Interpolate_SettingsFlag flag = ipo_settings->flag; @@ -913,7 +934,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } /* cannot interpolate in extremes */ - if (ELEM(CFRA, actframe->framenum, actframe->next->framenum)) { + if (ELEM(cfra_eval, actframe->framenum, actframe->next->framenum)) { BKE_report(op->reports, RPT_ERROR, "Cannot interpolate as current frame already has existing grease pencil frames"); return OPERATOR_CANCELLED; } @@ -961,7 +982,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) /* create new strokes data with interpolated points reading original stroke */ for (gps_from = prevFrame->strokes.first; gps_from; gps_from = gps_from->next) { - bGPDstroke *new_stroke; + bGPDstroke *new_stroke = NULL; /* only selected */ if ((flag & GP_TOOLFLAG_INTERPOLATE_ONLY_SELECTED) && ((gps_from->flag & GP_STROKE_SELECT) == 0)) { @@ -972,7 +993,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) continue; } /* check if the color is editable */ - if (ED_gpencil_stroke_color_use(gpl, gps_from) == false) { + if (ED_gpencil_stroke_color_use(ob, gpl, gps_from) == false) { continue; } @@ -990,15 +1011,15 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* create new stroke */ - new_stroke = MEM_dupallocN(gps_from); - new_stroke->points = MEM_dupallocN(gps_from->points); - new_stroke->triangles = MEM_dupallocN(gps_from->triangles); - new_stroke->tot_triangles = 0; - new_stroke->flag |= GP_STROKE_RECALC_CACHES; + new_stroke = BKE_gpencil_stroke_duplicate(gps_from); /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { + /* free weights of removed points */ + BKE_defvert_array_free_elems(gps_from->dvert + gps_to->totpoints, gps_from->totpoints - gps_to->totpoints); + new_stroke->points = MEM_recallocN(new_stroke->points, sizeof(*new_stroke->points) * gps_to->totpoints); + new_stroke->dvert = MEM_recallocN(new_stroke->dvert, sizeof(*new_stroke->dvert) * gps_to->totpoints); new_stroke->totpoints = gps_to->totpoints; new_stroke->tot_triangles = 0; new_stroke->flag |= GP_STROKE_RECALC_CACHES; @@ -1014,6 +1035,7 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) } /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; @@ -1055,6 +1077,8 @@ static bool gpencil_interpolate_reverse_poll(bContext *C) static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* Go through each layer, deleting the breakdowns around the current frame, * but only if there is a keyframe nearby to stop at */ @@ -1123,6 +1147,7 @@ static int gpencil_interpolate_reverse_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* notifiers */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/gpencil/gpencil_old.c b/source/blender/editors/gpencil/gpencil_old.c new file mode 100644 index 00000000000..b7af1c80d4c --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_old.c @@ -0,0 +1,219 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018, Blender Foundation, + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + * Use deprecated data to convert old 2.7x files + */ + +/** \file blender/editors/gpencil/gpencil_old.c + * \ingroup edgpencil + */ + + /* allow to use deprecated functionality */ +#define DNA_DEPRECATED_ALLOW + +#include <stdio.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_object.h" +#include "BKE_material.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "ED_object.h" +#include "ED_gpencil.h" + +#include "gpencil_intern.h" + + /* Free all of a gp-colors */ +static void free_gpencil_colors(bGPDpalette *palette) +{ + /* error checking */ + if (palette == NULL) { + return; + } + + /* free colors */ + BLI_freelistN(&palette->colors); +} + +/* Free all of the gp-palettes and colors */ +static void free_palettes(ListBase *list) +{ + bGPDpalette *palette_next; + + /* error checking */ + if (list == NULL) { + return; + } + + /* delete palettes */ + for (bGPDpalette *palette = list->first; palette; palette = palette_next) { + palette_next = palette->next; + /* free palette colors */ + free_gpencil_colors(palette); + + MEM_freeN(palette); + } + BLI_listbase_clear(list); +} + +/* ***************** Convert old 2.7 files to 2.8 ************************ */ +static bool gpencil_convert_old_files_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + return (int) (scene->gpd != NULL); +} + +static int gpencil_convert_old_files_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + /* Convert grease pencil scene datablock to GP object */ + if ((scene->gpd) && (view_layer != NULL)) { + Object *ob; + ob = BKE_object_add_for_data(bmain, view_layer, OB_GPENCIL, "GP_Scene", &scene->gpd->id, false); + zero_v3(ob->loc); + + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + } + + /* convert grease pencil palettes (version >= 2.78) to materials and weights */ + bGPdata *gpd = scene->gpd; + for (const bGPDpalette *palette = gpd->palettes.first; palette; palette = palette->next) { + for (bGPDpalettecolor *palcolor = palette->colors.first; palcolor; palcolor = palcolor->next) { + + /* create material slot */ + BKE_object_material_slot_add(bmain, ob); + Material *ma = BKE_material_add_gpencil(bmain, palcolor->info); + assign_material(bmain, ob, ma, ob->totcol, BKE_MAT_ASSIGN_EXISTING); + + /* copy color settings */ + MaterialGPencilStyle *gp_style = ma->gp_style; + copy_v4_v4(gp_style->stroke_rgba, palcolor->color); + copy_v4_v4(gp_style->fill_rgba, palcolor->fill); + gp_style->flag = palcolor->flag; + + /* fix strokes */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + if ((gps->colorname[0] != '\0') && + (STREQ(gps->colorname, palcolor->info))) + { + gps->mat_nr = ob->totcol - 1; + gps->colorname[0] = '\0'; + /* create weights array */ + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); + } + } + } + } + } + } + + /* free palettes */ + free_palettes(&gpd->palettes); + + /* disable all GP modes */ + ED_gpencil_setup_modes(C, gpd, 0); + + /* set cache as dirty */ + BKE_gpencil_batch_cache_dirty(ob->data); + + scene->gpd = NULL; + } + +#if 0 /* GPXX */ + /* Handle object-linked grease pencil datablocks */ + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + if (ob->gpd) { + if (ob->type == OB_GPENCIL) { + /* GP Object - remap the links */ + ob->data = ob->gpd; + ob->gpd = NULL; + } + else if (ob->type == OB_EMPTY) { + /* Empty with GP data - This should be able to be converted + * to a GP object with little data loss + */ + ob->data = ob->gpd; + ob->gpd = NULL; + ob->type = OB_GPENCIL; + } + else { + /* FIXME: What to do in this case? + * + * We cannot create new objects for these, as we don't have a scene & scene layer + * to put them into from here... + */ + printf("WARNING: Old Grease Pencil data ('%s') still exists on Object '%s'\n", + ob->gpd->id.name + 2, ob->id.name + 2); + } + } + } +#endif + + /* notifiers */ + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL); + + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_convert_old_files(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Convert 2.7 Grease Pencil File"; + ot->idname = "GPENCIL_OT_convert_old_files"; + ot->description = "Convert 2.7x grease pencil files to 2.8"; + + /* callbacks */ + ot->exec = gpencil_convert_old_files_exec; + ot->poll = gpencil_convert_old_files_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c index 3f114a4dd4a..3a86b0c13ef 100644 --- a/source/blender/editors/gpencil/gpencil_ops.c +++ b/source/blender/editors/gpencil/gpencil_ops.c @@ -35,8 +35,14 @@ #include "BLI_sys_types.h" #include "BKE_context.h" +#include "BKE_brush.h" +#include "BKE_gpencil.h" +#include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "WM_api.h" #include "WM_types.h" @@ -52,7 +58,7 @@ /* ****************************************** */ /* Grease Pencil Keymaps */ -/* Generic Drawing Keymap */ +/* Generic Drawing Keymap - Annotations */ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) { wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil", 0, 0); @@ -60,36 +66,22 @@ static void ed_keymap_gpencil_general(wmKeyConfig *keyconf) /* Draw --------------------------------------- */ /* draw */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, DKEY); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", LEFTMOUSE, KM_PRESS, 0, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); RNA_boolean_set(kmi->ptr, "wait_for_input", false); /* draw - straight lines */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, DKEY); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", LEFTMOUSE, KM_PRESS, KM_ALT, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_STRAIGHT); RNA_boolean_set(kmi->ptr, "wait_for_input", false); /* draw - poly lines */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", RIGHTMOUSE, KM_PRESS, KM_CTRL, DKEY); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", LEFTMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_POLY); RNA_boolean_set(kmi->ptr, "wait_for_input", false); /* erase */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", RIGHTMOUSE, KM_PRESS, 0, DKEY); - RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - - /* Tablet Mappings for Drawing ------------------ */ - /* For now, only support direct drawing using the eraser, as most users using a tablet - * may still want to use that as their primary pointing device! - */ -#if 0 - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_STYLUS, KM_PRESS, 0, 0); - RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); -#endif - - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_annotate", RIGHTMOUSE, KM_PRESS, 0, DKEY); RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); RNA_boolean_set(kmi->ptr, "wait_for_input", false); @@ -121,67 +113,85 @@ static bool gp_stroke_editmode_poll(bContext *C) return (gpd && (gpd->flag & GP_DATA_STROKE_EDITMODE)); } -/* Stroke Editing Keymap - Only when editmode is enabled */ -static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) +/* Poll callback for stroke painting mode */ +static bool gp_stroke_paintmode_poll(bContext *C) { - wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0); - wmKeyMapItem *kmi; - - /* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */ - keymap->poll = gp_stroke_editmode_poll; - - /* ----------------------------------------------- */ - - /* Exit EditMode */ - WM_keymap_add_item(keymap, "GPENCIL_OT_editmode_toggle", TABKEY, KM_PRESS, 0, 0); - - /* Pie Menu - For settings/tools easy access */ - WM_keymap_add_menu_pie(keymap, "GPENCIL_MT_pie_sculpt", EKEY, KM_PRESS, 0, DKEY); - - /* Brush Settings */ - /* NOTE: We cannot expose these in the standard keymap, as they will interfere with regular hotkeys - * in other modes. However, when we are dealing with Stroke Edit Mode, we know for certain - * that the only data being edited is that of the Grease Pencil strokes - */ - - /* 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"); - - /* Interpolation */ - WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate", EKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); - WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate_sequence", EKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE)); +} - /* Sculpting ------------------------------------- */ +/* Poll callback for stroke painting (draw brush) */ +static bool gp_stroke_paintmode_draw_poll(bContext *C) +{ + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && (brush) && + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)); +} - /* Brush-Based Editing: - * EKEY + LMB = Single stroke, draw immediately - * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. - * - * For the modal version, use D+E -> Sculpt - */ - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); +/* Poll callback for stroke painting (erase brush) */ +static bool gp_stroke_paintmode_erase_poll(bContext *C) +{ + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && (brush) && + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)); +} - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ +/* Poll callback for stroke painting (fill) */ +static bool gp_stroke_paintmode_fill_poll(bContext *C) +{ + /* TODO: limit this to mode, but review 2D editors */ + bGPdata *gpd = CTX_data_gpencil_data(C); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + return (gpd && (gpd->flag & GP_DATA_STROKE_PAINTMODE) && (brush) && + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_FILL)); +} - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, EKEY); - RNA_boolean_set(kmi->ptr, "wait_for_input", false); - /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ +/* Poll callback for stroke sculpting mode */ +static bool gp_stroke_sculptmode_poll(bContext *C) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + Object *ob = CTX_data_active_object(C); + ScrArea *sa = CTX_wm_area(C); + + /* if not gpencil object and not view3d, need sculpt keys if edit mode */ + if (sa->spacetype != SPACE_VIEW3D) { + return ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + } + else { + /* weight paint is a submode of sculpt */ + if ((ob) && (ob->type == OB_GPENCIL)) { + return GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd); + } + } + + return 0; +} +/* Poll callback for stroke weight paint mode */ +static bool gp_stroke_weightmode_poll(bContext *C) +{ + bGPdata *gpd = CTX_data_gpencil_data(C); + Object *ob = CTX_data_active_object(C); - /* Shift-FKEY = Sculpt Strength */ - 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"); + if ((ob) && (ob->type == OB_GPENCIL)) { + return (gpd && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)); + } - /* 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"); + return 0; +} +static void ed_keymap_gpencil_selection(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; - /* Selection ------------------------------------- */ /* select all */ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_all", AKEY, KM_PRESS, 0, 0); RNA_enum_set(kmi->ptr, "action", SEL_SELECT); @@ -204,17 +214,14 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) RNA_boolean_set(kmi->ptr, "deselect", true); /* In the Node Editor, lasso select needs ALT modifier too (as somehow CTRL+LMB drag gets taken for "cut" quite early) - * There probably isn't too much harm adding this for other editors too as part of standard GP editing keymap. This hotkey - * combo doesn't seem to see much use under standard scenarios? - */ + * There probably isn't too much harm adding this for other editors too as part of standard GP editing keymap. This hotkey + * combo doesn't seem to see much use under standard scenarios? + */ kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); RNA_boolean_set(kmi->ptr, "deselect", false); kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_SHIFT | KM_CTRL | KM_ALT, 0); RNA_boolean_set(kmi->ptr, "deselect", true); - /* normal select */ - WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); - kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "extend", true); RNA_boolean_set(kmi->ptr, "toggle", true); @@ -223,17 +230,130 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "entire_strokes", true); + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, KM_ALT | KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "entire_strokes", true); + RNA_boolean_set(kmi->ptr, "extend", true); + /* select linked */ /* NOTE: While LKEY is redundant, not having it breaks the mode illusion too much */ WM_keymap_add_item(keymap, "GPENCIL_OT_select_linked", LKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_select_linked", LKEY, KM_PRESS, KM_CTRL, 0); + /* select alternate */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select_alternate", LKEY, KM_PRESS, KM_SHIFT, 0); + /* select grouped */ WM_keymap_add_item(keymap, "GPENCIL_OT_select_grouped", GKEY, KM_PRESS, KM_SHIFT, 0); /* select more/less */ WM_keymap_add_item(keymap, "GPENCIL_OT_select_more", PADPLUSKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "GPENCIL_OT_select_less", PADMINUS, KM_PRESS, KM_CTRL, 0); +} + +static void ed_keymap_gpencil_display(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, KM_SHIFT, 0); + RNA_string_set(kmi->ptr, "data_path", "space_data.overlay.use_gpencil_edit_lines"); + + kmi = WM_keymap_add_item(keymap, "WM_OT_context_toggle", QKEY, KM_PRESS, KM_SHIFT | KM_ALT, 0); + RNA_string_set(kmi->ptr, "data_path", "space_data.overlay.use_gpencil_multiedit_line_only"); +} + +static void ed_keymap_gpencil_sculpt(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + + /* Pie Menu - For settings/tools easy access */ + WM_keymap_add_menu_pie(keymap, "GPENCIL_PIE_sculpt", EKEY, KM_PRESS, 0, DKEY); + + /* Sculpting ------------------------------------- */ + + /* Brush-Based Editing: + * EKEY + LMB = Single stroke, draw immediately + * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. + * + * For the modal version, use D+E -> Sculpt + */ + /* GPXX: disabled to make toolsystem works */ + //kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, 0); + //RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ + + /* Shift-FKEY = Sculpt Strength */ + 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"); + + /* 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"); + + /* menu sculpt specials */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_sculpt_specials", WKEY, KM_PRESS, 0, 0); +} + +static void ed_keymap_gpencil_weight(wmKeyMap *keymap) +{ + wmKeyMapItem *kmi; + + + /* Brush-Based Editing: + * EKEY + LMB = Single stroke, draw immediately + * + Other Modifiers (Ctrl/Shift) = Invert, Smooth, etc. + * + * For the modal version, use D+E -> Sculpt + */ + /* GPXX: disabled to make toolsystem works */ + //kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, 0, 0); + //RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_invert", true);*/ + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_brush_paint", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + RNA_boolean_set(kmi->ptr, "keep_brush", true); + /*RNA_boolean_set(kmi->ptr, "use_smooth", true);*/ +} + +/* Stroke Editing Keymap - Only when editmode is enabled */ +static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Edit Mode", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback - so that this keymap only gets enabled when stroke editmode is enabled */ + keymap->poll = gp_stroke_editmode_poll; + + /* ----------------------------------------------- */ + + /* Brush Settings */ + /* NOTE: We cannot expose these in the standard keymap, as they will interfere with regular hotkeys + * in other modes. However, when we are dealing with Stroke Edit Mode, we know for certain + * that the only data being edited is that of the Grease Pencil strokes + */ + + /* Interpolation */ + WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate", EKEY, KM_PRESS, KM_CTRL | KM_ALT, 0); + WM_keymap_add_item(keymap, "GPENCIL_OT_interpolate_sequence", EKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + + /* normal select */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select", SELECTMOUSE, KM_PRESS, 0, 0); + + /* Selection */ + ed_keymap_gpencil_selection(keymap); /* Editing ----------------------------------------- */ @@ -253,6 +373,12 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) /* menu edit specials */ WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_edit_specials", WKEY, KM_PRESS, 0, 0); + /* menu separate */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_separate", PKEY, KM_PRESS, 0, 0); + + /* split strokes */ + WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_split", VKEY, KM_PRESS, 0, 0); + /* join strokes */ WM_keymap_add_item(keymap, "GPENCIL_OT_stroke_join", JKEY, KM_PRESS, KM_CTRL, 0); @@ -271,7 +397,6 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) /* snap */ WM_keymap_add_menu(keymap, "GPENCIL_MT_snap", SKEY, KM_PRESS, KM_SHIFT, 0); - /* convert to geometry */ WM_keymap_add_item(keymap, "GPENCIL_OT_convert", CKEY, KM_PRESS, KM_ALT, 0); @@ -288,6 +413,9 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "GPENCIL_OT_selection_opacity_toggle", HKEY, KM_PRESS, KM_CTRL, 0); + /* Display. */ + ed_keymap_gpencil_display(keymap); + /* Isolate Layer */ WM_keymap_add_item(keymap, "GPENCIL_OT_layer_isolate", PADASTERKEY, KM_PRESS, 0, 0); @@ -317,27 +445,233 @@ static void ed_keymap_gpencil_editing(wmKeyConfig *keyconf) /* Proportional Editing */ ED_keymap_proportional_cycle(keyconf, keymap); ED_keymap_proportional_editmode(keyconf, keymap, true); + + /* menu - add GP object (3d view only) */ + WM_keymap_add_item(keymap, "OBJECT_OT_gpencil_add", AKEY, KM_PRESS, KM_SHIFT, 0); + + /* menu vertex group */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_vertex_group", GKEY, KM_PRESS, KM_CTRL, 0); + + /* toggle edit mode */ + WM_keymap_add_item(keymap, "GPENCIL_OT_editmode_toggle", TABKEY, KM_PRESS, 0, 0); +} + +/* keys for draw with a drawing brush (no fill) */ +static void ed_keymap_gpencil_painting_draw(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback */ + keymap->poll = gp_stroke_paintmode_draw_poll; + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* draw - straight lines */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_ALT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_STRAIGHT); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* draw - poly lines */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_SHIFT | KM_ALT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW_POLY); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* erase */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* Tablet Mappings for Drawing ------------------ */ + /* For now, only support direct drawing using the eraser, as most users using a tablet + * may still want to use that as their primary pointing device! + */ +#if 0 + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_STYLUS, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); +#endif + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* Selection (used by eraser) */ + /* border select */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select_border", BKEY, KM_PRESS, 0, 0); + + /* lasso select */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", false); +} + +/* keys for draw with a eraser brush (erase) */ +static void ed_keymap_gpencil_painting_erase(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Erase)", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback */ + keymap->poll = gp_stroke_paintmode_erase_poll; + + /* erase */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", TABLET_ERASER, KM_PRESS, 0, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_ERASER); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + + /* Selection (used by eraser) */ + /* border select */ + WM_keymap_add_item(keymap, "GPENCIL_OT_select_border", BKEY, KM_PRESS, 0, 0); + + /* lasso select */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_lasso", EVT_TWEAK_A, KM_ANY, KM_CTRL | KM_ALT, 0); + RNA_boolean_set(kmi->ptr, "deselect", false); +} + +/* keys for draw with a fill brush */ +static void ed_keymap_gpencil_painting_fill(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint (Fill)", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback */ + keymap->poll = gp_stroke_paintmode_fill_poll; + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_fill", LEFTMOUSE, KM_PRESS, 0, 0); + RNA_boolean_set(kmi->ptr, "on_back", false); + + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_fill", LEFTMOUSE, KM_PRESS, KM_SHIFT, 0); + RNA_boolean_set(kmi->ptr, "on_back", true); + + /* if press alternative key, the brush now it's for drawing areas */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_CTRL, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + /* disable straight lines */ + RNA_boolean_set(kmi->ptr, "disable_straight", true); + + /* if press alternative key, the brush now it's for drawing lines */ + kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_draw", LEFTMOUSE, KM_PRESS, KM_ALT, 0); + RNA_enum_set(kmi->ptr, "mode", GP_PAINTMODE_DRAW); + RNA_boolean_set(kmi->ptr, "wait_for_input", false); + /* disable straight lines */ + RNA_boolean_set(kmi->ptr, "disable_straight", true); + /* enable special stroke with no fill flag */ + RNA_boolean_set(kmi->ptr, "disable_fill", true); +} + +/* Stroke Painting Keymap - Only when paintmode is enabled */ +static void ed_keymap_gpencil_painting(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Paint Mode", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback - so that this keymap only gets enabled when stroke paintmode is enabled */ + keymap->poll = gp_stroke_paintmode_poll; + + /* FKEY = 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_paint.brush.size"); + + /* 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"); + + /* menu draw specials */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_draw_specials", WKEY, KM_PRESS, 0, 0); + + /* menu draw delete */ + WM_keymap_add_menu(keymap, "GPENCIL_MT_gpencil_draw_delete", XKEY, KM_PRESS, 0, 0); + } +/* Stroke Sculpting Keymap - Only when sculptmode is enabled */ +static void ed_keymap_gpencil_sculpting(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Sculpt Mode", 0, 0); + + /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ + keymap->poll = gp_stroke_sculptmode_poll; + + /* Selection */ + ed_keymap_gpencil_selection(keymap); + + /* sculpt */ + ed_keymap_gpencil_sculpt(keymap); + + /* Display. */ + ed_keymap_gpencil_display(keymap); +} + +/* Stroke Weight Paint Keymap - Only when weight is enabled */ +static void ed_keymap_gpencil_weightpainting(wmKeyConfig *keyconf) +{ + wmKeyMap *keymap = WM_keymap_find(keyconf, "Grease Pencil Stroke Weight Mode", 0, 0); + wmKeyMapItem *kmi; + + /* set poll callback - so that this keymap only gets enabled when stroke sculptmode is enabled */ + keymap->poll = gp_stroke_weightmode_poll; + + /* Selection */ + ed_keymap_gpencil_selection(keymap); + + /* sculpt */ + ed_keymap_gpencil_weight(keymap); + + /* Shift-FKEY = Sculpt Strength */ + 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.weight_brush.strength"); + + /* 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.weight_brush.size"); + + /* Display. */ + ed_keymap_gpencil_display(keymap); +} /* ==================== */ void ED_keymap_gpencil(wmKeyConfig *keyconf) { ed_keymap_gpencil_general(keyconf); ed_keymap_gpencil_editing(keyconf); + ed_keymap_gpencil_painting(keyconf); + ed_keymap_gpencil_painting_draw(keyconf); + ed_keymap_gpencil_painting_erase(keyconf); + ed_keymap_gpencil_painting_fill(keyconf); + ed_keymap_gpencil_sculpting(keyconf); + ed_keymap_gpencil_weightpainting(keyconf); } /* ****************************************** */ void ED_operatortypes_gpencil(void) { + /* Annotations -------------------- */ + + WM_operatortype_append(GPENCIL_OT_annotate); + /* Drawing ----------------------- */ WM_operatortype_append(GPENCIL_OT_draw); + WM_operatortype_append(GPENCIL_OT_fill); /* Editing (Strokes) ------------ */ WM_operatortype_append(GPENCIL_OT_editmode_toggle); + WM_operatortype_append(GPENCIL_OT_paintmode_toggle); + WM_operatortype_append(GPENCIL_OT_sculptmode_toggle); + WM_operatortype_append(GPENCIL_OT_weightmode_toggle); WM_operatortype_append(GPENCIL_OT_selection_opacity_toggle); WM_operatortype_append(GPENCIL_OT_select); @@ -352,6 +686,7 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_select_less); WM_operatortype_append(GPENCIL_OT_select_first); WM_operatortype_append(GPENCIL_OT_select_last); + WM_operatortype_append(GPENCIL_OT_select_alternate); WM_operatortype_append(GPENCIL_OT_duplicate); WM_operatortype_append(GPENCIL_OT_delete); @@ -391,6 +726,8 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_active_frame_delete); WM_operatortype_append(GPENCIL_OT_active_frames_delete_all); + WM_operatortype_append(GPENCIL_OT_frame_duplicate); + WM_operatortype_append(GPENCIL_OT_frame_clean_fill); WM_operatortype_append(GPENCIL_OT_convert); @@ -402,36 +739,46 @@ void ED_operatortypes_gpencil(void) WM_operatortype_append(GPENCIL_OT_stroke_join); WM_operatortype_append(GPENCIL_OT_stroke_flip); WM_operatortype_append(GPENCIL_OT_stroke_subdivide); + WM_operatortype_append(GPENCIL_OT_stroke_simplify); + WM_operatortype_append(GPENCIL_OT_stroke_simplify_fixed); + WM_operatortype_append(GPENCIL_OT_stroke_separate); + WM_operatortype_append(GPENCIL_OT_stroke_split); - 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); + WM_operatortype_append(GPENCIL_OT_sculpt_select); + + /* vertex groups */ + WM_operatortype_append(GPENCIL_OT_vertex_group_assign); + WM_operatortype_append(GPENCIL_OT_vertex_group_remove_from); + WM_operatortype_append(GPENCIL_OT_vertex_group_select); + WM_operatortype_append(GPENCIL_OT_vertex_group_deselect); + WM_operatortype_append(GPENCIL_OT_vertex_group_invert); + WM_operatortype_append(GPENCIL_OT_vertex_group_smooth); + + /* color handle */ + WM_operatortype_append(GPENCIL_OT_lock_layer); + WM_operatortype_append(GPENCIL_OT_color_isolate); + WM_operatortype_append(GPENCIL_OT_color_hide); + WM_operatortype_append(GPENCIL_OT_color_reveal); + WM_operatortype_append(GPENCIL_OT_color_lock_all); + WM_operatortype_append(GPENCIL_OT_color_unlock_all); + WM_operatortype_append(GPENCIL_OT_color_select); + /* Editing (Time) --------------- */ /* Interpolation */ WM_operatortype_append(GPENCIL_OT_interpolate); WM_operatortype_append(GPENCIL_OT_interpolate_sequence); WM_operatortype_append(GPENCIL_OT_interpolate_reverse); + + /* Primitives */ + WM_operatortype_append(GPENCIL_OT_primitive); + + /* convert old 2.7 files to 2.8 */ + WM_operatortype_append(GPENCIL_OT_convert_old_files); + } void ED_operatormacros_gpencil(void) diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 789e9865ae4..0c411db57b4 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -46,26 +46,32 @@ #include "PIL_time.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_material_types.h" +#include "DNA_brush_types.h" +#include "DNA_windowmanager_types.h" + #include "BKE_colortools.h" +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_paint.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_gpencil.h" -#include "BKE_main.h" -#include "BKE_paint.h" #include "BKE_report.h" +#include "BKE_layer.h" +#include "BKE_material.h" #include "BKE_screen.h" #include "BKE_tracking.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" #include "ED_gpencil.h" #include "ED_screen.h" +#include "ED_object.h" #include "ED_view3d.h" #include "ED_clip.h" @@ -82,6 +88,7 @@ #include "WM_types.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "gpencil_intern.h" @@ -110,6 +117,8 @@ typedef enum eGPencil_PaintFlags { GP_PAINTFLAG_STROKEADDED = (1 << 1), GP_PAINTFLAG_V3D_ERASER_DEPTH = (1 << 2), GP_PAINTFLAG_SELECTMASK = (1 << 3), + GP_PAINTFLAG_HARD_ERASER = (1 << 4), + GP_PAINTFLAG_STROKE_ERASER = (1 << 5), } eGPencil_PaintFlags; @@ -117,10 +126,13 @@ typedef enum eGPencil_PaintFlags { * "p" = op->customdata */ typedef struct tGPsdata { - Main *bmain; + bContext *C; + + Main *bmain; /* main database pointer */ Scene *scene; /* current scene from context */ struct Depsgraph *depsgraph; + Object *ob; /* current object */ wmWindow *win; /* window where painting originated */ ScrArea *sa; /* area where painting originated */ ARegion *ar; /* region where painting originated */ @@ -165,14 +177,23 @@ typedef struct tGPsdata { void *erasercursor; /* radial cursor data for drawing eraser */ - bGPDpalettecolor *palettecolor; /* current palette color */ - bGPDbrush *brush; /* current drawing brush */ + /* mat settings are only used for 3D view */ + Material *material; /* current material */ + + Brush *brush; /* current drawing brush */ + Brush *eraser; /* default eraser brush */ short straight[2]; /* 1: line horizontal, 2: line vertical, other: not defined, second element position */ int lock_axis; /* lock drawing to one axis */ + bool disable_fill; /* the stroke is no fill mode */ RNG *rng; short keymodifier; /* key used for invoking the operator */ + short shift; /* shift modifier flag */ + + float totpixlen; /* size in pixels for uv calculation */ + + ReportList *reports; } tGPsdata; /* ------ */ @@ -183,6 +204,14 @@ typedef struct tGPsdata { /* minimum length of new segment before new point can be added */ #define MIN_EUCLIDEAN_PX (U.gp_euclideandist) +static void gp_update_cache(bGPdata *gpd) +{ + if (gpd) { + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + gpd->flag |= GP_DATA_CACHE_IS_DIRTY; + } +} + static bool gp_stroke_added_check(tGPsdata *p) { return (p->gpf && p->gpf->strokes.last && p->flags & GP_PAINTFLAG_STROKEADDED); @@ -192,6 +221,9 @@ static void gp_stroke_added_enable(tGPsdata *p) { BLI_assert(p->gpf->strokes.last != NULL); p->flags |= GP_PAINTFLAG_STROKEADDED; + + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); } /* ------ */ @@ -206,30 +238,42 @@ static void gp_session_validatebuffer(tGPsdata *p); static bool gpencil_draw_poll(bContext *C) { if (ED_operator_regionactive(C)) { - /* check if current context can support GPencil data */ - if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { - /* check if Grease Pencil isn't already running */ - if (ED_gpencil_session_active() == 0) - return 1; - else - CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); + ScrArea *sa = CTX_wm_area(C); + if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { + /* check if current context can support GPencil data */ + if (ED_gpencil_data_get_pointers(C, NULL) != NULL) { + /* check if Grease Pencil isn't already running */ + if (ED_gpencil_session_active() == 0) + return 1; + else + CTX_wm_operator_poll_msg_set(C, "Grease Pencil operator is already active"); + } + else { + CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + } + return 0; } + /* 3D Viewport */ else { - CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into"); + if (ED_gpencil_session_active() == 0) { + return 1; + } + else { + return 0; + } } } else { CTX_wm_operator_poll_msg_set(C, "Active region not set"); + return 0; } - - return 0; } /* check if projecting strokes into 3d-geometry in the 3D-View */ static bool gpencil_project_check(tGPsdata *p) { bGPdata *gpd = p->gpd; - return ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); + return ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) && (*p->align_flag & (GP_PROJECT_DEPTH_VIEW | GP_PROJECT_DEPTH_STROKE))); } /* ******************************************* */ @@ -241,38 +285,39 @@ static bool gpencil_project_check(tGPsdata *p) static void gp_get_3d_reference(tGPsdata *p, float vec[3]) { View3D *v3d = p->sa->spacedata.first; - const float *fp = ED_view3d_cursor3d_get(p->scene, v3d)->location; - - /* the reference point used depends on the owner... */ -#if 0 /* XXX: disabled for now, since we can't draw relative to the owner yet */ + Object *ob = NULL; if (p->ownerPtr.type == &RNA_Object) { - Object *ob = (Object *)p->ownerPtr.data; - - /* active Object - * - use relative distance of 3D-cursor from object center - */ - sub_v3_v3v3(vec, fp, ob->loc); - } - else -#endif - { - /* use 3D-cursor */ - copy_v3_v3(vec, fp); + ob = (Object *)p->ownerPtr.data; } + ED_gp_get_drawing_reference(v3d, p->scene, ob, p->gpl, *p->align_flag, vec); } /* Stroke Editing ---------------------------- */ - /* check if the current mouse position is suitable for adding a new point */ static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) { + Brush *brush = p->brush; int dx = abs(mval[0] - pmval[0]); int dy = abs(mval[1] - pmval[1]); + brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; /* if buffer is empty, just let this go through (i.e. so that dots will work) */ - if (p->gpd->sbuffer_size == 0) + if (p->gpd->runtime.sbuffer_size == 0) { return true; - + } + /* if lazy mouse, check minimum distance */ + else if (GPENCIL_LAZY_MODE(brush, p->shift)) { + brush->gpencil_settings->flag |= GP_BRUSH_STABILIZE_MOUSE_TEMP; + if ((dx * dx + dy * dy) > (brush->smooth_stroke_radius * brush->smooth_stroke_radius)) { + return true; + } + else { + /* If the mouse is moving within the radius of the last move, + * don't update the mouse position. This allows sharp turns. */ + copy_v2_v2_int(p->mval, p->mvalo); + return false; + } + } /* check if mouse moved at least certain distance on both axes (best case) * - aims to eliminate some jitter-noise from input when trying to draw straight lines freehand */ @@ -291,47 +336,17 @@ static bool gp_stroke_filtermval(tGPsdata *p, const int mval[2], int pmval[2]) return false; } -/* reproject the points of the stroke to a plane locked to axis to avoid stroke offset */ -static void gp_project_points_to_plane(RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) -{ - float plane_normal[3]; - float vn[3]; - - float ray[3]; - float rpoint[3]; - - /* normal vector for a plane locked to axis */ - zero_v3(plane_normal); - plane_normal[axis] = 1.0f; - - /* Reproject the points in the plane */ - for (int i = 0; i < gps->totpoints; i++) { - bGPDspoint *pt = &gps->points[i]; - - /* get a vector from the point with the current view direction of the viewport */ - ED_view3d_global_to_vector(rv3d, &pt->x, vn); - - /* calculate line extrem point to create a ray that cross the plane */ - mul_v3_fl(vn, -50.0f); - add_v3_v3v3(ray, &pt->x, vn); - - /* if the line never intersect, the point is not changed */ - if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { - copy_v3_v3(&pt->x, rpoint); - } - } -} - /* reproject stroke to plane locked to axis in 3d cursor location */ static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) { bGPdata *gpd = p->gpd; + Object *obact = (Object *)p->ownerPtr.data; + float origin[3]; - float cursor[3]; RegionView3D *rv3d = p->ar->regiondata; /* verify the stroke mode is CURSOR 3d space mode */ - if ((gpd->sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { + if ((gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) == 0) { return; } if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { @@ -341,12 +356,9 @@ static void gp_reproject_toplane(tGPsdata *p, bGPDstroke *gps) return; } - /* get 3d cursor and set origin for locked axis only. Uses axis-1 because the enum for XYZ start with 1 */ - gp_get_3d_reference(p, cursor); - zero_v3(origin); - origin[p->lock_axis - 1] = cursor[p->lock_axis - 1]; - - gp_project_points_to_plane(rv3d, gps, origin, p->lock_axis - 1); + /* get drawing origin */ + gp_get_3d_reference(p, origin); + ED_gp_project_stroke_to_plane(obact, rv3d, gps, origin, p->lock_axis - 1); } /* convert screen-coordinates to buffer-coordinates */ @@ -356,7 +368,7 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] bGPdata *gpd = p->gpd; /* in 3d-space - pt->x/y/z are 3 side-by-side floats */ - if (gpd->sbuffer_sflag & GP_STROKE_3DSPACE) { + if (gpd->runtime.sbuffer_sflag & GP_STROKE_3DSPACE) { if (gpencil_project_check(p) && (ED_view3d_autodist_simple(p->ar, mval, out, 0, depth))) { /* projecting onto 3D-Geometry * - nothing more needs to be done here, since view_autodist_simple() has already done it @@ -365,7 +377,8 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] else { float mval_prj[2]; float rvec[3], dvec[3]; - float mval_f[2] = {UNPACK2(mval)}; + float mval_f[2]; + copy_v2fl_v2i(mval_f, mval); float zfac; /* Current method just converts each point in screen-coordinates to @@ -390,42 +403,23 @@ static void gp_stroke_convertcoords(tGPsdata *p, const int mval[2], float out[3] } } } - - /* 2d - on 'canvas' (assume that p->v2d is set) */ - else if ((gpd->sbuffer_sflag & GP_STROKE_2DSPACE) && (p->v2d)) { - UI_view2d_region_to_view(p->v2d, mval[0], mval[1], &out[0], &out[1]); - mul_v3_m4v3(out, p->imat, out); - } - - /* 2d - relative to screen (viewport area) */ - else { - if (p->subrect == NULL) { /* normal 3D view */ - out[0] = (float)(mval[0]) / (float)(p->ar->winx) * 100; - out[1] = (float)(mval[1]) / (float)(p->ar->winy) * 100; - } - else { /* camera view, use subrect */ - out[0] = ((mval[0] - p->subrect->xmin) / BLI_rctf_size_x(p->subrect)) * 100; - out[1] = ((mval[1] - p->subrect->ymin) / BLI_rctf_size_y(p->subrect)) * 100; - } - } } /* apply jitter to stroke */ -static void gp_brush_jitter( - bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const int mval[2], int r_mval[2], RNG *rng) +static void gp_brush_jitter(bGPdata *gpd, Brush *brush, tGPspoint *pt, const int mval[2], int r_mval[2], RNG *rng) { 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; + if (brush->gpencil_settings->draw_jitter > 0.0f) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_jitter, 0, pressure); + tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; } - const float exfactor = (brush->draw_jitter + 2.0f) * (brush->draw_jitter + 2.0f); /* exponential value */ + const float exfactor = (brush->gpencil_settings->draw_jitter + 2.0f) * (brush->gpencil_settings->draw_jitter + 2.0f); /* exponential value */ const float fac = BLI_rng_get_float(rng) * 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) { + if (gpd->runtime.sbuffer_size > 1) { mvec[0] = (float)(mval[0] - (pt - 1)->x); mvec[1] = (float)(mval[1] - (pt - 1)->y); normalize_v2(mvec); @@ -451,18 +445,18 @@ static void gp_brush_jitter( } /* 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]) +static void gp_brush_angle(bGPdata *gpd, Brush *brush, tGPspoint *pt, const int mval[2]) { float mvec[2]; - float sen = brush->draw_angle_factor; /* sensitivity */; + float sen = brush->gpencil_settings->draw_angle_factor; /* sensitivity */; float fac; float mpressure; - float angle = brush->draw_angle; /* default angle of brush in radians */; + float angle = brush->gpencil_settings->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) { + if (gpd->runtime.sbuffer_size == 1) { mvec[0] = (float)(mval[0] - (pt - 1)->x); mvec[1] = (float)(mval[1] - (pt - 1)->y); normalize_v2(mvec); @@ -475,7 +469,7 @@ static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const } /* apply from second point */ - if (gpd->sbuffer_size >= 1) { + if (gpd->runtime.sbuffer_size >= 1) { mvec[0] = (float)(mval[0] - (pt - 1)->x); mvec[1] = (float)(mval[1] - (pt - 1)->y); normalize_v2(mvec); @@ -490,21 +484,83 @@ static void gp_brush_angle(bGPdata *gpd, bGPDbrush *brush, tGPspoint *pt, const } +/* Apply smooth to buffer while drawing +* to smooth point C, use 2 before (A, B) and current point (D): +* +* A----B-----C------D +* +* \param p Temp data +* \param inf Influence factor +* \param idx Index of the last point (need minimum 3 points in the array) +*/ +static void gp_smooth_buffer(tGPsdata *p, float inf, int idx) +{ + bGPdata *gpd = p->gpd; + short num_points = gpd->runtime.sbuffer_size; + + /* Do nothing if not enough points to smooth out */ + if ((num_points < 3) || (idx < 3) || (inf == 0.0f)) { + return; + } + + tGPspoint *points = (tGPspoint *)gpd->runtime.sbuffer; + float steps = 4.0f; + if (idx < 4) { + steps--; + } + + tGPspoint *pta = idx >= 4 ? &points[idx - 4] : NULL; + tGPspoint *ptb = idx >= 3 ? &points[idx - 3] : NULL; + tGPspoint *ptc = idx >= 2 ? &points[idx - 2] : NULL; + tGPspoint *ptd = &points[idx - 1]; + + float sco[2] = { 0.0f }; + float a[2], b[2], c[2], d[2]; + const float average_fac = 1.0f / steps; + + /* Compute smoothed coordinate by taking the ones nearby */ + if (pta) { + copy_v2fl_v2i(a, &pta->x); + madd_v2_v2fl(sco, a, average_fac); + } + if (ptb) { + copy_v2fl_v2i(b, &ptb->x); + madd_v2_v2fl(sco, b, average_fac); + } + if (ptc) { + copy_v2fl_v2i(c, &ptc->x); + madd_v2_v2fl(sco, c, average_fac); + } + if (ptd) { + copy_v2fl_v2i(d, &ptd->x); + madd_v2_v2fl(sco, d, average_fac); + } + + /* Based on influence factor, blend between original and optimal smoothed coordinate */ + interp_v2_v2v2(c, c, sco, inf); + round_v2i_v2fl(&ptc->x, c); +} + /* 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; + Brush *brush = p->brush; tGPspoint *pt; ToolSettings *ts = p->scene->toolsettings; + Object *obact = (Object *)p->ownerPtr.data; + Depsgraph *depsgraph = p->depsgraph; \ + RegionView3D *rv3d = p->ar->regiondata; + View3D *v3d = p->sa->spacedata.first; + MaterialGPencilStyle *gp_style = p->material->gp_style; /* check painting mode */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { /* straight lines only - i.e. only store start and end point in buffer */ - if (gpd->sbuffer_size == 0) { + if (gpd->runtime.sbuffer_size == 0) { /* first point in buffer (start point) */ - pt = (tGPspoint *)(gpd->sbuffer); + pt = (tGPspoint *)(gpd->runtime.sbuffer); /* store settings */ copy_v2_v2_int(&pt->x, mval); @@ -513,13 +569,13 @@ static short gp_stroke_addpoint( pt->time = (float)(curtime - p->inittime); /* increment buffer size */ - gpd->sbuffer_size++; + gpd->runtime.sbuffer_size++; } else { /* just reset the endpoint to the latest value * - assume that pointers for this are always valid... */ - pt = ((tGPspoint *)(gpd->sbuffer) + 1); + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + 1); /* store settings */ copy_v2_v2_int(&pt->x, mval); @@ -528,31 +584,35 @@ static short gp_stroke_addpoint( pt->time = (float)(curtime - p->inittime); /* now the buffer has 2 points (and shouldn't be allowed to get any larger) */ - gpd->sbuffer_size = 2; + gpd->runtime.sbuffer_size = 2; } + /* tag depsgraph to update object */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + /* can keep carrying on this way :) */ return GP_STROKEADD_NORMAL; } else if (p->paintmode == GP_PAINTMODE_DRAW) { /* normal drawing */ /* check if still room in buffer */ - if (gpd->sbuffer_size >= GP_STROKE_BUFFER_MAX) + if (gpd->runtime.sbuffer_size >= GP_STROKE_BUFFER_MAX) return GP_STROKEADD_OVERFLOW; /* get pointer to destination point */ - pt = ((tGPspoint *)(gpd->sbuffer) + gpd->sbuffer_size); + pt = ((tGPspoint *)(gpd->runtime.sbuffer) + gpd->runtime.sbuffer_size); /* store settings */ /* pressure */ - if (brush->flag & GP_BRUSH_USE_PRESSURE) { - float curvef = curvemapping_evaluateF(brush->cur_sensitivity, 0, pressure); - pt->pressure = curvef * brush->draw_sensitivity; + if (brush->gpencil_settings->flag & GP_BRUSH_USE_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_sensitivity, 0, pressure); + pt->pressure = curvef * brush->gpencil_settings->draw_sensitivity; } else { pt->pressure = 1.0f; } + /* Apply jitter to position */ - if (brush->draw_jitter > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush->gpencil_settings->draw_jitter > 0.0f)) { int r_mval[2]; gp_brush_jitter(gpd, brush, pt, mval, r_mval, p->rng); copy_v2_v2_int(&pt->x, r_mval); @@ -561,42 +621,62 @@ static short gp_stroke_addpoint( 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 ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_press > 0.0f)) + { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_sensitivity, 0, pressure); + float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->pressure -= tmp_pressure * brush->draw_random_press * BLI_rng_get_float(p->rng); + pt->pressure -= tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); } else { - pt->pressure += tmp_pressure * brush->draw_random_press * BLI_rng_get_float(p->rng); + pt->pressure += tmp_pressure * brush->gpencil_settings->draw_random_press * BLI_rng_get_float(p->rng); } CLAMP(pt->pressure, GPENCIL_STRENGTH_MIN, 1.0f); } + /* apply randomness to uv texture rotation */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && (brush->gpencil_settings->uv_random > 0.0f)) { + if (BLI_rng_get_float(p->rng) > 0.5f) { + pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI * -1) * brush->gpencil_settings->uv_random; + } + else { + pt->uv_rot = (BLI_rng_get_float(p->rng) * M_PI) * brush->gpencil_settings->uv_random; + } + CLAMP(pt->uv_rot, -M_PI_2, M_PI_2); + } + else { + pt->uv_rot = 0.0f; + } + /* apply angle of stroke to brush size */ - if (brush->draw_angle_factor > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->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; + if (brush->gpencil_settings->flag & GP_BRUSH_USE_STENGTH_PRESSURE) { + float curvef = curvemapping_evaluateF(brush->gpencil_settings->curve_strength, 0, pressure); + float tmp_pressure = curvef * brush->gpencil_settings->draw_sensitivity; - pt->strength = tmp_pressure * brush->draw_strength; + pt->strength = tmp_pressure * brush->gpencil_settings->draw_strength; } else { - pt->strength = brush->draw_strength; + pt->strength = brush->gpencil_settings->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 ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_strength > 0.0f)) + { if (BLI_rng_get_float(p->rng) > 0.5f) { - pt->strength -= pt->strength * brush->draw_random_press * BLI_rng_get_float(p->rng); + pt->strength -= pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); } else { - pt->strength += pt->strength * brush->draw_random_press * BLI_rng_get_float(p->rng); + pt->strength += pt->strength * brush->gpencil_settings->draw_random_strength * BLI_rng_get_float(p->rng); } CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); } @@ -604,11 +684,49 @@ static short gp_stroke_addpoint( /* point time */ pt->time = (float)(curtime - p->inittime); + /* point uv (only 3d view) */ + if ((p->sa->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_size > 1)) { + float pixsize = gp_style->texture_pixsize / 1000000.0f; + tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 2; + bGPDspoint spt, spt2; + + /* get origin to reproject point */ + float origin[3]; + gp_get_3d_reference(p, origin); + /* reproject current */ + ED_gpencil_tpoint_to_point(p->ar, origin, pt, &spt); + ED_gp_project_point_to_plane(obact, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &spt); + + /* reproject previous */ + ED_gpencil_tpoint_to_point(p->ar, origin, ptb, &spt2); + ED_gp_project_point_to_plane(obact, rv3d, origin, ts->gp_sculpt.lock_axis - 1, &spt2); + + p->totpixlen += len_v3v3(&spt.x, &spt2.x) / pixsize; + pt->uv_fac = p->totpixlen; + if ((gp_style) && (gp_style->sima)) { + pt->uv_fac /= gp_style->sima->gen_x; + } + } + else { + p->totpixlen = 0.0f; + pt->uv_fac = 0.0f; + } + /* increment counters */ - gpd->sbuffer_size++; + gpd->runtime.sbuffer_size++; + + /* smooth while drawing previous points with a reduction factor for previous */ + if (brush->gpencil_settings->active_smooth > 0.0f) { + for (int s = 0; s < 3; s++) { + gp_smooth_buffer(p, brush->gpencil_settings->active_smooth * ((3.0f - s) / 3.0f), gpd->runtime.sbuffer_size - s); + } + } + + /* tag depsgraph to update object */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); /* check if another operation can still occur */ - if (gpd->sbuffer_size == GP_STROKE_BUFFER_MAX) + if (gpd->runtime.sbuffer_size == GP_STROKE_BUFFER_MAX) return GP_STROKEADD_FULL; else return GP_STROKEADD_NORMAL; @@ -617,7 +735,7 @@ static short gp_stroke_addpoint( bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); /* get pointer to destination point */ - pt = (tGPspoint *)(gpd->sbuffer); + pt = (tGPspoint *)(gpd->runtime.sbuffer); /* store settings */ copy_v2_v2_int(&pt->x, mval); @@ -632,14 +750,17 @@ static short gp_stroke_addpoint( if (gp_stroke_added_check(p)) { bGPDstroke *gps = p->gpf->strokes.last; bGPDspoint *pts; + MDeformVert *dvert; /* first time point is adding to temporary buffer -- need to allocate new point in stroke */ - if (gpd->sbuffer_size == 0) { + if (gpd->runtime.sbuffer_size == 0) { gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1)); + gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (gps->totpoints + 1)); gps->totpoints++; } pts = &gps->points[gps->totpoints - 1]; + dvert = &gps->dvert[gps->totpoints - 1]; /* special case for poly lines: normally, * depth is needed only when creating new stroke from buffer, @@ -647,8 +768,6 @@ static short gp_stroke_addpoint( * so initialize depth buffer before converting coordinates */ if (gpencil_project_check(p)) { - View3D *v3d = p->sa->spacedata.first; - view3d_region_operator_needs_opengl(p->win, p->ar); ED_view3d_autodist_init( p->depsgraph, p->ar, v3d, (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? 1 : 0); @@ -656,25 +775,32 @@ static short gp_stroke_addpoint( /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &pt->x, &pts->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent_point(gpl, pts); - } + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pts); /* copy pressure and time */ pts->pressure = pt->pressure; pts->strength = pt->strength; pts->time = pt->time; + pts->uv_fac = pt->uv_fac; + pts->uv_rot = pt->uv_rot; + + dvert->totweight = 0; + dvert->dw = NULL; + /* force fill recalc */ gps->flag |= GP_STROKE_RECALC_CACHES; + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); } /* increment counters */ - if (gpd->sbuffer_size == 0) - gpd->sbuffer_size++; + if (gpd->runtime.sbuffer_size == 0) + gpd->runtime.sbuffer_size++; + + /* tag depsgraph to update object */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); return GP_STROKEADD_NORMAL; } @@ -690,9 +816,9 @@ static short gp_stroke_addpoint( static void gp_stroke_simplify(tGPsdata *p) { bGPdata *gpd = p->gpd; - tGPspoint *old_points = (tGPspoint *)gpd->sbuffer; - short num_points = gpd->sbuffer_size; - short flag = gpd->sbuffer_sflag; + tGPspoint *old_points = (tGPspoint *)gpd->runtime.sbuffer; + short num_points = gpd->runtime.sbuffer_size; + short flag = gpd->runtime.sbuffer_sflag; short i, j; /* only simplify if simplification is enabled, and we're not doing a straight line */ @@ -707,9 +833,9 @@ static void gp_stroke_simplify(tGPsdata *p) * - firstly set sbuffer to NULL, so a new one is allocated * - secondly, reset flag after, as it gets cleared auto */ - gpd->sbuffer = NULL; + gpd->runtime.sbuffer = NULL; gp_session_validatebuffer(p); - gpd->sbuffer_sflag = flag; + gpd->runtime.sbuffer_sflag = flag; /* macro used in loop to get position of new point * - used due to the mixture of datatypes in use here @@ -766,8 +892,11 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) bGPDstroke *gps; bGPDspoint *pt; tGPspoint *ptc; - bGPDbrush *brush = p->brush; + MDeformVert *dvert; + Brush *brush = p->brush; ToolSettings *ts = p->scene->toolsettings; + Depsgraph *depsgraph = p->depsgraph; + Object *obact = (Object *)p->ownerPtr.data; int i, totelem; /* since strokes are so fine, when using their depth we need a margin otherwise they might get missed */ @@ -777,14 +906,14 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) * - drawing straight-lines only requires the endpoints */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) - totelem = (gpd->sbuffer_size >= 2) ? 2 : gpd->sbuffer_size; + totelem = (gpd->runtime.sbuffer_size >= 2) ? 2 : gpd->runtime.sbuffer_size; else - totelem = gpd->sbuffer_size; + totelem = gpd->runtime.sbuffer_size; /* exit with error if no valid points from this stroke */ if (totelem == 0) { if (G.debug & G_DEBUG) - printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->sbuffer_size); + printf("Error: No valid points in stroke buffer to convert (tot=%d)\n", gpd->runtime.sbuffer_size); return; } @@ -793,6 +922,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) * interactive behavior */ if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { + /* be sure to hide any lazy cursor */ + ED_gpencil_toggle_brush_cursor(p->C, true, NULL); + if (gp_stroke_added_check(p)) { return; } @@ -803,95 +935,94 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) /* copy appropriate settings for stroke */ gps->totpoints = totelem; - gps->thickness = brush->thickness; - gps->flag = gpd->sbuffer_sflag; + gps->thickness = brush->size; + gps->flag = gpd->runtime.sbuffer_sflag; gps->inittime = p->inittime; /* enable recalculation flag by default (only used if hq fill) */ gps->flag |= GP_STROKE_RECALC_CACHES; /* allocate enough memory for a continuous array for storage points */ - int sublevel = brush->sublevel; - int new_totpoints = gps->totpoints; + const int subdivide = brush->gpencil_settings->draw_subdivide; + + gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); - for (i = 0; i < sublevel; i++) { - new_totpoints += new_totpoints - 1; - } - gps->points = MEM_callocN(sizeof(bGPDspoint) * new_totpoints, "gp_stroke_points"); /* initialize triangle memory to dummy data */ gps->triangles = MEM_callocN(sizeof(bGPDtriangle), "GP Stroke triangulation"); gps->flag |= GP_STROKE_RECALC_CACHES; gps->tot_triangles = 0; + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); /* set pointer to first non-initialized point */ pt = gps->points + (gps->totpoints - totelem); + dvert = gps->dvert + (gps->totpoints - totelem); /* copy points from the buffer to the stroke */ if (p->paintmode == GP_PAINTMODE_DRAW_STRAIGHT) { /* straight lines only -> only endpoints */ { /* first point */ - ptc = gpd->sbuffer; + ptc = gpd->runtime.sbuffer; /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } - /* 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; + dvert->totweight = 0; + dvert->dw = NULL; + pt++; + dvert++; } if (totelem == 2) { /* last point if applicable */ - ptc = ((tGPspoint *)gpd->sbuffer) + (gpd->sbuffer_size - 1); + ptc = ((tGPspoint *)gpd->runtime.sbuffer) + (gpd->runtime.sbuffer_size - 1); /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } - /* 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; + + dvert->totweight = 0; + dvert->dw = NULL; + } + + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); + pt = gps->points; + for (i = 0; i < gps->totpoints; i++, pt++) { + /* if parented change position relative to parent object */ + gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt); } } else if (p->paintmode == GP_PAINTMODE_DRAW_POLY) { /* first point */ - ptc = gpd->sbuffer; + ptc = gpd->runtime.sbuffer; /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, NULL); - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent_point(gpl, pt); - } + gp_apply_parent_point(depsgraph, obact, gpd, 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; + + dvert->totweight = 0; + dvert->dw = NULL; + } else { float *depth_arr = NULL; @@ -902,9 +1033,9 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) int interp_depth = 0; int found_depth = 0; - depth_arr = MEM_mallocN(sizeof(float) * gpd->sbuffer_size, "depth_points"); + depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_size, "depth_points"); - for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size; i++, ptc++, pt++) { + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size; i++, ptc++, pt++) { copy_v2_v2_int(mval, &ptc->x); if ((ED_view3d_autodist_depth(p->ar, mval, depth_margin, depth_arr + i) == 0) && @@ -921,7 +1052,7 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) if (found_depth == false) { /* eeh... not much we can do.. :/, ignore depth in this case, use the 3D cursor */ - for (i = gpd->sbuffer_size - 1; i >= 0; i--) + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) depth_arr[i] = 0.9999f; } else { @@ -930,13 +1061,13 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) int first_valid = 0; int last_valid = 0; - for (i = 0; i < gpd->sbuffer_size; i++) { + for (i = 0; i < gpd->runtime.sbuffer_size; i++) { if (depth_arr[i] != FLT_MAX) break; } first_valid = i; - for (i = gpd->sbuffer_size - 1; i >= 0; i--) { + for (i = gpd->runtime.sbuffer_size - 1; i >= 0; i--) { if (depth_arr[i] != FLT_MAX) break; } @@ -950,16 +1081,15 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) } if (interp_depth) { - interp_sparse_array(depth_arr, gpd->sbuffer_size, FLT_MAX); + interp_sparse_array(depth_arr, gpd->runtime.sbuffer_size, FLT_MAX); } } } - pt = gps->points; /* convert all points (normal behavior) */ - for (i = 0, ptc = gpd->sbuffer; i < gpd->sbuffer_size && ptc; i++, ptc++, pt++) { + for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_size && ptc; i++, ptc++, pt++) { /* convert screen-coordinates to appropriate coordinates (and store them) */ gp_stroke_convertcoords(p, &ptc->x, &pt->x, depth_arr ? depth_arr + i : NULL); @@ -968,20 +1098,18 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) pt->strength = ptc->strength; CLAMP(pt->strength, GPENCIL_STRENGTH_MIN, 1.0f); pt->time = ptc->time; + pt->uv_fac = ptc->uv_fac; + pt->uv_rot = ptc->uv_rot; } - /* subdivide the stroke */ - if (sublevel > 0) { - int totpoints = gps->totpoints; - for (i = 0; i < sublevel; i++) { - /* we're adding one new point between each pair of verts on each step */ - totpoints += totpoints - 1; - - gp_subdivide_stroke(gps, totpoints); - } + /* subdivide and smooth the stroke */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && (subdivide > 0)) { + gp_subdivide_stroke(gps, subdivide); } /* apply randomness to stroke */ - if (brush->draw_random_sub > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_RANDOM) && + (brush->gpencil_settings->draw_random_sub > 0.0f)) + { gp_randomize_stroke(gps, brush, p->rng); } @@ -989,34 +1117,43 @@ static void gp_stroke_newfrombuffer(tGPsdata *p) * for each iteration, the factor is reduced to get a better smoothing without changing too much * the original stroke */ - if (brush->draw_smoothfac > 0.0f) { + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && + (brush->gpencil_settings->draw_smoothfac > 0.0f)) + { float reduce = 0.0f; - for (int r = 0; r < brush->draw_smoothlvl; ++r) { + for (int r = 0; r < brush->gpencil_settings->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, brush->draw_smoothfac - reduce, false); + BKE_gpencil_smooth_stroke(gps, i, brush->gpencil_settings->draw_smoothfac - reduce); + BKE_gpencil_smooth_stroke_strength(gps, i, brush->gpencil_settings->draw_smoothfac); } reduce += 0.25f; // reduce the factor } } - - /* if axis locked, reproject to plane locked (only in 3d space) */ - if (p->lock_axis > GP_LOCKAXIS_NONE) { - gp_reproject_toplane(p, gps); - } - /* if parented change position relative to parent object */ - if (gpl->parent != NULL) { - gp_apply_parent(gpl, gps); + /* smooth thickness */ + if ((brush->gpencil_settings->flag & GP_BRUSH_GROUP_SETTINGS) && + (brush->gpencil_settings->thick_smoothfac > 0.0f)) + { + for (int r = 0; r < brush->gpencil_settings->thick_smoothlvl * 2; r++) { + for (i = 0; i < gps->totpoints; i++) { + BKE_gpencil_smooth_stroke_thickness(gps, i, brush->gpencil_settings->thick_smoothfac); + } + } } + /* reproject to plane (only in 3d space) */ + gp_reproject_toplane(p, gps); + /* change position relative to parent object */ + gp_apply_parent(depsgraph, obact, gpd, gpl, gps); + if (depth_arr) MEM_freeN(depth_arr); } - /* Save palette color */ - bGPDpalette *palette = BKE_gpencil_palette_getactive(p->gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - gps->palcolor = palcolor; - BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); + + /* Save material index */ + gps->mat_nr = BKE_object_material_slot_find_index(p->ob, p->material) - 1; + + /* calculate UVs along the stroke */ + ED_gpencil_calc_stroke_uv(obact, gps); /* add stroke to frame, usually on tail of the listbase, but if on back is enabled the stroke is added on listbase head * because the drawing order is inverse and the head stroke is the first to draw. This is very useful for artist @@ -1047,6 +1184,8 @@ static float view3d_point_depth(const RegionView3D *rv3d, const float co[3]) /* only erase stroke points that are visible */ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, const int x, const int y) { + Object *obact = (Object *)p->ownerPtr.data; + if ((p->sa->spacetype == SPACE_VIEW3D) && (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH)) { @@ -1059,7 +1198,7 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p, const bGPDspoint *pt, cons float diff_mat[4][4]; /* calculate difference matrix if parent object */ - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(p->depsgraph, obact, p->gpd, 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); @@ -1092,6 +1231,24 @@ static float gp_stroke_eraser_calc_influence(tGPsdata *p, const int mval[2], con return fac; } +/* helper to free a stroke */ +static void gp_free_stroke(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps) +{ + if (gps->points) { + MEM_freeN(gps->points); + } + + if (gps->dvert) { + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + } + + if (gps->triangles) + MEM_freeN(gps->triangles); + BLI_freelinkN(&gpf->strokes, gps); + gp_update_cache(gpd); +} + /* eraser tool - evaluation per stroke */ /* TODO: this could really do with some optimization (KD-Tree/BVH?) */ static void gp_stroke_eraser_dostroke(tGPsdata *p, @@ -1099,46 +1256,58 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, const int mval[2], const int mvalo[2], const int radius, const rcti *rect) { + Depsgraph *depsgraph = p->depsgraph; + Object *obact = (Object *)p->ownerPtr.data; + Brush *eraser = p->eraser; bGPDspoint *pt1, *pt2; 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); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, p->gpd, gpl, diff_mat); if (gps->totpoints == 0) { /* just free stroke */ - if (gps->points) - MEM_freeN(gps->points); - if (gps->triangles) - MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + gp_free_stroke(p->gpd, gpf, gps); } 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)) { - 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]); + 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 */ + if (len_v2v2_int(mval, pc1) <= radius) { + /* free stroke */ + gp_free_stroke(p->gpd, gpf, gps); + } } + } + } + else if ((p->flags & GP_PAINTFLAG_STROKE_ERASER) || (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_STROKE)) { + for (i = 0; (i + 1) < gps->totpoints; i++) { + + /* only process if it hasn't been masked out... */ + if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) + continue; + + /* get points to work with */ + pt1 = gps->points + i; + bGPDspoint npt; + gp_point_to_parent_space(pt1, diff_mat, &npt); + gp_point_to_xy(&p->gsc, gps, &npt, &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 */ if (len_v2v2_int(mval, pc1) <= radius) { /* free stroke */ - // XXX: pressure sensitive eraser should apply here too? - MEM_freeN(gps->points); - if (gps->triangles) - MEM_freeN(gps->triangles); - BLI_freelinkN(&gpf->strokes, gps); + gp_free_stroke(p->gpd, gpf, gps); + return; } } } @@ -1181,18 +1350,12 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, if ((p->flags & GP_PAINTFLAG_SELECTMASK) && !(gps->points->flag & GP_SPOINT_SELECT)) continue; - 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]); + 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]); - } + 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])) || @@ -1215,11 +1378,15 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, pt2->pressure -= gp_stroke_eraser_calc_influence(p, mval, radius, pc2) * strength / 2.0f; /* 2) Tag any point with overly low influence for removal in the next pass */ - if (pt1->pressure < cull_thresh) { + if ((pt1->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) + { pt1->flag |= GP_SPOINT_TAG; do_cull = true; } - if (pt2->pressure < cull_thresh) { + if ((pt2->pressure < cull_thresh) || (p->flags & GP_PAINTFLAG_HARD_ERASER) || + (eraser->gpencil_settings->eraser_mode == GP_BRUSH_ERASER_HARD)) + { pt2->flag |= GP_SPOINT_TAG; do_cull = true; } @@ -1230,8 +1397,9 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p, /* Second Pass: Remove any points that are tagged */ if (do_cull) { - gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG); + gp_stroke_delete_tagged_points(gpf, gps, gps->next, GP_SPOINT_TAG, false); } + gp_update_cache(p->gpd); } } @@ -1275,7 +1443,7 @@ static void gp_stroke_doeraser(tGPsdata *p) 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) { + if (ED_gpencil_stroke_color_use(p->ob, gpl, gps) == false) { continue; } /* Not all strokes in the datablock may be valid in the current editor/context @@ -1295,107 +1463,178 @@ static void gp_stroke_doeraser(tGPsdata *p) static void gp_session_validatebuffer(tGPsdata *p) { bGPdata *gpd = p->gpd; + Brush *brush = p->brush; /* clear memory of buffer (or allocate it if starting a new session) */ - if (gpd->sbuffer) { + if (gpd->runtime.sbuffer) { /* printf("\t\tGP - reset sbuffer\n"); */ - memset(gpd->sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); + memset(gpd->runtime.sbuffer, 0, sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX); } else { /* printf("\t\tGP - allocate sbuffer\n"); */ - gpd->sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); + gpd->runtime.sbuffer = MEM_callocN(sizeof(tGPspoint) * GP_STROKE_BUFFER_MAX, "gp_session_strokebuffer"); } /* reset indices */ - gpd->sbuffer_size = 0; + gpd->runtime.sbuffer_size = 0; /* reset flags */ - gpd->sbuffer_sflag = 0; + gpd->runtime.sbuffer_sflag = 0; /* reset inittime */ p->inittime = 0.0; + + /* reset lazy */ + if (brush) { + brush->gpencil_settings->flag &= ~GP_BRUSH_STABILIZE_MOUSE_TEMP; + } } -/* create a new palette color */ -static bGPDpalettecolor *gp_create_new_color(bGPDpalette *palette) +/* helper to get default eraser and create one if no eraser brush */ +static Brush *gp_get_default_eraser(Main *bmain, ToolSettings *ts) { - bGPDpalettecolor *palcolor; + Brush *brush_dft = NULL; + Paint *paint = BKE_brush_get_gpencil_paint(ts); + Brush *brush_old = paint->brush; + for (Brush *brush = bmain->brush.first; brush; brush = brush->id.next) { + if ((brush->ob_mode == OB_MODE_GPENCIL_PAINT) && + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) + { + /* save first eraser to use later if no default */ + if (brush_dft == NULL) { + brush_dft = brush; + } + /* found default */ + if (brush->gpencil_settings->flag & GP_BRUSH_DEFAULT_ERASER) { + return brush; + } + } + } + /* if no default, but exist eraser brush, return this and set as default */ + if (brush_dft) { + brush_dft->gpencil_settings->flag |= GP_BRUSH_DEFAULT_ERASER; + return brush_dft; + } + /* create a new soft eraser brush */ + else { + brush_dft = BKE_brush_add_gpencil(bmain, ts, "Soft Eraser"); + brush_dft->size = 30.0f; + brush_dft->gpencil_settings->flag |= (GP_BRUSH_ENABLE_CURSOR | GP_BRUSH_DEFAULT_ERASER); + brush_dft->gpencil_settings->icon_id = GP_BRUSH_ICON_ERASE_SOFT; + brush_dft->gpencil_settings->brush_type = GP_BRUSH_TYPE_ERASE; + brush_dft->gpencil_settings->eraser_mode = GP_BRUSH_ERASER_SOFT; - palcolor = BKE_gpencil_palettecolor_addnew(palette, DATA_("Color"), true); + /* reset current brush */ + BKE_paint_brush_set(paint, brush_old); - return palcolor; + return brush_dft; + } } /* initialize a drawing brush */ -static void gp_init_drawing_brush(ToolSettings *ts, tGPsdata *p) +static void gp_init_drawing_brush(bContext *C, tGPsdata *p) { - bGPDbrush *brush; + Brush *brush; + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + Paint *paint = BKE_brush_get_gpencil_paint(ts); /* if not exist, create a new one */ - if (BLI_listbase_is_empty(&ts->gp_brushes)) { + if (paint->brush == NULL) { /* create new brushes */ - BKE_gpencil_brush_init_presets(ts); - brush = BKE_gpencil_brush_getactive(ts); + BKE_brush_gpencil_presets(C); + brush = BKE_brush_getactive_gpencil(ts); } else { /* Use the current */ - brush = BKE_gpencil_brush_getactive(ts); + brush = BKE_brush_getactive_gpencil(ts); } /* be sure curves are initializated */ - curvemapping_initialize(brush->cur_sensitivity); - curvemapping_initialize(brush->cur_strength); - curvemapping_initialize(brush->cur_jitter); + curvemapping_initialize(brush->gpencil_settings->curve_sensitivity); + curvemapping_initialize(brush->gpencil_settings->curve_strength); + curvemapping_initialize(brush->gpencil_settings->curve_jitter); /* asign to temp tGPsdata */ p->brush = brush; + if (brush->gpencil_settings->brush_type != GP_BRUSH_TYPE_ERASE) { + p->eraser = gp_get_default_eraser(p->bmain, ts); + } + else { + p->eraser = brush; + } + /* use radius of eraser */ + p->radius = (short)p->eraser->size; + + /* GPXX: Need this update to synchronize brush with draw manager. + * Maybe this update can be removed when the new tool system + * will be in place, but while, we need this to keep drawing working. + * + */ + DEG_id_tag_update(&scene->id, DEG_TAG_COPY_ON_WRITE); } -/* initialize a paint palette brush and a default color if not exist */ -static void gp_init_palette(tGPsdata *p) +/* initialize a paint brush and a default color if not exist */ +static void gp_init_colors(tGPsdata *p) { - bGPdata *gpd; - bGPDpalette *palette; - bGPDpalettecolor *palcolor; + bGPdata *gpd = p->gpd; + Brush *brush = p->brush; - gpd = p->gpd; + Material *ma = NULL; + MaterialGPencilStyle *gp_style = NULL; - /* if not exist, create a new palette */ - if (BLI_listbase_is_empty(&gpd->palettes)) { - /* create new palette */ - palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); - /* now create a default color */ - palcolor = gp_create_new_color(palette); + /* use brush material */ + ma = BKE_gpencil_get_material_from_brush(brush); + + /* if no brush defaults, get material and color info + * NOTE: Ensures that everything we need will exist... + */ + if ((ma == NULL) || (ma->gp_style == NULL)) { + BKE_gpencil_material_ensure(p->bmain, p->ob); + + /* assign always the first material to the brush */ + p->material = give_current_material(p->ob, 1); + brush->gpencil_settings->material = p->material; } else { - /* Use the current palette and color */ - palette = BKE_gpencil_palette_getactive(gpd); - /* the palette needs one color */ - if (BLI_listbase_is_empty(&palette->colors)) { - palcolor = gp_create_new_color(palette); - } - else { - palcolor = BKE_gpencil_palettecolor_getactive(palette); - } - /* in some situations can be null, so use first */ - if (palcolor == NULL) { - BKE_gpencil_palettecolor_setactive(palette, palette->colors.first); - palcolor = palette->colors.first; - } + p->material = ma; } - /* asign to temp tGPsdata */ - p->palettecolor = palcolor; + /* check if the material is already on object material slots and add it if missing */ + if (BKE_object_material_slot_find_index(p->ob, p->material) == 0) { + BKE_object_material_slot_add(p->bmain, p->ob); + assign_material(p->bmain, p->ob, ma, p->ob->totcol, BKE_MAT_ASSIGN_EXISTING); + } + + /* assign color information to temp tGPsdata */ + gp_style = p->material->gp_style; + if (gp_style) { + + /* set colors */ + copy_v4_v4(gpd->runtime.scolor, gp_style->stroke_rgba); + copy_v4_v4(gpd->runtime.sfill, gp_style->fill_rgba); + /* add some alpha to make easy the filling without hide strokes */ + if (gpd->runtime.sfill[3] > 0.8f) { + gpd->runtime.sfill[3] = 0.8f; + } + + gpd->runtime.mode = (short)gp_style->mode; + gpd->runtime.bstroke_style = gp_style->stroke_style; + gpd->runtime.bfill_style = gp_style->fill_style; + } } /* (re)init new painting data */ -static bool gp_session_initdata(bContext *C, tGPsdata *p) +static bool gp_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) { Main *bmain = CTX_data_main(C); bGPdata **gpd_ptr = NULL; ScrArea *curarea = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); ToolSettings *ts = CTX_data_tool_settings(C); + Object *obact = CTX_data_active_object(C); + View3D *v3d = curarea->spacedata.first; /* make sure the active view (at the starting time) is a 3d-view */ if (curarea == NULL) { @@ -1406,10 +1645,12 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) } /* pass on current scene and window */ + p->C = C; p->bmain = CTX_data_main(C); p->scene = CTX_data_scene(C); p->depsgraph = CTX_data_depsgraph(C); p->win = CTX_wm_window(C); + p->disable_fill = RNA_boolean_get(op->ptr, "disable_fill"); unit_m4(p->imat); unit_m4(p->mat); @@ -1424,7 +1665,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* set current area * - must verify that region data is 3D-view (and not something else) */ - /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ p->sa = curarea; p->ar = ar; p->align_flag = &ts->gpencil_v3d_align; @@ -1435,92 +1676,19 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) printf("Error: 3D-View active region doesn't have any region data, so cannot be drawable\n"); return 0; } - break; - } - case SPACE_NODE: - { - /* SpaceNode *snode = curarea->spacedata.first; */ - /* set current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_v2d_align; - break; - } - case SPACE_SEQ: - { - SpaceSeq *sseq = curarea->spacedata.first; - - /* set current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_seq_align; - - /* check that gpencil data is allowed to be drawn */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { - p->status = GP_STATUS_ERROR; - if (G.debug & G_DEBUG) - printf("Error: In active view (sequencer), active mode doesn't support Grease Pencil\n"); - return 0; + /* if active object doesn't exist or isn't a GP Object, create one */ + float *cur = ED_view3d_cursor3d_get(p->scene, v3d)->location; + if ((!obact) || (obact->type != OB_GPENCIL)) { + /* create new default object */ + obact = ED_add_gpencil_object(C, p->scene, cur); } - break; - } - case SPACE_IMAGE: - { - /* SpaceImage *sima = curarea->spacedata.first; */ + /* assign object after all checks to be sure we have one active */ + p->ob = obact; - /* set the current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_ima_align; break; } - case SPACE_CLIP: - { - SpaceClip *sc = curarea->spacedata.first; - MovieClip *clip = ED_space_clip_get_clip(sc); - - if (clip == NULL) { - p->status = GP_STATUS_ERROR; - return false; - } - - /* set the current area */ - p->sa = curarea; - p->ar = ar; - p->v2d = &ar->v2d; - p->align_flag = &ts->gpencil_v2d_align; - invert_m4_m4(p->imat, sc->unistabmat); - - /* custom color for new layer */ - p->custom_color[0] = 1.0f; - p->custom_color[1] = 0.0f; - p->custom_color[2] = 0.5f; - p->custom_color[3] = 0.9f; - - if (sc->gpencil_src == SC_GPENCIL_SRC_TRACK) { - int framenr = ED_space_clip_get_clip_frame_number(sc); - MovieTrackingTrack *track = BKE_tracking_track_get_active(&clip->tracking); - MovieTrackingMarker *marker = track ? BKE_tracking_marker_get(track, framenr) : NULL; - - if (marker) { - p->imat[3][0] -= marker->pos[0]; - p->imat[3][1] -= marker->pos[1]; - } - else { - p->status = GP_STATUS_ERROR; - return false; - } - } - - invert_m4_m4(p->mat, p->imat); - copy_m4_m4(p->gsc.mat, p->mat); - break; - } /* unsupported views */ default: { @@ -1533,7 +1701,7 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) /* get gp-data */ gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); - if (gpd_ptr == NULL) { + if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { p->status = GP_STATUS_ERROR; if (G.debug & G_DEBUG) printf("Error: Current context doesn't allow for any Grease Pencil data\n"); @@ -1555,16 +1723,18 @@ 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); - copy_v4_v4(pdata->sfill, palcolor->fill); - pdata->sflag = palcolor->flag; + gp_init_drawing_brush(C, p); + + /* setup active color */ + if (curarea->spacetype == SPACE_VIEW3D) { + /* NOTE: This is only done for 3D view, as Materials aren't used for + * annotations in 2D editors + */ + gp_init_colors(p); + } + /* lock axis */ p->lock_axis = ts->gp_sculpt.lock_axis; @@ -1572,20 +1742,22 @@ static bool gp_session_initdata(bContext *C, tGPsdata *p) } /* init new painting session */ -static tGPsdata *gp_session_initpaint(bContext *C) +static tGPsdata *gp_session_initpaint(bContext *C, wmOperator *op) { tGPsdata *p = NULL; /* create new context data */ p = MEM_callocN(sizeof(tGPsdata), "GPencil Drawing Data"); - gp_session_initdata(C, p); + gp_session_initdata(C, op, p); +#if 0 /* radius for eraser circle is defined in userprefs now */ /* NOTE: we do this here, so that if we exit immediately, * erase size won't get lost */ p->radius = U.gp_eraser; +#endif /* Random generator, only init once. */ uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); @@ -1606,15 +1778,15 @@ static void gp_session_cleanup(tGPsdata *p) return; /* free stroke buffer */ - if (gpd->sbuffer) { + if (gpd->runtime.sbuffer) { /* printf("\t\tGP - free sbuffer\n"); */ - MEM_freeN(gpd->sbuffer); - gpd->sbuffer = NULL; + MEM_freeN(gpd->runtime.sbuffer); + gpd->runtime.sbuffer = NULL; } /* clear flags */ - gpd->sbuffer_size = 0; - gpd->sbuffer_sflag = 0; + gpd->runtime.sbuffer_size = 0; + gpd->runtime.sbuffer_sflag = 0; p->inittime = 0.0; } @@ -1632,11 +1804,12 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps { Scene *scene = p->scene; ToolSettings *ts = scene->toolsettings; + int cfra_eval = (int)DEG_get_ctime(p->depsgraph); /* get active layer (or add a new one if non-existent) */ p->gpl = BKE_gpencil_layer_getactive(p->gpd); if (p->gpl == NULL) { - p->gpl = BKE_gpencil_layer_addnew(p->gpd, "GP_Layer", true); + p->gpl = BKE_gpencil_layer_addnew(p->gpd, DATA_("GP_Layer"), true); if (p->custom_color[3]) copy_v3_v3(p->gpl->color, p->custom_color); @@ -1670,7 +1843,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps * -> If there are no strokes in that frame, don't add a new empty frame */ if (gpl->actframe && gpl->actframe->strokes.first) { - gpl->actframe = BKE_gpencil_layer_getframe(gpl, CFRA, GP_GETFRAME_ADD_COPY); + gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_COPY); has_layer_to_erase = true; } @@ -1708,7 +1881,9 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps else add_frame_mode = GP_GETFRAME_ADD_NEW; - p->gpf = BKE_gpencil_layer_getframe(p->gpl, CFRA, add_frame_mode); + p->gpf = BKE_gpencil_layer_getframe(p->gpl, cfra_eval, add_frame_mode); + /* set as dirty draw manager cache */ + gp_update_cache(p->gpd); if (p->gpf == NULL) { p->status = GP_STATUS_ERROR; @@ -1724,7 +1899,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps /* set 'eraser' for this stroke if using eraser */ p->paintmode = paintmode; if (p->paintmode == GP_PAINTMODE_ERASER) { - p->gpd->sbuffer_sflag |= GP_STROKE_ERASER; + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_ERASER; /* check if we should respect depth while erasing */ if (p->sa->spacetype == SPACE_VIEW3D) { @@ -1735,7 +1910,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps } else { /* disable eraser flags - so that we can switch modes during a session */ - p->gpd->sbuffer_sflag &= ~GP_STROKE_ERASER; + p->gpd->runtime.sbuffer_sflag &= ~GP_STROKE_ERASER; if (p->sa->spacetype == SPACE_VIEW3D) { if (p->gpl->flag & GP_LAYER_NO_XRAY) { @@ -1744,10 +1919,16 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps } } + /* set special fill stroke mode */ + if (p->disable_fill == true) { + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_NOFILL; + /* replace stroke color with fill color */ + copy_v4_v4(p->gpd->runtime.scolor, p->gpd->runtime.sfill); + } + /* set 'initial run' flag, which is only used to denote when a new stroke is starting */ p->flags |= GP_PAINTFLAG_FIRSTRUN; - /* when drawing in the camera view, in 2D space, set the subrect */ p->subrect = NULL; if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { @@ -1782,41 +1963,7 @@ static void gp_paint_initstroke(tGPsdata *p, eGPencil_PaintModes paintmode, Deps switch (p->sa->spacetype) { case SPACE_VIEW3D: { - p->gpd->sbuffer_sflag |= GP_STROKE_3DSPACE; - break; - } - case SPACE_NODE: - { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - break; - } - case SPACE_SEQ: - { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - break; - } - case SPACE_IMAGE: - { - SpaceImage *sima = (SpaceImage *)p->sa->spacedata.first; - - /* only set these flags if the image editor doesn't have an image active, - * otherwise user will be confused by strokes not appearing after they're drawn - * - * Admittedly, this is a bit hacky, but it works much nicer from an ergonomic standpoint! - */ - if (ELEM(NULL, sima, sima->image)) { - /* make strokes be drawn in screen space */ - p->gpd->sbuffer_sflag &= ~GP_STROKE_2DSPACE; - *(p->align_flag) &= ~GP_PROJECT_VIEWSPACE; - } - else { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; - } - break; - } - case SPACE_CLIP: - { - p->gpd->sbuffer_sflag |= GP_STROKE_2DSPACE; + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; break; } } @@ -1839,7 +1986,7 @@ static void gp_paint_strokeend(tGPsdata *p) } /* check if doing eraser or not */ - if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) { + if ((p->gpd->runtime.sbuffer_sflag & GP_STROKE_ERASER) == 0) { /* simplify stroke before transferring? */ gp_stroke_simplify(p); @@ -1920,10 +2067,12 @@ static void gpencil_draw_toggle_eraser_cursor(bContext *C, tGPsdata *p, short en p->erasercursor = NULL; } else if (enable && !p->erasercursor) { + ED_gpencil_toggle_brush_cursor(p->C, false, NULL); /* enable cursor */ - p->erasercursor = WM_paint_cursor_activate(CTX_wm_manager(C), - NULL, /* XXX */ - gpencil_draw_eraser, p); + p->erasercursor = WM_paint_cursor_activate( + CTX_wm_manager(C), + NULL, /* XXX */ + gpencil_draw_eraser, p); } } @@ -1943,13 +2092,26 @@ static bool gpencil_is_tablet_eraser_active(const wmEvent *event) static void gpencil_draw_exit(bContext *C, wmOperator *op) { tGPsdata *p = op->customdata; + bGPdata *gpd = CTX_data_gpencil_data(C); /* clear undo stack */ gpencil_undo_finish(); /* restore cursor to indicate end of drawing */ - WM_cursor_modal_restore(CTX_wm_window(C)); + if (p->sa->spacetype != SPACE_VIEW3D) { + WM_cursor_modal_restore(CTX_wm_window(C)); + } + else { + /* or restore paint if 3D view */ + if ((p) && (p->paintmode == GP_PAINTMODE_ERASER)) { + WM_cursor_modal_set(p->win, CURSOR_STD); + } + /* drawing batch cache is dirty now */ + if (gpd) { + gp_update_cache(gpd); + } + } /* don't assume that operator data exists at all */ if (p) { /* check size of buffer before cleanup, to determine if anything happened here */ @@ -1962,11 +2124,16 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) * NOTE: Do this even when not in eraser mode, as eraser may * have been toggled at some point. */ - U.gp_eraser = p->radius; + if (p->eraser) { + p->eraser->size = p->radius; + } /* cleanup */ gp_paint_cleanup(p); gp_session_cleanup(p); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + + /* finally, free the temp data */ gp_session_free(p); } @@ -1986,9 +2153,18 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) { tGPsdata *p; eGPencil_PaintModes paintmode = RNA_enum_get(op->ptr, "mode"); + ToolSettings *ts = CTX_data_tool_settings(C); + Brush *brush = BKE_brush_getactive_gpencil(ts); + + /* if mode is draw and the brush is eraser, cancel */ + if (paintmode != GP_PAINTMODE_ERASER) { + if ((brush) && (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) { + return 0; + } + } /* check context */ - p = op->customdata = gp_session_initpaint(C); + p = op->customdata = gp_session_initpaint(C, op); if ((p == NULL) || (p->status == GP_STATUS_ERROR)) { /* something wasn't set correctly in context */ gpencil_draw_exit(C, op); @@ -2009,6 +2185,8 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) p->keymodifier = -1; } + p->reports = op->reports; + /* everything is now setup ok */ return 1; } @@ -2019,10 +2197,15 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) /* ensure that the correct cursor icon is set */ static void gpencil_draw_cursor_set(tGPsdata *p) { - if (p->paintmode == GP_PAINTMODE_ERASER) + Brush *brush = p->brush; + if ((p->paintmode == GP_PAINTMODE_ERASER) || + (brush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE)) + { WM_cursor_modal_set(p->win, BC_CROSSCURSOR); /* XXX need a better cursor */ - else - WM_cursor_modal_set(p->win, BC_PAINTBRUSHCURSOR); + } + else { + WM_cursor_modal_set(p->win, CURSOR_STD); + } } /* update UI indicators of status, including cursor and header prints */ @@ -2030,11 +2213,24 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) { /* header prints */ switch (p->status) { - case GP_STATUS_PAINTING: - /* only print this for paint-sessions, otherwise it gets annoying */ - if (GPENCIL_SKETCH_SESSIONS_ON(p->scene)) - ED_workspace_status_text(C, IFACE_("Grease Pencil: Drawing/erasing stroke... Release to end stroke")); - break; + +#if 0 /* FIXME, this never runs! */ + switch (p->paintmode) { + case GP_PAINTMODE_DRAW_POLY: + /* Provide usage tips, since this is modal, and unintuitive without hints */ + ED_workspace_status_text( + C, IFACE_( + "Annotation Create Poly: LMB click to place next stroke vertex | " + "ESC/Enter to end (or click outside this area)" + )); + break; + default: + /* Do nothing - the others are self explanatory, exit quickly once the mouse is released + * Showing any text would just be annoying as it would flicker. + */ + break; + } +#endif case GP_STATUS_IDLING: /* print status info */ @@ -2048,12 +2244,11 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) "ESC/Enter to end (or click outside this area)")); break; case GP_PAINTMODE_DRAW: - ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw | " - "E/ESC/Enter to end (or click outside this area)")); + ED_workspace_status_text(C, IFACE_("Grease Pencil Freehand Session: Hold and drag LMB to draw")); break; case GP_PAINTMODE_DRAW_POLY: ED_workspace_status_text(C, IFACE_("Grease Pencil Poly Session: LMB click to place next stroke vertex | " - "ESC/Enter to end (or click outside this area)")); + "Release Shift/ESC/Enter to end (or click outside this area)")); break; default: /* unhandled future cases */ @@ -2067,14 +2262,19 @@ static void gpencil_draw_status_indicators(bContext *C, tGPsdata *p) /* clear status string */ ED_workspace_status_text(C, NULL); break; + case GP_STATUS_PAINTING: + break; } } /* ------------------------------- */ /* create a new stroke point at the point indicated by the painting context */ -static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) +static void gpencil_draw_apply(bContext *C, wmOperator *op, tGPsdata *p, Depsgraph *depsgraph) { + bGPdata *gpd = p->gpd; + tGPspoint *pt = NULL; + /* handle drawing/erasing -> test for erasing first */ if (p->paintmode == GP_PAINTMODE_ERASER) { /* do 'live' erasing now */ @@ -2087,6 +2287,17 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph } /* only add current point to buffer if mouse moved (even though we got an event, it might be just noise) */ else if (gp_stroke_filtermval(p, p->mval, p->mvalo)) { + + /* if lazy mouse, interpolate the last and current mouse positions */ + if (GPENCIL_LAZY_MODE(p->brush, p->shift)) { + float now_mouse[2]; + float last_mouse[2]; + copy_v2fl_v2i(now_mouse, p->mval); + copy_v2fl_v2i(last_mouse, p->mvalo); + interp_v2_v2v2(now_mouse, now_mouse, last_mouse, p->brush->smooth_stroke_factor); + round_v2i_v2fl(p->mval, now_mouse); + } + /* try to add point */ short ok = gp_stroke_addpoint(p, p->mval, p->pressure, p->curtime); @@ -2124,11 +2335,24 @@ static void gpencil_draw_apply(wmOperator *op, tGPsdata *p, Depsgraph *depsgraph p->mvalo[1] = p->mval[1]; p->opressure = p->pressure; p->ocurtime = p->curtime; + + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; + if (p->paintmode != GP_PAINTMODE_ERASER) { + ED_gpencil_toggle_brush_cursor(C, true, &pt->x); + } + } + else if ((p->brush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) && + (gpd->runtime.sbuffer_size > 0)) + { + pt = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_size - 1; + if (p->paintmode != GP_PAINTMODE_ERASER) { + ED_gpencil_toggle_brush_cursor(C, true, &pt->x); + } } } /* handle draw event */ -static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsgraph *depsgraph) +static void gpencil_draw_apply_event(bContext *C, wmOperator *op, const wmEvent *event, Depsgraph *depsgraph, int x, int y) { tGPsdata *p = op->customdata; PointerRNA itemptr; @@ -2136,13 +2360,15 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg int tablet = 0; /* convert from window-space to area-space mouse coordinates + * add any x,y override position for fake events * NOTE: float to ints conversions, +1 factor is probably used to ensure a bit more accurate rounding... */ - p->mval[0] = event->mval[0] + 1; - p->mval[1] = event->mval[1] + 1; + p->mval[0] = event->mval[0] + 1 - x; + p->mval[1] = event->mval[1] + 1 - y; + p->shift = event->shift; /* verify key status for straight lines */ - if ((event->ctrl > 0) || (event->alt > 0)) { + if ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false)) { if (p->straight[0] == 0) { int dx = abs(p->mval[0] - p->mvalo[0]); int dy = abs(p->mval[1] - p->mvalo[1]); @@ -2151,12 +2377,12 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg if (dx >= dy) { /* horizontal */ p->straight[0] = 1; - p->straight[1] = p->mval[1]; /* save y */ + p->straight[1] = (short)p->mval[1]; /* save y */ } else { /* vertical */ p->straight[0] = 2; - p->straight[1] = p->mval[0]; /* save x */ + p->straight[1] = (short)p->mval[0]; /* save x */ } } } @@ -2191,6 +2417,22 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg p->pressure = 1.0f; } + /* special eraser modes */ + if (p->paintmode == GP_PAINTMODE_ERASER) { + if (event->shift > 0) { + p->flags |= GP_PAINTFLAG_HARD_ERASER; + } + else { + p->flags &= ~GP_PAINTFLAG_HARD_ERASER; + } + if (event->alt > 0) { + p->flags |= GP_PAINTFLAG_STROKE_ERASER; + } + else { + p->flags &= ~GP_PAINTFLAG_STROKE_ERASER; + } + } + /* special exception for start of strokes (i.e. maybe for just a dot) */ if (p->flags & GP_PAINTFLAG_FIRSTRUN) { p->flags &= ~GP_PAINTFLAG_FIRSTRUN; @@ -2233,7 +2475,7 @@ static void gpencil_draw_apply_event(wmOperator *op, const wmEvent *event, Depsg RNA_float_set(&itemptr, "time", p->curtime - p->inittime); /* apply the current latest drawing point */ - gpencil_draw_apply(op, p, depsgraph); + gpencil_draw_apply(C, op, p, depsgraph); /* force refresh */ ED_region_tag_redraw(p->ar); /* just active area for now, since doing whole screen is too slow */ @@ -2251,7 +2493,7 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) /* try to initialize context data needed while drawing */ if (!gpencil_draw_init(C, op, NULL)) { - if (op->customdata) MEM_freeN(op->customdata); + MEM_SAFE_FREE(op->customdata); /* printf("\tGP - no valid data\n"); */ return OPERATOR_CANCELLED; } @@ -2298,7 +2540,7 @@ static int gpencil_draw_exec(bContext *C, wmOperator *op) } /* apply this data as necessary now (as per usual) */ - gpencil_draw_apply(op, p, depsgraph); + gpencil_draw_apply(C, op, p, depsgraph); } RNA_END; @@ -2324,6 +2566,11 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event if (G.debug & G_DEBUG) printf("GPencil - Starting Drawing\n"); + /* support for tablets eraser pen */ + if (gpencil_is_tablet_eraser_active(event)) { + RNA_enum_set(op->ptr, "mode", GP_PAINTMODE_ERASER); + } + /* try to initialize context data needed while drawing */ if (!gpencil_draw_init(C, op, event)) { if (op->customdata) @@ -2344,6 +2591,9 @@ 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); } + else { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } /* set cursor * NOTE: This may change later (i.e. intentionally via brush toggle, * or unintentionally if the user scrolls outside the area)... @@ -2357,7 +2607,7 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event p->status = GP_STATUS_PAINTING; /* handle the initial drawing - i.e. for just doing a simple dot */ - gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0); op->flag |= OP_IS_MODAL_CURSOR_REGION; } else { @@ -2366,9 +2616,29 @@ static int gpencil_draw_invoke(bContext *C, wmOperator *op, const wmEvent *event op->flag |= OP_IS_MODAL_CURSOR_REGION; } + /* enable paint mode */ + if (p->sa->spacetype == SPACE_VIEW3D) { + Object *ob = CTX_data_active_object(C); + if (ob && (ob->type == OB_GPENCIL) && ((p->gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0)) { + /* Just set paintmode flag... */ + p->gpd->flag |= GP_DATA_STROKE_PAINTMODE; + /* disable other GP modes */ + p->gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + p->gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + p->gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + /* set workspace mode */ + ob->restore_mode = ob->mode; + ob->mode = OB_MODE_GPENCIL_PAINT; + /* redraw mode on screen */ + WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL); + } + } + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + /* add a modal handler for this operator, so that we can then draw continuous strokes */ WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } @@ -2397,7 +2667,7 @@ static tGPsdata *gpencil_stroke_begin(bContext *C, wmOperator *op) /* XXX: watch it with the paintmode! in future, * it'd be nice to allow changing paint-mode when in sketching-sessions */ - if (gp_session_initdata(C, p)) + if (gp_session_initdata(C, op, p)) gp_paint_initstroke(p, p->paintmode, CTX_data_depsgraph(C)); if (p->status != GP_STATUS_ERROR) { @@ -2448,6 +2718,76 @@ static void gpencil_move_last_stroke_to_back(bContext *C) BLI_insertlinkbefore(&gpf->strokes, gpf->strokes.first, gps); } +/* add events for missing mouse movements when the artist draw very fast */ +static void gpencil_add_missing_events(bContext *C, wmOperator *op, const wmEvent *event, tGPsdata *p) +{ + Brush *brush = p->brush; + if (brush->gpencil_settings->input_samples == 0) { + return; + } + RegionView3D *rv3d = p->ar->regiondata; + float defaultpixsize = rv3d->pixsize * 1000.0f; + int samples = (GP_MAX_INPUT_SAMPLES - brush->gpencil_settings->input_samples + 1); + float thickness = (float)brush->size; + + float pt[2], a[2], b[2]; + float vec[3]; + float scale = 1.0f; + + /* get pixel scale */ + gp_get_3d_reference(p, vec); + mul_m4_v3(rv3d->persmat, vec); + if (rv3d->is_persp) { + scale = vec[2] * defaultpixsize; + } + else { + scale = defaultpixsize; + } + + /* The thickness of the brush is reduced of thickness to get overlap dots */ + float dot_factor = 0.50f; + if (samples < 2) { + dot_factor = 0.05f; + } + else if (samples < 4) { + dot_factor = 0.10f; + } + else if (samples < 7) { + dot_factor = 0.3f; + } + else if (samples < 10) { + dot_factor = 0.4f; + } + float factor = ((thickness * dot_factor) / scale) * samples; + + copy_v2fl_v2i(a, p->mvalo); + b[0] = event->mval[0] + 1; + b[1] = event->mval[1] + 1; + + /* get distance in pixels */ + float dist = len_v2v2(a, b); + + /* for very small distances, add a half way point */ + if (dist <= 2.0f) { + interp_v2_v2v2(pt, a, b, 0.5f); + sub_v2_v2v2(pt, b, pt); + /* create fake event */ + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), + (int)pt[0], (int)pt[1]); + } + else if (dist >= factor) { + int slices = 2 + (int)((dist - 1.0) / factor); + float n = 1.0f / slices; + for (int i = 1; i < slices; i++) { + interp_v2_v2v2(pt, a, b, n * i); + sub_v2_v2v2(pt, b, pt); + /* create fake event */ + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), + (int)pt[0], (int)pt[1]); + } + } +} + /* events handling during interactive drawing part of operator */ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) { @@ -2488,10 +2828,6 @@ 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, p->keymodifier)) && (event->val == KM_RELEASE)) { - /* enable continuous if release D key in mid drawing */ - p->scene->toolsettings->gpencil_flags |= GP_TOOL_FLAG_PAINTSESSIONS_ON; - } else if ((event->type == BKEY) && (event->val == KM_RELEASE)) { /* Add Blank Frame * - Since this operator is non-modal, we can just call it here, and keep going... @@ -2510,7 +2846,10 @@ 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, EKEY)) { + /* if polyline and release shift must cancel */ + if ((ELEM(event->type, RETKEY, PADENTER, ESCKEY, SPACEKEY, EKEY)) || + ((p->paintmode == GP_PAINTMODE_DRAW_POLY) && (event->shift == 0))) + { /* exit() ends the current stroke before cleaning up */ /* printf("\t\tGP - end of paint op + end of stroke\n"); */ /* if drawing polygon and enable on back, must move stroke */ @@ -2537,10 +2876,8 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) int sketch = 0; /* basically, this should be mouse-button up = end stroke - * BUT what happens next depends on whether we 'painting sessions' is enabled + * BUT, polyline drawing is an exception -- all knots should be added during one session */ - sketch |= GPENCIL_SKETCH_SESSIONS_ON(p->scene); - /* polyline drawing is also 'sketching' -- all knots should be added during one session */ sketch |= (p->paintmode == GP_PAINTMODE_DRAW_POLY); if (sketch) { @@ -2585,6 +2922,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) } } } + /* drawing batch cache is dirty now */ + gp_update_cache(p->gpd); + p->status = GP_STATUS_DONE; estate = OPERATOR_FINISHED; } @@ -2689,7 +3029,9 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE) || (p->flags & GP_PAINTFLAG_FIRSTRUN)) { /* handle drawing event */ /* printf("\t\tGP - add point\n"); */ - gpencil_draw_apply_event(op, event, CTX_data_depsgraph(C)); + gpencil_add_missing_events(C, op, event, p); + + gpencil_draw_apply_event(C, op, event, CTX_data_depsgraph(C), 0, 0); /* finish painting operation if anything went wrong just now */ if (p->status == GP_STATUS_ERROR) { @@ -2791,7 +3133,7 @@ void GPENCIL_OT_draw(wmOperatorType *ot) /* identifiers */ ot->name = "Grease Pencil Draw"; ot->idname = "GPENCIL_OT_draw"; - ot->description = "Make annotations on the active data"; + ot->description = "Draw a new stroke in the active Grease Pencil Object"; /* api callbacks */ ot->exec = gpencil_draw_exec; @@ -2811,5 +3153,11 @@ void GPENCIL_OT_draw(wmOperatorType *ot) /* NOTE: wait for input is enabled by default, so that all UI code can work properly without needing users to know about this */ prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", "Wait for first click instead of painting immediately"); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "disable_straight", false, "No Straight lines", "Disable key for straight lines"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + + prop = RNA_def_boolean(ot->srna, "disable_fill", false, "No Fill Areas", "Disable fill to use stroke as fill boundary"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c new file mode 100644 index 00000000000..9dbec666cc4 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -0,0 +1,716 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2017, Blender Foundation + * This is a new part of Blender + * + * Contributor(s): Antonio Vazquez + * + * ***** END GPL LICENSE BLOCK ***** + * + * Operators for creating new Grease Pencil primitives (boxes, circles, ...) + */ + +/** \file blender/editors/gpencil/gpencil_primitive.c + * \ingroup edgpencil + */ + + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <stddef.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_utildefines.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_main.h" +#include "BKE_brush.h" +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_library.h" +#include "BKE_material.h" +#include "BKE_paint.h" +#include "BKE_report.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "ED_gpencil.h" +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_view3d.h" +#include "ED_space_api.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "gpencil_intern.h" + +#define MIN_EDGES 2 +#define MAX_EDGES 100 + +#define IDLE 0 +#define IN_PROGRESS 1 + +/* ************************************************ */ +/* Core/Shared Utilities */ + +/* Poll callback for primitive operators */ +static bool gpencil_primitive_add_poll(bContext *C) +{ + /* only 3D view */ + ScrArea *sa = CTX_wm_area(C); + if (sa && sa->spacetype != SPACE_VIEW3D) { + return 0; + } + + /* need data to create primitive */ + bGPdata *gpd = CTX_data_gpencil_data(C); + if (gpd == NULL) { + return 0; + } + + /* only in edit and paint modes + * - paint as it's the "drawing/creation mode" + * - edit as this is more of an atomic editing operation + * (similar to copy/paste), and also for consistency + */ + if ((gpd->flag & (GP_DATA_STROKE_PAINTMODE | GP_DATA_STROKE_EDITMODE)) == 0) { + CTX_wm_operator_poll_msg_set(C, "Primitives can only be added in Draw or Edit modes"); + return 0; + } + + /* don't allow operator to function if the active layer is locked/hidden + * (BUT, if there isn't an active layer, we are free to add new layer when the time comes) + */ + bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); + if ((gpl) && (gpl->flag & (GP_LAYER_LOCKED | GP_LAYER_HIDE))) { + CTX_wm_operator_poll_msg_set(C, "Primitives cannot be added as active layer is locked or hidden"); + return 0; + } + + return 1; +} + + +/* ****************** Primitive Interactive *********************** */ + +/* Helper: Create internal strokes primitives data */ +static void gp_primitive_set_initdata(bContext *C, tGPDprimitive *tgpi) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); + Brush *brush; + + /* if brush doesn't exist, create a new one */ + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + brush = BKE_brush_getactive_gpencil(ts); + } + else { + /* Use the current */ + brush = BKE_brush_getactive_gpencil(ts); + } + tgpi->brush = brush; + + /* if layer doesn't exist, create a new one */ + if (gpl == NULL) { + gpl = BKE_gpencil_layer_addnew(tgpi->gpd, DATA_("Primitives"), true); + } + tgpi->gpl = gpl; + + /* create a new temporary frame */ + tgpi->gpf = MEM_callocN(sizeof(bGPDframe), "Temp bGPDframe"); + tgpi->gpf->framenum = tgpi->cframe = cfra_eval; + + /* create new temp stroke */ + bGPDstroke *gps = MEM_callocN(sizeof(bGPDstroke), "Temp bGPDstroke"); + gps->thickness = 2.0f; + gps->inittime = 0.0f; + + /* enable recalculation flag by default */ + gps->flag |= GP_STROKE_RECALC_CACHES; + /* the polygon must be closed, so enabled cyclic */ + gps->flag |= GP_STROKE_CYCLIC; + gps->flag |= GP_STROKE_3DSPACE; + + gps->mat_nr = BKE_object_material_slot_find_index(tgpi->ob, tgpi->mat) - 1; + + /* allocate memory for storage points, but keep empty */ + gps->totpoints = 0; + gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert), "gp_stroke_weights"); + /* initialize triangle memory to dummy data */ + gps->tot_triangles = 0; + gps->triangles = NULL; + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* add to strokes */ + BLI_addtail(&tgpi->gpf->strokes, gps); +} + +/* ----------------------- */ +/* Drawing Callbacks */ + +/* Drawing callback for modal operator in 3d mode */ +static void gpencil_primitive_draw_3d(const bContext *C, ARegion *UNUSED(ar), void *arg) +{ + tGPDprimitive *tgpi = (tGPDprimitive *)arg; + ED_gp_draw_primitives(C, tgpi, REGION_DRAW_POST_VIEW); +} + +/* ----------------------- */ + +/* Helper: Draw status message while the user is running the operator */ +static void gpencil_primitive_status_indicators(bContext *C, tGPDprimitive *tgpi) +{ + Scene *scene = tgpi->scene; + char status_str[UI_MAX_DRAW_STR]; + char msg_str[UI_MAX_DRAW_STR]; + + if (tgpi->type == GP_STROKE_BOX) { + BLI_strncpy(msg_str, IFACE_("Rectangle: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm, Shift to square"), UI_MAX_DRAW_STR); + } + else if (tgpi->type == GP_STROKE_LINE) { + BLI_strncpy(msg_str, IFACE_("Line: ESC/RMB to cancel, LMB set origin, Enter/LMB to confirm"), UI_MAX_DRAW_STR); + } + else { + BLI_strncpy(msg_str, IFACE_("Circle: ESC/RMB to cancel, Enter/LMB to confirm, WHEEL to adjust edge number, Shift to square"), UI_MAX_DRAW_STR); + } + + if (tgpi->type == GP_STROKE_CIRCLE) { + if (hasNumInput(&tgpi->num)) { + char str_offs[NUM_STR_REP_LEN]; + + outputNumInput(&tgpi->num, str_offs, &scene->unit); + BLI_snprintf(status_str, sizeof(status_str), "%s: %s", msg_str, str_offs); + } + else { + if (tgpi->flag == IN_PROGRESS) { + BLI_snprintf( + status_str, sizeof(status_str), "%s: %d (%d, %d) (%d, %d)", msg_str, (int)tgpi->tot_edges, + tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]); + } + else { + BLI_snprintf( + status_str, sizeof(status_str), "%s: %d (%d, %d)", msg_str, (int)tgpi->tot_edges, + tgpi->bottom[0], tgpi->bottom[1]); + } + } + } + else { + if (tgpi->flag == IN_PROGRESS) { + BLI_snprintf( + status_str, sizeof(status_str), "%s: (%d, %d) (%d, %d)", msg_str, + tgpi->top[0], tgpi->top[1], tgpi->bottom[0], tgpi->bottom[1]); + } + else { + BLI_snprintf( + status_str, sizeof(status_str), "%s: (%d, %d)", msg_str, + tgpi->bottom[0], tgpi->bottom[1]); + } + } + ED_workspace_status_text(C, status_str); +} + +/* ----------------------- */ + +/* create a rectangle */ +static void gp_primitive_rectangle(tGPDprimitive *tgpi, tGPspoint *points2D) +{ + BLI_assert(tgpi->tot_edges == 4); + + points2D[0].x = tgpi->top[0]; + points2D[0].y = tgpi->top[1]; + + points2D[1].x = tgpi->bottom[0]; + points2D[1].y = tgpi->top[1]; + + points2D[2].x = tgpi->bottom[0]; + points2D[2].y = tgpi->bottom[1]; + + points2D[3].x = tgpi->top[0]; + points2D[3].y = tgpi->bottom[1]; +} + +/* create a line */ +static void gp_primitive_line(tGPDprimitive *tgpi, tGPspoint *points2D) +{ + BLI_assert(tgpi->tot_edges == 2); + + points2D[0].x = tgpi->top[0]; + points2D[0].y = tgpi->top[1]; + + points2D[1].x = tgpi->bottom[0]; + points2D[1].y = tgpi->bottom[1]; +} + +/* create a circle */ +static void gp_primitive_circle(tGPDprimitive *tgpi, tGPspoint *points2D) +{ + const int totpoints = tgpi->tot_edges; + const float step = (2.0f * M_PI) / (float)(totpoints); + float center[2]; + float radius[2]; + float a = 0.0f; + + /* TODO: Use math-lib functions for these? */ + center[0] = tgpi->top[0] + ((tgpi->bottom[0] - tgpi->top[0]) / 2.0f); + center[1] = tgpi->top[1] + ((tgpi->bottom[1] - tgpi->top[1]) / 2.0f); + radius[0] = fabsf(((tgpi->bottom[0] - tgpi->top[0]) / 2.0f)); + radius[1] = fabsf(((tgpi->bottom[1] - tgpi->top[1]) / 2.0f)); + + for (int i = 0; i < totpoints; i++) { + tGPspoint *p2d = &points2D[i]; + + p2d->x = (int)(center[0] + cosf(a) * radius[0]); + p2d->y = (int)(center[1] + sinf(a) * radius[1]); + a += step; + } +} + +/* Helper: Update shape of the stroke */ +static void gp_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) +{ + ToolSettings *ts = tgpi->scene->toolsettings; + bGPdata *gpd = tgpi->gpd; + bGPDstroke *gps = tgpi->gpf->strokes.first; + + /* realloc points to new size */ + /* TODO: only do this if the size has changed? */ + gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * tgpi->tot_edges); + gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * tgpi->tot_edges); + gps->totpoints = tgpi->tot_edges; + + /* compute screen-space coordinates for points */ + tGPspoint *points2D = MEM_callocN(sizeof(tGPspoint) * tgpi->tot_edges, "gp primitive points2D"); + switch (tgpi->type) { + case GP_STROKE_BOX: + gp_primitive_rectangle(tgpi, points2D); + break; + case GP_STROKE_LINE: + gp_primitive_line(tgpi, points2D); + break; + case GP_STROKE_CIRCLE: + gp_primitive_circle(tgpi, points2D); + break; + default: + break; + } + + /* convert screen-coordinates to 3D coordinates */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + tGPspoint *p2d = &points2D[i]; + + + /* convert screen-coordinates to 3D coordinates */ + gp_stroke_convertcoords_tpoint(tgpi->scene, tgpi->ar, tgpi->v3d, tgpi->ob, tgpi->gpl, p2d, NULL, &pt->x); + + pt->pressure = 1.0f; + pt->strength = tgpi->brush->gpencil_settings->draw_strength; + pt->time = 0.0f; + + dvert->totweight = 0; + dvert->dw = NULL; + } + + /* if axis locked, reproject to plane locked */ + if (tgpi->lock_axis > GP_LOCKAXIS_NONE) { + bGPDspoint *tpt = gps->points; + float origin[3]; + ED_gp_get_drawing_reference(tgpi->v3d, tgpi->scene, tgpi->ob, tgpi->gpl, + ts->gpencil_v3d_align, origin); + + for (int i = 0; i < gps->totpoints; i++, tpt++) { + ED_gp_project_point_to_plane(tgpi->ob, tgpi->rv3d, origin, + ts->gp_sculpt.lock_axis - 1, + tpt); + } + } + + /* if parented change position relative to parent object */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + gp_apply_parent_point(tgpi->depsgraph, tgpi->ob, tgpi->gpd, tgpi->gpl, pt); + } + + /* force fill recalc */ + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* free temp data */ + MEM_SAFE_FREE(points2D); + + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); +} + +/* Update screen and stroke */ +static void gpencil_primitive_update(bContext *C, wmOperator *op, tGPDprimitive *tgpi) +{ + /* update indicator in header */ + gpencil_primitive_status_indicators(C, tgpi); + /* apply... */ + tgpi->type = RNA_enum_get(op->ptr, "type"); + tgpi->tot_edges = RNA_int_get(op->ptr, "edges"); + /* update points position */ + gp_primitive_update_strokes(C, tgpi); +} + +/* ----------------------- */ + +/* Exit and free memory */ +static void gpencil_primitive_exit(bContext *C, wmOperator *op) +{ + tGPDprimitive *tgpi = op->customdata; + bGPdata *gpd = tgpi->gpd; + + /* don't assume that operator data exists at all */ + if (tgpi) { + /* remove drawing handler */ + if (tgpi->draw_handle_3d) { + ED_region_draw_cb_exit(tgpi->ar->type, tgpi->draw_handle_3d); + } + + /* clear status message area */ + ED_workspace_status_text(C, NULL); + + /* finally, free memory used by temp data */ + BKE_gpencil_free_strokes(tgpi->gpf); + MEM_freeN(tgpi->gpf); + MEM_freeN(tgpi); + } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* clear pointer */ + op->customdata = NULL; +} + +/* Init new temporary primitive data */ +static void gpencil_primitive_init(bContext *C, wmOperator *op) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); + + /* create temporary operator data */ + tGPDprimitive *tgpi = MEM_callocN(sizeof(tGPDprimitive), "GPencil Primitive Data"); + op->customdata = tgpi; + + /* set current scene and window info */ + tgpi->scene = scene; + tgpi->ob = CTX_data_active_object(C); + tgpi->sa = CTX_wm_area(C); + tgpi->ar = CTX_wm_region(C); + tgpi->rv3d = tgpi->ar->regiondata; + tgpi->v3d = tgpi->sa->spacedata.first; + tgpi->depsgraph = CTX_data_depsgraph(C); + tgpi->win = CTX_wm_window(C); + + /* set current frame number */ + tgpi->cframe = cfra_eval; + + /* set GP datablock */ + tgpi->gpd = gpd; + + /* getcolor info */ + tgpi->mat = BKE_gpencil_material_ensure(bmain, tgpi->ob); + + /* set parameters */ + tgpi->type = RNA_enum_get(op->ptr, "type"); + + /* if circle set default to 32 */ + if (tgpi->type == GP_STROKE_CIRCLE) { + RNA_int_set(op->ptr, "edges", 32); + } + else if (tgpi->type == GP_STROKE_BOX) { + RNA_int_set(op->ptr, "edges", 4); + } + else { /* LINE */ + RNA_int_set(op->ptr, "edges", 2); + } + + tgpi->tot_edges = RNA_int_get(op->ptr, "edges"); + tgpi->flag = IDLE; + + tgpi->lock_axis = ts->gp_sculpt.lock_axis; + + /* set temp layer, frame and stroke */ + gp_primitive_set_initdata(C, tgpi); +} + +/* ----------------------- */ + +/* Invoke handler: Initialize the operator */ +static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wmWindow *win = CTX_wm_window(C); + bGPdata *gpd = CTX_data_gpencil_data(C); + tGPDprimitive *tgpi = NULL; + + /* initialize operator runtime data */ + gpencil_primitive_init(C, op); + tgpi = op->customdata; + + /* if in tools region, wait till we get to the main (3d-space) + * region before allowing drawing to take place. + */ + op->flag |= OP_IS_MODAL_CURSOR_REGION; + + /* Enable custom drawing handlers */ + tgpi->draw_handle_3d = ED_region_draw_cb_activate(tgpi->ar->type, gpencil_primitive_draw_3d, tgpi, REGION_DRAW_POST_VIEW); + + /* set cursor to indicate modal */ + WM_cursor_modal_set(win, BC_CROSSCURSOR); + + /* update sindicator in header */ + gpencil_primitive_status_indicators(C, tgpi); + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); + + /* add a modal handler for this operator */ + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +/* Helper to complete a primitive */ +static void gpencil_primitive_done(bContext *C, wmOperator *op, wmWindow *win, tGPDprimitive *tgpi) +{ + bGPDframe *gpf; + bGPDstroke *gps; + + /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* insert keyframes as required... */ + gpf = BKE_gpencil_layer_getframe(tgpi->gpl, tgpi->cframe, GP_GETFRAME_ADD_NEW); + + /* prepare stroke to get transfered */ + gps = tgpi->gpf->strokes.first; + if (gps) { + gps->thickness = tgpi->brush->size; + gps->flag |= GP_STROKE_RECALC_CACHES; + } + + /* transfer stroke from temporary buffer to the actual frame */ + BLI_movelisttolist(&gpf->strokes, &tgpi->gpf->strokes); + BLI_assert(BLI_listbase_is_empty(&tgpi->gpf->strokes)); + + /* clean up temp data */ + gpencil_primitive_exit(C, op); +} + +/* Modal handler: Events handling during interactive part */ +static int gpencil_primitive_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + tGPDprimitive *tgpi = op->customdata; + wmWindow *win = CTX_wm_window(C); + const bool has_numinput = hasNumInput(&tgpi->num); + + switch (event->type) { + case LEFTMOUSE: + if ((event->val == KM_PRESS) && (tgpi->flag == IDLE)) { + /* start drawing primitive */ + /* TODO: Ignore if not in main region yet */ + tgpi->flag = IN_PROGRESS; + + tgpi->top[0] = event->mval[0]; + tgpi->top[1] = event->mval[1]; + + tgpi->bottom[0] = event->mval[0]; + tgpi->bottom[1] = event->mval[1]; + } + else if ((event->val == KM_RELEASE) && (tgpi->flag == IN_PROGRESS)) { + /* stop drawing primitive */ + tgpi->flag = IDLE; + gpencil_primitive_done(C, op, win, tgpi); + /* done! */ + return OPERATOR_FINISHED; + } + else { + if (G.debug & G_DEBUG) { + printf("GP Add Primitive Modal: LEFTMOUSE %d, Status = %d\n", event->val, tgpi->flag); + } + } + break; + case RETKEY: /* confirm */ + { + tgpi->flag = IDLE; + gpencil_primitive_done(C, op, win, tgpi); + /* done! */ + return OPERATOR_FINISHED; + } + + case ESCKEY: /* cancel */ + case RIGHTMOUSE: + { + /* return to normal cursor and header status */ + ED_workspace_status_text(C, NULL); + WM_cursor_modal_restore(win); + + /* clean up temp data */ + gpencil_primitive_exit(C, op); + + /* canceled! */ + return OPERATOR_CANCELLED; + } + + case WHEELUPMOUSE: + { + if (tgpi->type == GP_STROKE_CIRCLE) { + tgpi->tot_edges = tgpi->tot_edges + 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case WHEELDOWNMOUSE: + { + if (tgpi->type == GP_STROKE_CIRCLE) { + tgpi->tot_edges = tgpi->tot_edges - 1; + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + } + break; + } + case MOUSEMOVE: /* calculate new position */ + { + /* only handle mousemove if not doing numinput */ + if (has_numinput == false) { + /* update position of mouse */ + tgpi->bottom[0] = event->mval[0]; + tgpi->bottom[1] = event->mval[1]; + if (tgpi->flag == IDLE) { + tgpi->top[0] = event->mval[0]; + tgpi->top[1] = event->mval[1]; + } + /* Keep square if shift key */ + if (event->shift) { + tgpi->bottom[1] = tgpi->top[1] - (tgpi->bottom[0] - tgpi->top[0]); + } + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + } + break; + } + default: + { + if ((event->val == KM_PRESS) && handleNumInput(C, &tgpi->num, event)) { + float value; + + /* Grab data from numeric input, and store this new value (the user see an int) */ + value = tgpi->tot_edges; + applyNumInput(&tgpi->num, &value); + tgpi->tot_edges = value; + + CLAMP(tgpi->tot_edges, MIN_EDGES, MAX_EDGES); + RNA_int_set(op->ptr, "edges", tgpi->tot_edges); + + /* update screen */ + gpencil_primitive_update(C, op, tgpi); + + break; + } + else { + /* unhandled event - allow to pass through */ + return OPERATOR_RUNNING_MODAL | OPERATOR_PASS_THROUGH; + } + } + } + + /* still running... */ + return OPERATOR_RUNNING_MODAL; +} + +/* Cancel handler */ +static void gpencil_primitive_cancel(bContext *C, wmOperator *op) +{ + /* this is just a wrapper around exit() */ + gpencil_primitive_exit(C, op); +} + +void GPENCIL_OT_primitive(wmOperatorType *ot) +{ + static EnumPropertyItem primitive_type[] = { + { GP_STROKE_BOX, "BOX", 0, "Box", "" }, + { GP_STROKE_LINE, "LINE", 0, "Line", "" }, + { GP_STROKE_CIRCLE, "CIRCLE", 0, "Circle", "" }, + { 0, NULL, 0, NULL, NULL } + }; + + /* identifiers */ + ot->name = "Grease Pencil Shapes"; + ot->idname = "GPENCIL_OT_primitive"; + ot->description = "Create predefined grease pencil stroke shapes"; + + /* callbacks */ + ot->invoke = gpencil_primitive_invoke; + ot->modal = gpencil_primitive_modal; + ot->cancel = gpencil_primitive_cancel; + ot->poll = gpencil_primitive_add_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_BLOCKING; + + /* properties */ + RNA_def_int(ot->srna, "edges", 4, MIN_EDGES, MAX_EDGES, "Edges", "Number of polygon edges", MIN_EDGES, MAX_EDGES); + RNA_def_enum(ot->srna, "type", primitive_type, GP_STROKE_BOX, "Type", "Type of shape"); +} + +/* *************************************************************** */ diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c index dd556e99264..9386bfb3333 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_space_types.h" #include "DNA_screen_types.h" #include "DNA_object_types.h" @@ -62,6 +63,9 @@ #include "ED_gpencil.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "gpencil_intern.h" /* ********************************************** */ @@ -71,8 +75,8 @@ static bool gpencil_select_poll(bContext *C) { bGPdata *gpd = ED_gpencil_data_get_active(C); - /* we just need some visible strokes, and to be in editmode */ - if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { + /* we just need some visible strokes, and to be in editmode or other modes only to catch event */ + if (GPENCIL_ANY_MODE(gpd)) { /* TODO: include a check for visible strokes? */ if (gpd->layers.first) return true; @@ -94,6 +98,11 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + /* for "toggle", test for existing selected strokes */ if (action == SEL_TOGGLE) { action = SEL_SELECT; @@ -180,6 +189,11 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op) } /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -213,6 +227,11 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + /* select all points in selected strokes */ CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { @@ -228,6 +247,11 @@ static int gpencil_select_linked_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -248,6 +272,86 @@ void GPENCIL_OT_select_linked(wmOperatorType *ot) } /* ********************************************** */ +/* Select Alternate */ + +static int gpencil_select_alternate_exec(bContext *C, wmOperator *op) +{ + const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + + if (gpd == NULL) { + BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data"); + return OPERATOR_CANCELLED; + } + + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + + /* select all points in selected strokes */ + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) { + bGPDspoint *pt; + int row = 0; + int start = 0; + if (unselect_ends) { + start = 1; + } + + for (int i = start; i < gps->totpoints; i++) { + pt = &gps->points[i]; + if ((row % 2) == 0) { + pt->flag |= GP_SPOINT_SELECT; + } + else { + pt->flag &= ~GP_SPOINT_SELECT; + } + row++; + } + + /* unselect start and end points */ + if (unselect_ends) { + pt = &gps->points[0]; + pt->flag &= ~GP_SPOINT_SELECT; + + pt = &gps->points[gps->totpoints - 1]; + pt->flag &= ~GP_SPOINT_SELECT; + } + } + } + CTX_DATA_END; + + /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); + return OPERATOR_FINISHED; +} + +void GPENCIL_OT_select_alternate(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Alternated"; + ot->idname = "GPENCIL_OT_select_alternate"; + ot->description = "Select alternative points in same strokes as already selected points"; + + /* callbacks */ + ot->exec = gpencil_select_alternate_exec; + ot->poll = gpencil_select_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_boolean(ot->srna, "unselect_ends", true, "Unselect Ends", "Do not select the first and last point of the stroke"); +} + +/* ********************************************** */ /* Select Grouped */ typedef enum eGP_SelectGrouped { @@ -266,11 +370,12 @@ typedef enum eGP_SelectGrouped { /* On each visible layer, check for selected strokes - if found, select all others */ static void gp_select_same_layer(bContext *C) { - Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + int cfra_eval = (int)DEG_get_ctime(depsgraph); CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, CFRA, 0); + bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, 0); bGPDstroke *gps; bool found = false; @@ -309,10 +414,7 @@ static void gp_select_same_layer(bContext *C) /* Select all strokes with same colors as selected ones */ static void gp_select_same_color(bContext *C) { - /* First, build set containing all the colors of selected strokes - * - We use the palette names, so that we can select all strokes with one - * (potentially missing) color, and remap them to something else - */ + /* First, build set containing all the colors of selected strokes */ GSet *selected_colors = BLI_gset_str_new("GP Selected Colors"); CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) @@ -321,7 +423,7 @@ static void gp_select_same_color(bContext *C) /* add instead of insert here, otherwise the uniqueness check gets skipped, * and we get many duplicate entries... */ - BLI_gset_add(selected_colors, gps->colorname); + BLI_gset_add(selected_colors, &gps->mat_nr); } } CTX_DATA_END; @@ -329,7 +431,7 @@ static void gp_select_same_color(bContext *C) /* Second, select any visible stroke that uses these colors */ CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { - if (BLI_gset_haskey(selected_colors, gps->colorname)) { + if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) { /* select this stroke */ bGPDspoint *pt; int i; @@ -342,6 +444,11 @@ static void gp_select_same_color(bContext *C) } } CTX_DATA_END; + + /* free memomy */ + if (selected_colors != NULL) { + BLI_gset_free(selected_colors, NULL); + } } @@ -350,6 +457,11 @@ static void gp_select_same_color(bContext *C) static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) { eGP_SelectGrouped mode = RNA_enum_get(op->ptr, "type"); + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } switch (mode) { case GP_SEL_SAME_LAYER: @@ -365,6 +477,11 @@ static int gpencil_select_grouped_exec(bContext *C, wmOperator *op) } /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -399,6 +516,12 @@ void GPENCIL_OT_select_grouped(wmOperatorType *ot) static int gpencil_select_first_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); const bool extend = RNA_boolean_get(op->ptr, "extend"); @@ -429,6 +552,11 @@ static int gpencil_select_first_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -459,6 +587,12 @@ void GPENCIL_OT_select_first(wmOperatorType *ot) static int gpencil_select_last_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes"); const bool extend = RNA_boolean_get(op->ptr, "extend"); @@ -489,6 +623,11 @@ static int gpencil_select_last_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -519,6 +658,12 @@ void GPENCIL_OT_select_last(wmOperatorType *ot) static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { if (gps->flag & GP_STROKE_SELECT) { @@ -565,6 +710,11 @@ static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -589,6 +739,12 @@ void GPENCIL_OT_select_more(wmOperatorType *ot) static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op)) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) { if (gps->flag & GP_STROKE_SELECT) { @@ -636,6 +792,11 @@ static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op)) CTX_DATA_END; /* updates */ + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); return OPERATOR_FINISHED; } @@ -665,7 +826,7 @@ void GPENCIL_OT_select_less(wmOperatorType *ot) 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]) + const bool select, rcti *rect, float diff_mat[4][4]) { bGPDspoint *pt1, *pt2; int x0 = 0, y0 = 0, x1 = 0, y1 = 0; @@ -673,14 +834,9 @@ static bool gp_stroke_do_circle_sel( bool changed = false; if (gps->totpoints == 1) { - 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); - } + 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)) { @@ -708,18 +864,12 @@ static bool gp_stroke_do_circle_sel( /* get points to work with */ pt1 = gps->points + i; pt2 = gps->points + i + 1; - 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); + 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); - } + 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)) || @@ -763,6 +913,12 @@ static bool gp_stroke_do_circle_sel( static int gpencil_circle_select_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + /* if not edit/sculpt mode, the event is catched but not processed */ + if (GPENCIL_NONE_EDIT_MODE(gpd)) { + return OPERATOR_CANCELLED; + } + ScrArea *sa = CTX_wm_area(C); const int mx = RNA_int_get(op->ptr, "x"); @@ -798,13 +954,17 @@ static int gpencil_circle_select_exec(bContext *C, wmOperator *op) GP_EDITABLE_STROKES_BEGIN(C, gpl, gps) { changed |= gp_stroke_do_circle_sel( - gps, &gsc, mx, my, radius, select, &rect, - (gpl->parent != NULL), diff_mat); + gps, &gsc, mx, my, radius, select, &rect, diff_mat); } GP_EDITABLE_STROKES_END; /* updates */ if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } @@ -837,10 +997,11 @@ void GPENCIL_OT_select_circle(wmOperatorType *ot) static int gpencil_border_select_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); ScrArea *sa = CTX_wm_area(C); const bool select = !RNA_boolean_get(op->ptr, "deselect"); - const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool extend = RNA_boolean_get(op->ptr, "extend") && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0); GP_SpaceConversion gsc = {NULL}; rcti rect = {0}; @@ -888,14 +1049,9 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) int x0, y0; /* convert point coords to screenspace */ - 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); - } + 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)) { @@ -915,8 +1071,20 @@ static int gpencil_border_select_exec(bContext *C, wmOperator *op) } GP_EDITABLE_STROKES_END; + /* if paint mode,delete selected points */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + gp_delete_selected_point_wrap(C); + changed = true; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + /* updates */ if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } @@ -950,10 +1118,11 @@ void GPENCIL_OT_select_border(wmOperatorType *ot) static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) { + bGPdata *gpd = ED_gpencil_data_get_active(C); GP_SpaceConversion gsc = {NULL}; rcti rect = {0}; - const bool extend = RNA_boolean_get(op->ptr, "extend"); + const bool extend = RNA_boolean_get(op->ptr, "extend") && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0); const bool select = !RNA_boolean_get(op->ptr, "deselect"); int mcords_tot; @@ -997,14 +1166,9 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) int x0, y0; /* convert point coords to screenspace */ - 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); - } + 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)) @@ -1028,8 +1192,20 @@ static int gpencil_lasso_select_exec(bContext *C, wmOperator *op) /* cleanup */ MEM_freeN((void *)mcords); + /* if paint mode,delete selected points */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + gp_delete_selected_point_wrap(C); + changed = true; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + } + /* updates */ if (changed) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } @@ -1061,6 +1237,7 @@ void GPENCIL_OT_select_lasso(wmOperatorType *ot) static int gpencil_select_exec(bContext *C, wmOperator *op) { ScrArea *sa = CTX_wm_area(C); + bGPdata *gpd = ED_gpencil_data_get_active(C); /* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */ const float radius = 0.75f * U.widget_unit; @@ -1102,14 +1279,9 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { int xy[2]; - 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]); - } + 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])) { @@ -1197,6 +1369,11 @@ static int gpencil_select_exec(bContext *C, wmOperator *op) /* updates */ if (hit_point != NULL) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + + /* copy on write tag is needed, or else no refresh happens */ + DEG_id_tag_update(&gpd->id, DEG_TAG_COPY_ON_WRITE); + WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL); } diff --git a/source/blender/editors/gpencil/gpencil_undo.c b/source/blender/editors/gpencil/gpencil_undo.c index d35df8bc380..708d8f37e58 100644 --- a/source/blender/editors/gpencil/gpencil_undo.c +++ b/source/blender/editors/gpencil/gpencil_undo.c @@ -36,6 +36,7 @@ #include "MEM_guardedalloc.h" #include "DNA_gpencil_types.h" +#include "DNA_object_types.h" #include "DNA_listBase.h" #include "DNA_windowmanager_types.h" @@ -51,6 +52,8 @@ #include "WM_api.h" #include "WM_types.h" +#include "DEG_depsgraph.h" + #include "gpencil_intern.h" typedef struct bGPundonode { @@ -111,6 +114,9 @@ int ED_undo_gpencil_step(bContext *C, int step, const char *name) } } } + /* drawing batch cache is dirty now */ + DEG_id_tag_update(&new_gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + new_gpd->flag |= GP_DATA_CACHE_IS_DIRTY; } WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index 8b65855f7c4..cd352579b4a 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -40,7 +40,9 @@ #include "BLT_translation.h" #include "BLI_rand.h" +#include "DNA_meshdata_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -48,9 +50,13 @@ #include "DNA_view3d_types.h" #include "BKE_action.h" +#include "BKE_main.h" +#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_gpencil.h" -#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_material.h" #include "BKE_tracking.h" #include "WM_api.h" @@ -65,8 +71,14 @@ #include "ED_gpencil.h" #include "ED_clip.h" #include "ED_view3d.h" +#include "ED_object.h" +#include "ED_screen.h" + +#include "GPU_immediate.h" +#include "GPU_immediate_util.h" #include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" #include "gpencil_intern.h" @@ -76,7 +88,7 @@ /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it, * when context info is not available. */ -bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob, PointerRNA *ptr) +bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, ScrArea *sa, Scene *scene, Object *ob, PointerRNA *r_ptr) { /* if there's an active area, check if the particular editor may * have defined any special Grease Pencil context for editing... @@ -85,26 +97,37 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr SpaceLink *sl = sa->spacedata.first; switch (sa->spacetype) { - case SPACE_VIEW3D: /* 3D-View */ + /* XXX: Should we reduce reliance on context.gpencil_data for these cases? */ + case SPACE_BUTS: /* properties */ + case SPACE_INFO: /* header info (needed after workspaces merge) */ { - BLI_assert(scene && ELEM(scene->toolsettings->gpencil_src, - GP_TOOL_SOURCE_SCENE, GP_TOOL_SOURCE_OBJECT)); + if (ob && (ob->type == OB_GPENCIL)) { + /* GP Object */ + if (r_ptr) RNA_id_pointer_create(&ob->id, r_ptr); + return (bGPdata **)&ob->data; + } + else { + return NULL; + } - if (scene->toolsettings->gpencil_src == GP_TOOL_SOURCE_OBJECT) { - /* legacy behaviour for usage with old addons requiring object-linked to objects */ + break; + } - /* just in case no active/selected object... */ - if (ob && (ob->flag & SELECT)) { - /* for now, as long as there's an object, default to using that in 3D-View */ - if (ptr) RNA_id_pointer_create(&ob->id, ptr); - return &ob->gpd; - } - /* else: defaults to scene... */ + case SPACE_TOPBAR: /* Topbar (needed after topbar merge) */ + case SPACE_VIEW3D: /* 3D-View */ + { + if (ob && (ob->type == OB_GPENCIL)) { + /* GP Object */ + if (r_ptr) RNA_id_pointer_create(&ob->id, r_ptr); + return (bGPdata **)&ob->data; } else { - if (ptr) RNA_id_pointer_create(&scene->id, ptr); + /* Annotations */ + /* XXX: */ + if (r_ptr) RNA_id_pointer_create(&scene->id, r_ptr); return &scene->gpd; } + break; } case SPACE_NODE: /* Nodes Editor */ @@ -114,7 +137,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr /* return the GP data for the active node block/node */ if (snode && snode->nodetree) { /* for now, as long as there's an active node tree, default to using that in the Nodes Editor */ - if (ptr) RNA_id_pointer_create(&snode->nodetree->id, ptr); + if (r_ptr) RNA_id_pointer_create(&snode->nodetree->id, r_ptr); return &snode->nodetree->gpd; } @@ -127,7 +150,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr /* for now, Grease Pencil data is associated with the space (actually preview region only) */ /* XXX our convention for everything else is to link to data though... */ - if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, ptr); + if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceSequenceEditor, sseq, r_ptr); return &sseq->gpd; } case SPACE_IMAGE: /* Image/UV Editor */ @@ -136,7 +159,7 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr /* for now, Grease Pencil data is associated with the space... */ /* XXX our convention for everything else is to link to data though... */ - if (ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, ptr); + if (r_ptr) RNA_pointer_create(screen_id, &RNA_SpaceImageEditor, sima, r_ptr); return &sima->gpd; } case SPACE_CLIP: /* Nodes Editor */ @@ -151,15 +174,11 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr if (!track) return NULL; - if (ptr) - RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, ptr); - + if (r_ptr) RNA_pointer_create(&clip->id, &RNA_MovieTrackingTrack, track, r_ptr); return &track->gpd; } else { - if (ptr) - RNA_id_pointer_create(&clip->id, ptr); - + if (r_ptr) RNA_id_pointer_create(&clip->id, r_ptr); return &clip->gpd; } } @@ -170,79 +189,102 @@ bGPdata **ED_gpencil_data_get_pointers_direct(ID *screen_id, Scene *scene, ScrAr } } - /* just fall back on the scene's GP data */ - if (ptr) RNA_id_pointer_create((ID *)scene, ptr); - return (scene) ? &scene->gpd : NULL; + return NULL; } /* Get pointer to active Grease Pencil datablock, and an RNA-pointer to trace back to whatever owns it */ -bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *ptr) +bGPdata **ED_gpencil_data_get_pointers(const bContext *C, PointerRNA *r_ptr) { ID *screen_id = (ID *)CTX_wm_screen(C); Scene *scene = CTX_data_scene(C); ScrArea *sa = CTX_wm_area(C); Object *ob = CTX_data_active_object(C); - return ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, ptr); + return ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, r_ptr); } /* -------------------------------------------------------- */ /* Get the active Grease Pencil datablock, when context is not available */ -bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, Scene *scene, ScrArea *sa, Object *ob) +bGPdata *ED_gpencil_data_get_active_direct(ID *screen_id, ScrArea *sa, Scene *scene, Object *ob) { - bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, scene, sa, ob, NULL); + bGPdata **gpd_ptr = ED_gpencil_data_get_pointers_direct(screen_id, sa, scene, ob, NULL); return (gpd_ptr) ? *(gpd_ptr) : NULL; } -/* Get the active Grease Pencil datablock */ +/** + * Get the active Grease Pencil datablock + * \note This is the original (bmain) copy of the datablock, stored in files. + * Do not use for reading evaluated copies of GP Objects data + */ bGPdata *ED_gpencil_data_get_active(const bContext *C) { bGPdata **gpd_ptr = ED_gpencil_data_get_pointers(C, NULL); return (gpd_ptr) ? *(gpd_ptr) : NULL; } +/** + * Get the evaluated copy of the active Grease Pencil datablock (where applicable) + * - For the 3D View (i.e. "GP Objects"), this gives the evaluated copy of the GP datablock + * (i.e. a copy of the active GP datablock for the active object, where modifiers have been + * applied). This is needed to correctly work with "Copy-on-Write" + * - For all other editors (i.e. "GP Annotations"), this just gives the active datablock + * like for ED_gpencil_data_get_active() + */ +bGPdata *ED_gpencil_data_get_active_evaluated(const bContext *C) +{ + ID *screen_id = (ID *)CTX_wm_screen(C); + ScrArea *sa = CTX_wm_area(C); + + const Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene_eval = DEG_get_evaluated_scene(depsgraph); + Object *ob = CTX_data_active_object(C); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + + /* if (ob && ob->type == OB_GPENCIL) BLI_assert(ob_eval->data == DEG_get_evaluated_id(ob->data)); */ + return ED_gpencil_data_get_active_direct(screen_id, sa, scene_eval, ob_eval); +} + +/* -------------------------------------------------------- */ + +/** + * Utility to check whether the r_ptr output of ED_gpencil_data_get_pointers() + * is for annotation usage. + */ +bool ED_gpencil_data_owner_is_annotation(PointerRNA *owner_ptr) +{ + /* Key Assumption: If the pointer is an object, we're dealing with a GP Object's data. + * Otherwise, the GP datablock is being used for annotations (i.e. everywhere else) + */ + return ((owner_ptr) && (owner_ptr->type != &RNA_Object)); +} + /* -------------------------------------------------------- */ // XXX: this should be removed... We really shouldn't duplicate logic like this! -bGPdata *ED_gpencil_data_get_active_v3d(Scene *scene, ViewLayer *view_layer) +bGPdata *ED_gpencil_data_get_active_v3d(ViewLayer *view_layer) { Base *base = view_layer->basact; bGPdata *gpd = NULL; + /* We have to make sure active object is actually visible and selected, else we must use default scene gpd, * to be consistent with ED_gpencil_data_get_active's behavior. */ - if (base && TESTBASE(base)) { - gpd = base->object->gpd; + if (base->object->type == OB_GPENCIL) + gpd = base->object->data; } - return gpd ? gpd : scene->gpd; + return gpd ? gpd : NULL; } /* ******************************************************** */ /* Keyframe Indicator Checks */ /* Check whether there's an active GP keyframe on the current frame */ -bool ED_gpencil_has_keyframe_v3d(Scene *scene, Object *ob, int cfra) +bool ED_gpencil_has_keyframe_v3d(Scene *UNUSED(scene), Object *ob, int cfra) { - /* just check both for now... */ - // XXX: this could get confusing (e.g. if only on the object, but other places don't show this) - if (scene->gpd) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(scene->gpd); - if (gpl) { - if (gpl->actframe) { - // XXX: assumes that frame has been fetched already - return (gpl->actframe->framenum == cfra); - } - else { - /* XXX: disabled as could be too much of a penalty */ - /* return BKE_gpencil_layer_find_frame(gpl, cfra); */ - } - } - } - - if (ob && ob->gpd) { - bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->gpd); + if (ob && ob->data && (ob->type == OB_GPENCIL)) { + bGPDlayer *gpl = BKE_gpencil_layer_getactive(ob->data); if (gpl) { if (gpl->actframe) { // XXX: assumes that frame has been fetched already @@ -281,28 +323,13 @@ bool gp_active_layer_poll(bContext *C) bool gp_active_brush_poll(bContext *C) { ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush = BKE_gpencil_brush_getactive(ts); - - return (brush != NULL); -} - -/* poll callback for checking if there is an active palette */ -bool gp_active_palette_poll(bContext *C) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - - return (palette != NULL); -} - -/* poll callback for checking if there is an active palette color */ -bool gp_active_palettecolor_poll(bContext *C) -{ - bGPdata *gpd = ED_gpencil_data_get_active(C); - bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd); - bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette); - - return (palcolor != NULL); + Paint *paint = &ts->gp_paint->paint; + if (paint) { + return (paint->brush != NULL); + } + else { + return false; + } } /* ******************************************************** */ @@ -360,7 +387,7 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( /* Create new layer */ /* TODO: have some way of specifying that we don't want this? */ { - /* active Keying Set */ + /* "New Layer" entry */ item_tmp.identifier = "__CREATE__"; item_tmp.name = "New Layer"; item_tmp.value = -1; @@ -392,7 +419,6 @@ const EnumPropertyItem *ED_gpencil_layers_with_new_enum_itemf( } - /* ******************************************************** */ /* Brush Tool Core */ @@ -426,7 +452,7 @@ bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]), /* Stroke Validity Testing */ /* Check whether given stroke can be edited given the supplied context */ -// XXX: do we need additional flags for screenspace vs dataspace? +/* TODO: do we need additional flags for screenspace vs dataspace? */ bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) { /* sanity check */ @@ -436,7 +462,7 @@ bool ED_gpencil_stroke_can_use_direct(const ScrArea *sa, const bGPDstroke *gps) /* filter stroke types by flags + spacetype */ if (gps->flag & GP_STROKE_3DSPACE) { /* 3D strokes - only in 3D view */ - return (sa->spacetype == SPACE_VIEW3D); + return ((sa->spacetype == SPACE_VIEW3D) || (sa->spacetype == SPACE_BUTS)); } else if (gps->flag & GP_STROKE_2DIMAGE) { /* Special "image" strokes - only in Image Editor */ @@ -460,59 +486,21 @@ bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps) } /* Check whether given stroke can be edited for the current color */ -bool ED_gpencil_stroke_color_use(const bGPDlayer *gpl, const bGPDstroke *gps) +bool ED_gpencil_stroke_color_use(Object *ob, 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) + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + + if (gp_style != NULL) { + if (gp_style->flag & GP_STYLE_COLOR_HIDE) return false; - if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (palcolor->flag & PC_COLOR_LOCKED)) + if (((gpl->flag & GP_LAYER_UNLOCK_COLOR) == 0) && (gp_style->flag & GP_STYLE_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 = BKE_gpencil_palette_getactive(gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(gpd, DATA_("GP_Palette"), true); - } - /* get color */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, gps->colorname); - if (palcolor == NULL) { - if (gps->palcolor == NULL) { - palcolor = BKE_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 = BKE_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 */ @@ -573,9 +561,9 @@ void gp_point_to_parent_space(bGPDspoint *pt, float diff_mat[4][4], bGPDspoint * } /** - * Change points position relative to parent object + * Change position relative to parent object */ -void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) +void gp_apply_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps) { bGPDspoint *pt; int i; @@ -585,7 +573,7 @@ void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) float inverse_diff_mat[4][4]; float fpt[3]; - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); for (i = 0; i < gps->totpoints; i++) { @@ -598,14 +586,14 @@ void gp_apply_parent(bGPDlayer *gpl, bGPDstroke *gps) /** * Change point position relative to parent object */ -void gp_apply_parent_point(bGPDlayer *gpl, bGPDspoint *pt) +void gp_apply_parent_point(Depsgraph *depsgraph, Object *obact, bGPdata *gpd, 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); + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); invert_m4_m4(inverse_diff_mat, diff_mat); mul_v3_m4v3(fpt, inverse_diff_mat, &pt->x); @@ -770,194 +758,259 @@ bool gp_point_xy_to_3d(GP_SpaceConversion *gsc, Scene *scene, const float screen } /** - * Apply smooth to stroke point - * \param gps Stroke to smooth - * \param i Point index - * \param inf Amount of smoothing to apply - * \param affect_pressure Apply smoothing to pressure values too? + * Convert tGPspoint (temporary 2D/screenspace point data used by GP modal operators) + * to 3D coordinates. + * + * \param point2D: The screenspace 2D point data to convert + * \param depth: Depth array (via ED_view3d_autodist_depth()) + * \param[out] r_out: The resulting 2D point data */ -bool gp_smooth_stroke(bGPDstroke *gps, int i, float inf, bool affect_pressure) +void gp_stroke_convertcoords_tpoint( + Scene *scene, ARegion *ar, View3D *v3d, + Object *ob, bGPDlayer *gpl, + const tGPspoint *point2D, float *depth, + float r_out[3]) { - bGPDspoint *pt = &gps->points[i]; - float pressure = 0.0f; - float sco[3] = {0.0f}; + ToolSettings *ts = scene->toolsettings; + const int mval[2] = {point2D->x, point2D->y}; - /* Do nothing if not enough points to smooth out */ - if (gps->totpoints <= 2) { - return false; + if ((depth != NULL) && (ED_view3d_autodist_simple(ar, mval, r_out, 0, depth))) { + /* projecting onto 3D-Geometry + * - nothing more needs to be done here, since view_autodist_simple() has already done it + */ } - - /* Only affect endpoints by a fraction of the normal strength, - * to prevent the stroke from shrinking too much - */ - if ((i == 0) || (i == gps->totpoints - 1)) { - inf *= 0.1f; - } - - /* Compute smoothed coordinate by taking the ones nearby */ - /* XXX: This is potentially slow, and suffers from accumulation error as earlier points are handled before later ones */ - { - // XXX: this is hardcoded to look at 2 points on either side of the current one (i.e. 5 items total) - const int steps = 2; - const float average_fac = 1.0f / (float)(steps * 2 + 1); - int step; - - /* add the point itself */ - madd_v3_v3fl(sco, &pt->x, average_fac); - - if (affect_pressure) { - pressure += pt->pressure * average_fac; + else { + float mval_f[2] = {(float)point2D->x, (float)point2D->y}; + float mval_prj[2]; + float rvec[3], dvec[3]; + float zfac; + + /* Current method just converts each point in screen-coordinates to + * 3D-coordinates using the 3D-cursor as reference. + */ + ED_gp_get_drawing_reference(v3d, scene, ob, gpl, ts->gpencil_v3d_align, rvec); + zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); + sub_v3_v3v3(r_out, rvec, dvec); + } + else { + zero_v3(r_out); } + } +} - /* n-steps before/after current point */ - // XXX: review how the endpoints are treated by this algorithm - // XXX: falloff measures should also introduce some weighting variations, so that further-out points get less weight - for (step = 1; step <= steps; step++) { - bGPDspoint *pt1, *pt2; - int before = i - step; - int after = i + step; - - CLAMP_MIN(before, 0); - CLAMP_MAX(after, gps->totpoints - 1); - - pt1 = &gps->points[before]; - pt2 = &gps->points[after]; - - /* add both these points to the average-sum (s += p[i]/n) */ - 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; +/** + * Get drawing reference point for conversion or projection of the stroke + * \param[out] r_vec : Reference point found + */ +void ED_gp_get_drawing_reference( + View3D *v3d, Scene *scene, Object *ob, bGPDlayer *UNUSED(gpl), + char align_flag, float r_vec[3]) +{ + const float *fp = ED_view3d_cursor3d_get(scene, v3d)->location; + + /* if using a gpencil object at cursor mode, can use the location of the object */ + if (align_flag & GP_PROJECT_VIEWSPACE) { + if (ob && (ob->type == OB_GPENCIL)) { + /* fallback (no strokes) - use cursor or object location */ + if (align_flag & GP_PROJECT_CURSOR) { + /* use 3D-cursor */ + copy_v3_v3(r_vec, fp); + } + else { + /* use object location */ + copy_v3_v3(r_vec, ob->obmat[3]); } -#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; + else { + /* use 3D-cursor */ + copy_v3_v3(r_vec, fp); } -#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) + * Reproject all points of the stroke to a plane locked to axis to avoid stroke offset + */ +void ED_gp_project_stroke_to_plane(Object *ob, RegionView3D *rv3d, bGPDstroke *gps, const float origin[3], const int axis) { - bGPDspoint *ptb = &gps->points[i]; - - /* Do nothing if not enough points */ - if (gps->totpoints <= 2) { - return false; + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + if (axis < 0) { + /* if the axis is not locked, need a vector to the view direction + * in order to get the right size of the stroke. + */ + ED_view3d_global_to_vector(rv3d, origin, plane_normal); + } + else { + plane_normal[axis] = 1.0f; + /* if object, apply object rotation */ + if (ob && (ob->type == OB_GPENCIL)) { + mul_mat3_m4_v3(ob->obmat, plane_normal); + } } - /* 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]; + /* Reproject the points in the plane */ + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; - /* 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; + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); - /* Based on influence factor, blend between original and optimal */ - ptb->strength = (1.0f - inf) * ptb->strength + inf * optimal; + /* calculate line extreme point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); - return true; + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } + } } /** -* 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) + * Reproject given point to a plane locked to axis to avoid stroke offset + * \param[in, out] pt : Point to affect + */ +void ED_gp_project_point_to_plane(Object *ob, RegionView3D *rv3d, const float origin[3], const int axis, bGPDspoint *pt) { - bGPDspoint *ptb = &gps->points[i]; - - /* Do nothing if not enough points */ - if (gps->totpoints <= 2) { - return false; + float plane_normal[3]; + float vn[3]; + + float ray[3]; + float rpoint[3]; + + /* normal vector for a plane locked to axis */ + zero_v3(plane_normal); + if (axis < 0) { + /* if the axis is not locked, need a vector to the view direction + * in order to get the right size of the stroke. + */ + ED_view3d_global_to_vector(rv3d, origin, plane_normal); + } + else { + plane_normal[axis] = 1.0f; + /* if object, apply object rotation */ + if (ob && (ob->type == OB_GPENCIL)) { + mul_mat3_m4_v3(ob->obmat, plane_normal); + } } - /* 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; + /* Reproject the points in the plane */ + /* get a vector from the point with the current view direction of the viewport */ + ED_view3d_global_to_vector(rv3d, &pt->x, vn); - /* Based on influence factor, blend between original and optimal */ - ptb->pressure = (1.0f - inf) * ptb->pressure + inf * optimal; + /* calculate line extrem point to create a ray that cross the plane */ + mul_v3_fl(vn, -50.0f); + add_v3_v3v3(ray, &pt->x, vn); - return true; + /* if the line never intersect, the point is not changed */ + if (isect_line_plane_v3(rpoint, &pt->x, ray, origin, plane_normal)) { + copy_v3_v3(&pt->x, rpoint); + } } +/* ******************************************************** */ +/* Stroke Operations */ +// XXX: Check if these functions duplicate stuff in blenkernel, and/or whether we should just deduplicate + /** * 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) + * \param subdivide Number of times to subdivide */ -void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) +void gp_subdivide_stroke(bGPDstroke *gps, const int subdivide) { - /* Move points towards end of enlarged points array to leave space for new points */ - int y = 1; - for (int i = gps->totpoints - 1; i > 0; i--) { - gps->points[new_totpoints - y] = gps->points[i]; - y += 2; - } + bGPDspoint *temp_points; + int totnewpoints, oldtotpoints; + int i2; + + /* loop as many times as levels */ + for (int s = 0; s < subdivide; s++) { + totnewpoints = gps->totpoints - 1; + /* duplicate points in a temp area */ + temp_points = MEM_dupallocN(gps->points); + oldtotpoints = gps->totpoints; + + /* resize the points arrys */ + gps->totpoints += totnewpoints; + gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints); + gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints); + gps->flag |= GP_STROKE_RECALC_CACHES; + + /* move points from last to first to new place */ + i2 = gps->totpoints - 1; + for (int i = oldtotpoints - 1; i > 0; i--) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert = &gps->dvert[i]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + copy_v3_v3(&pt_final->x, &pt->x); + pt_final->pressure = pt->pressure; + pt_final->strength = pt->strength; + pt_final->time = pt->time; + pt_final->flag = pt->flag; + pt_final->uv_fac = pt->uv_fac; + pt_final->uv_rot = pt->uv_rot; + + dvert_final->totweight = dvert->totweight; + dvert_final->dw = dvert->dw; + + i2 -= 2; + } + /* interpolate mid points */ + i2 = 1; + for (int i = 0; i < oldtotpoints - 1; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i2]; + MDeformVert *dvert_final = &gps->dvert[i2]; + + /* add a half way point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f); + pt_final->strength = interpf(pt->strength, next->strength, 0.5f); + CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f); + pt_final->time = interpf(pt->time, next->time, 0.5f); + pt_final->uv_fac = interpf(pt->uv_fac, next->uv_fac, 0.5f); + pt_final->uv_rot = interpf(pt->uv_rot, next->uv_rot, 0.5f); + + dvert_final->totweight = 0; + dvert_final->dw = NULL; + + i2 += 2; + } - /* Create interpolated points */ - for (int i = 0; i < new_totpoints - 1; i += 2) { - bGPDspoint *prev = &gps->points[i]; - bGPDspoint *pt = &gps->points[i + 1]; - bGPDspoint *next = &gps->points[i + 2]; + MEM_SAFE_FREE(temp_points); - /* Interpolate all values */ - interp_v3_v3v3(&pt->x, &prev->x, &next->x, 0.5f); + /* move points to smooth stroke */ + /* duplicate points in a temp area with the new subdivide data */ + temp_points = MEM_dupallocN(gps->points); - pt->pressure = interpf(prev->pressure, next->pressure, 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); - } + /* extreme points are not changed */ + for (int i = 0; i < gps->totpoints - 2; i++) { + bGPDspoint *pt = &temp_points[i]; + bGPDspoint *next = &temp_points[i + 1]; + bGPDspoint *pt_final = &gps->points[i + 1]; - /* Update to new total number of points */ - gps->totpoints = new_totpoints; + /* move point */ + interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f); + } + /* free temp memory */ + MEM_SAFE_FREE(temp_points); + } } /** @@ -965,7 +1018,7 @@ void gp_subdivide_stroke(bGPDstroke *gps, const int new_totpoints) * \param gps Stroke data * \param brush Brush data */ -void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, RNG *rng) +void gp_randomize_stroke(bGPDstroke *gps, Brush *brush, RNG *rng) { bGPDspoint *pt1, *pt2, *pt3; float v1[3]; @@ -995,10 +1048,10 @@ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, RNG *rng) 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) { + 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_rng_get_float(rng) * (brush->draw_random_sub / 10.0f); + const float fac = BLI_rng_get_float(rng) * (brush->gpencil_settings->draw_random_sub / 10.0f); float svec[3]; copy_v3_v3(svec, ortho); if (BLI_rng_get_float(rng) > 0.5f) { @@ -1011,31 +1064,46 @@ void gp_randomize_stroke(bGPDstroke *gps, bGPDbrush *brush, RNG *rng) /* apply shift */ add_v3_v3(&pt->x, svec); } - } + +/* ******************************************************** */ +/* Layer Parenting - Compute Parent Transforms */ + /* calculate difference matrix */ -void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) +void ED_gpencil_parent_location( + const Depsgraph *depsgraph, Object *obact, bGPdata *UNUSED(gpd), + bGPDlayer *gpl, float diff_mat[4][4]) { - Object *ob = gpl->parent; - - if (ob == NULL) { + Object *ob_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obact) : obact; + Object *obparent = gpl->parent; + Object *obparent_eval = depsgraph != NULL ? DEG_get_evaluated_object(depsgraph, obparent) : obparent; + + /* if not layer parented, try with object parented */ + if (obparent_eval == NULL) { + if (ob_eval != NULL) { + if (ob_eval->type == OB_GPENCIL) { + copy_m4_m4(diff_mat, ob_eval->obmat); + return; + } + } + /* not gpencil object */ unit_m4(diff_mat); return; } else { if ((gpl->partype == PAROBJECT) || (gpl->partype == PARSKEL)) { - mul_m4_m4m4(diff_mat, ob->obmat, gpl->inverse); + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); return; } else if (gpl->partype == PARBONE) { - bPoseChannel *pchan = BKE_pose_channel_find_name(ob->pose, gpl->parsubstr); + bPoseChannel *pchan = BKE_pose_channel_find_name(obparent_eval->pose, gpl->parsubstr); if (pchan) { float tmp_mat[4][4]; - mul_m4_m4m4(tmp_mat, ob->obmat, pchan->pose_mat); + mul_m4_m4m4(tmp_mat, obparent_eval->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) */ + mul_m4_m4m4(diff_mat, obparent_eval->obmat, gpl->inverse); /* if bone not found use object (armature) */ } return; } @@ -1046,7 +1114,7 @@ void ED_gpencil_parent_location(bGPDlayer *gpl, float diff_mat[4][4]) } /* reset parent matrix for all layers */ -void ED_gpencil_reset_layers_parent(bGPdata *gpd) +void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd) { bGPDspoint *pt; int i; @@ -1071,7 +1139,7 @@ void ED_gpencil_reset_layers_parent(bGPdata *gpd) /* only redo if any change */ if (!equals_m4m4(gpl->inverse, cur_mat)) { /* first apply current transformation to all strokes */ - ED_gpencil_parent_location(gpl, diff_mat); + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { @@ -1086,90 +1154,582 @@ void ED_gpencil_reset_layers_parent(bGPdata *gpd) } } /* ******************************************************** */ -bool ED_gpencil_stroke_minmax( - const bGPDstroke *gps, const bool use_select, - float r_min[3], float r_max[3]) +/* GP Object Stuff */ + +/* Helper function to create new OB_GPENCIL Object */ +Object *ED_add_gpencil_object(bContext *C, Scene *scene, const float loc[3]) { - const bGPDspoint *pt; - int i; - bool changed = false; + float rot[3] = {0.0f}; + + Object *ob = ED_object_add_type(C, OB_GPENCIL, NULL, loc, rot, false, scene->lay); - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if ((use_select == false) || (pt->flag & GP_SPOINT_SELECT)) {; - minmax_v3v3_v3(r_min, r_max, &pt->x); - changed = true; + /* define size */ + BKE_object_obdata_size_init(ob, GP_OBGPENCIL_DEFAULT_SIZE); + /* create default brushes and colors */ + ED_gpencil_add_defaults(C); + + return ob; +} + +/* Helper function to create default colors and drawing brushes */ +void ED_gpencil_add_defaults(bContext *C) +{ + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + /* first try to reuse default material */ + if (ob->actcol > 0) { + Material *ma = give_current_material(ob, ob->actcol); + if ((ma) && (ma->gp_style == NULL)) { + BKE_material_init_gpencil_settings(ma); } } - return changed; + + /* ensure color exist */ + BKE_gpencil_material_ensure(bmain, ob); + + Paint *paint = BKE_brush_get_gpencil_paint(ts); + /* if not exist, create a new one */ + if (paint->brush == NULL) { + /* create new brushes */ + BKE_brush_gpencil_presets(C); + } + } -/* Dynamic Enums of GP Brushes */ -const EnumPropertyItem *ED_gpencil_brushes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free) +/* ******************************************************** */ +/* Vertex Groups */ + +/* assign points to vertex group */ +void ED_gpencil_vgroup_assign(bContext *C, Object *ob, float weight) { - ToolSettings *ts = CTX_data_tool_settings(C); - bGPDbrush *brush; - EnumPropertyItem *item = NULL, item_tmp = { 0 }; - int totitem = 0; - int i = 0; + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; - if (ELEM(NULL, C, ts)) { - return DummyRNA_DEFAULT_items; + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + if (gps->flag & GP_STROKE_SELECT) { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + if (pt->flag & GP_SPOINT_SELECT) { + BKE_gpencil_vgroup_add_point_weight(dvert, def_nr, weight); + } + } + } } + CTX_DATA_END; +} - /* 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; +/* remove points from vertex group */ +void ED_gpencil_vgroup_remove(bContext *C, Object *ob) +{ + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; - if (brush->flag & GP_BRUSH_ACTIVE) - item_tmp.icon = ICON_BRUSH_DATA; - else - item_tmp.icon = ICON_NONE; + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; - RNA_enum_item_add(&item, &totitem, &item_tmp); + if ((pt->flag & GP_SPOINT_SELECT) && (dvert->totweight > 0)) { + BKE_gpencil_vgroup_remove_point_weight(dvert, def_nr); + } + } } + CTX_DATA_END; +} - RNA_enum_item_end(&item, &totitem); - *r_free = true; +/* select points of vertex group */ +void ED_gpencil_vgroup_select(bContext *C, Object *ob) +{ + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; - return item; + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + if (BKE_gpencil_vgroup_use_index(dvert, def_nr) > -1.0f) { + pt->flag |= GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + } + } + } + CTX_DATA_END; } -/* Dynamic Enums of GP Palettes */ -const EnumPropertyItem *ED_gpencil_palettes_enum_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), - bool *r_free) +/* unselect points of vertex group */ +void ED_gpencil_vgroup_deselect(bContext *C, Object *ob) { - bGPdata *gpd = CTX_data_gpencil_data(C); - bGPDpalette *palette; - EnumPropertyItem *item = NULL, item_tmp = { 0 }; - int totitem = 0; - int i = 0; + const int def_nr = ob->actdef - 1; + if (!BLI_findlink(&ob->defbase, def_nr)) + return; - if (ELEM(NULL, C, gpd)) { - return DummyRNA_DEFAULT_items; + CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes) + { + for (int i = 0; i < gps->totpoints; i++) { + bGPDspoint *pt = &gps->points[i]; + MDeformVert *dvert = &gps->dvert[i]; + + if (BKE_gpencil_vgroup_use_index(dvert, def_nr) > -1.0f) { + pt->flag &= ~GP_SPOINT_SELECT; + gps->flag |= GP_STROKE_SELECT; + } + } } + CTX_DATA_END; +} - /* 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; +/* ******************************************************** */ +/* Cursor drawing */ - if (palette->flag & PL_PALETTE_ACTIVE) - item_tmp.icon = ICON_COLOR; - else - item_tmp.icon = ICON_NONE; +/* check if cursor is in drawing region */ +static bool gp_check_cursor_region(bContext *C, int mval[2]) +{ + ARegion *ar = CTX_wm_region(C); + ScrArea *sa = CTX_wm_area(C); + /* TODO: add more spacetypes */ + if (!ELEM(sa->spacetype, SPACE_VIEW3D)) { + return false; + } + if ((ar) && (ar->regiontype != RGN_TYPE_WINDOW)) { + return false; + } + else if (ar) { + rcti region_rect; - RNA_enum_item_add(&item, &totitem, &item_tmp); + /* Perform bounds check using */ + ED_region_visible_rect(ar, ®ion_rect); + return BLI_rcti_isect_pt_v(®ion_rect, mval); + } + else { + return false; } +} - RNA_enum_item_end(&item, &totitem); - *r_free = true; +/* draw eraser cursor */ +void ED_gpencil_brush_draw_eraser(Brush *brush, int x, int y) +{ + short radius = (short)brush->size; - return item; + GPUVertFormat *format = immVertexFormat(); + const uint shdr_pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + immUniformColor4ub(255, 100, 100, 20); + imm_draw_circle_fill_2d(shdr_pos, x, y, radius, 40); + + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + glGetFloatv(GL_VIEWPORT, viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + + immUniformColor4f(1.0f, 0.39f, 0.39f, 0.78f); + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniform1f("dash_width", 12.0f); + immUniform1f("dash_factor", 0.5f); + + imm_draw_circle_wire_2d(shdr_pos, x, y, radius, + /* XXX Dashed shader gives bad results with sets of small segments currently, + * temp hack around the issue. :( */ + max_ii(8, radius / 2)); /* was fixed 40 */ + + immUnbindProgram(); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); +} + +/* Helper callback for drawing the cursor itself */ +static void gp_brush_drawcursor(bContext *C, int x, int y, void *customdata) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + ARegion *ar = CTX_wm_region(C); + + GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; + bGPdata *gpd = ED_gpencil_data_get_active(C); + GP_EditBrush_Data *brush = NULL; + Brush *paintbrush = NULL; + Material *ma = NULL; + MaterialGPencilStyle *gp_style = NULL; + int *last_mouse_position = customdata; + + if ((gpd) && (gpd->flag & GP_DATA_STROKE_WEIGHTMODE)) { + brush = &gset->brush[gset->weighttype]; + } + else { + brush = &gset->brush[gset->brushtype]; + } + + /* default radius and color */ + float color[3] = {1.0f, 1.0f, 1.0f}; + float darkcolor[3]; + float radius = 3.0f; + + int mval[2] = {x, y}; + /* check if cursor is in drawing region and has valid datablock */ + if ((!gp_check_cursor_region(C, mval)) || (gpd == NULL)) { + return; + } + + /* for paint use paint brush size and color */ + if (gpd->flag & GP_DATA_STROKE_PAINTMODE) { + paintbrush = BKE_brush_getactive_gpencil(scene->toolsettings); + /* while drawing hide */ + if ((gpd->runtime.sbuffer_size > 0) && + (paintbrush) && ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0)) + { + return; + } + + if (paintbrush) { + if ((paintbrush->gpencil_settings->flag & GP_BRUSH_ENABLE_CURSOR) == 0) { + return; + } + + /* eraser has special shape and use a different shader program */ + if (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_ERASE) { + ED_gpencil_brush_draw_eraser(paintbrush, x, y); + return; + } + + /* get current drawing color */ + ma = BKE_gpencil_get_material_from_brush(paintbrush); + if (ma == NULL) { + BKE_gpencil_material_ensure(bmain, ob); + /* assign the first material to the brush */ + ma = give_current_material(ob, 1); + paintbrush->gpencil_settings->material = ma; + } + gp_style = ma->gp_style; + + /* after some testing, display the size of the brush is not practical because + * is too disruptive and the size of cursor does not change with zoom factor. + * The decision was to use a fix size, instead of paintbrush->thickness value. + */ + if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && + (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)) + { + radius = 2.0f; + copy_v3_v3(color, gp_style->stroke_rgba); + } + else { + radius = 5.0f; + copy_v3_v3(color, paintbrush->add_col); + } + } + else { + return; + } + } + + /* for sculpt use sculpt brush size */ + if (GPENCIL_SCULPT_OR_WEIGHT_MODE(gpd)) { + if (brush) { + if ((brush->flag & GP_EDITBRUSH_FLAG_ENABLE_CURSOR) == 0) { + return; + } + + radius = brush->size; + if (brush->flag & (GP_EDITBRUSH_FLAG_INVERT | GP_EDITBRUSH_FLAG_TMP_INVERT)) { + copy_v3_v3(color, brush->curcolor_sub); + } + else { + copy_v3_v3(color, brush->curcolor_add); + } + } + } + + /* draw icon */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + /* Inner Ring: Color from UI panel */ + immUniformColor4f(color[0], color[1], color[2], 0.8f); + if ((gp_style) && (GPENCIL_PAINT_MODE(gpd)) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE) == 0) && + ((paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP) == 0) && + (paintbrush->gpencil_settings->brush_type == GP_BRUSH_TYPE_DRAW)) + { + imm_draw_circle_fill_2d(pos, x, y, radius, 40); + } + else { + imm_draw_circle_wire_2d(pos, x, y, radius, 40); + } + + /* Outer Ring: Dark color for contrast on light backgrounds (e.g. gray on white) */ + mul_v3_v3fl(darkcolor, color, 0.40f); + immUniformColor4f(darkcolor[0], darkcolor[1], darkcolor[2], 0.8f); + imm_draw_circle_wire_2d(pos, x, y, radius + 1, 40); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + + /* Draw line for lazy mouse */ + if ((last_mouse_position) && + (paintbrush->gpencil_settings->flag & GP_BRUSH_STABILIZE_MOUSE_TEMP)) + { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + copy_v3_v3(color, paintbrush->add_col); + immUniformColor4f(color[0], color[1], color[2], 0.8f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, x, y); + immVertex2f( + pos, + last_mouse_position[0] + ar->winrct.xmin, + last_mouse_position[1] + ar->winrct.ymin); + immEnd(); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } + + immUnbindProgram(); +} + +/* Turn brush cursor in on/off */ +void ED_gpencil_toggle_brush_cursor(bContext *C, bool enable, void *customdata) +{ + Scene *scene = CTX_data_scene(C); + GP_BrushEdit_Settings *gset = &scene->toolsettings->gp_sculpt; + int *lastpost = customdata; + + if (gset->paintcursor && !enable) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); + gset->paintcursor = NULL; + } + else if (enable) { + /* in some situations cursor could be duplicated, so it is better disable first if exist */ + if (gset->paintcursor) { + /* clear cursor */ + WM_paint_cursor_end(CTX_wm_manager(C), gset->paintcursor); + gset->paintcursor = NULL; + } + /* enable cursor */ + gset->paintcursor = WM_paint_cursor_activate(CTX_wm_manager(C), + NULL, + gp_brush_drawcursor, + (lastpost) ? customdata : NULL); + } +} + +/* verify if is using the right brush */ +static void gpencil_verify_brush_type(bContext *C, int newmode) +{ + ToolSettings *ts = CTX_data_tool_settings(C); + GP_BrushEdit_Settings *gset = &ts->gp_sculpt; + + switch (newmode) { + case OB_MODE_GPENCIL_SCULPT: + gset->flag &= ~GP_BRUSHEDIT_FLAG_WEIGHT_MODE; + if ((gset->brushtype < 0) || (gset->brushtype >= GP_EDITBRUSH_TYPE_WEIGHT)) { + gset->brushtype = GP_EDITBRUSH_TYPE_PUSH; + } + break; + case OB_MODE_GPENCIL_WEIGHT: + gset->flag |= GP_BRUSHEDIT_FLAG_WEIGHT_MODE; + if ((gset->weighttype < GP_EDITBRUSH_TYPE_WEIGHT) || (gset->weighttype >= TOT_GP_EDITBRUSH_TYPES)) { + gset->weighttype = GP_EDITBRUSH_TYPE_WEIGHT; + } + break; + default: + break; + } +} + +/* set object modes */ +void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode) +{ + if (!gpd) { + return; + } + + switch (newmode) { + case OB_MODE_GPENCIL_EDIT: + gpd->flag |= GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, false, NULL); + break; + case OB_MODE_GPENCIL_PAINT: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag |= GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + case OB_MODE_GPENCIL_SCULPT: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag |= GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + gpencil_verify_brush_type(C, OB_MODE_GPENCIL_SCULPT); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + case OB_MODE_GPENCIL_WEIGHT: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag |= GP_DATA_STROKE_WEIGHTMODE; + gpencil_verify_brush_type(C, OB_MODE_GPENCIL_WEIGHT); + ED_gpencil_toggle_brush_cursor(C, true, NULL); + break; + default: + gpd->flag &= ~GP_DATA_STROKE_EDITMODE; + gpd->flag &= ~GP_DATA_STROKE_PAINTMODE; + gpd->flag &= ~GP_DATA_STROKE_SCULPTMODE; + gpd->flag &= ~GP_DATA_STROKE_WEIGHTMODE; + ED_gpencil_toggle_brush_cursor(C, false, NULL); + break; + } +} + +/* helper to convert 2d to 3d for simple drawing buffer */ +static void gpencil_stroke_convertcoords(ARegion *ar, const tGPspoint *point2D, float origin[3], float out[3]) +{ + float mval_f[2] = { (float)point2D->x, (float)point2D->y }; + float mval_prj[2]; + float rvec[3], dvec[3]; + float zfac; + + copy_v3_v3(rvec, origin); + + zfac = ED_view3d_calc_zfac(ar->regiondata, rvec, NULL); + + if (ED_view3d_project_float_global(ar, rvec, mval_prj, V3D_PROJ_TEST_NOP) == V3D_PROJ_RET_OK) { + sub_v2_v2v2(mval_f, mval_prj, mval_f); + ED_view3d_win_to_delta(ar, mval_f, dvec, zfac); + sub_v3_v3v3(out, rvec, dvec); + } + else { + zero_v3(out); + } +} + +/* convert 2d tGPspoint to 3d bGPDspoint */ +void ED_gpencil_tpoint_to_point(ARegion *ar, float origin[3], const tGPspoint *tpt, bGPDspoint *pt) +{ + float p3d[3]; + /* conversion to 3d format */ + gpencil_stroke_convertcoords(ar, tpt, origin, p3d); + copy_v3_v3(&pt->x, p3d); + + pt->pressure = tpt->pressure; + pt->strength = tpt->strength; + pt->uv_fac = tpt->uv_fac; + pt->uv_rot = tpt->uv_rot; +} + +/* texture coordinate utilities */ +void ED_gpencil_calc_stroke_uv(Object *ob, bGPDstroke *gps) +{ + if (gps == NULL) { + return; + } + MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1); + float pixsize; + if (gp_style) { + pixsize = gp_style->texture_pixsize / 1000000.0f; + } + else { + /* use this value by default */ + pixsize = 0.000100f; + } + pixsize = MAX2(pixsize, 0.0000001f); + + bGPDspoint *pt = NULL; + bGPDspoint *ptb = NULL; + int i; + float totlen = 0; + + /* first read all points and calc distance */ + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + /* first point */ + if (i == 0) { + pt->uv_fac = 0.0f; + continue; + } + + ptb = &gps->points[i - 1]; + totlen += len_v3v3(&pt->x, &ptb->x) / pixsize; + pt->uv_fac = totlen; + } + /* normalize the distance using a factor */ + float factor; + /* if image, use texture width */ + if ((gp_style) && (gp_style->sima)) { + factor = gp_style->sima->gen_x; + } + else { + factor = totlen; + } + for (i = 0; i < gps->totpoints; i++) { + pt = &gps->points[i]; + pt->uv_fac /= factor; + } +} + +/* recalc uv for any stroke using the material */ +void ED_gpencil_update_color_uv(Main *bmain, Material *mat) +{ + Material *gps_ma = NULL; + /* read all strokes */ + for (Object *ob = bmain->object.first; ob; ob = ob->id.next) { + if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + if (gpd == NULL) { + continue; + } + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* only editable and visible layers are considered */ + if (gpencil_layer_is_editable(gpl)) { + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) { + /* check if it is editable */ + if (ED_gpencil_stroke_color_use(ob, gpl, gps) == false) { + continue; + } + gps_ma = give_current_material(ob, gps->mat_nr + 1); + /* update */ + if ((gps_ma) && (gps_ma == mat)) { + ED_gpencil_calc_stroke_uv(ob, gps); + } + } + } + } + } + } + } } /* ******************************************************** */ diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 59a54f03e56..ae86e4a5fbf 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -197,6 +197,8 @@ typedef enum eAnim_ChannelType { ANIMTYPE_NLATRACK, ANIMTYPE_NLAACTION, + ANIMTYPE_PALETTE, + /* always as last item, the total number of channel types... */ ANIMTYPE_NUM_TYPES } eAnim_ChannelType; @@ -347,6 +349,9 @@ typedef enum eAnimFilter_Flags { /* Movie clip only */ #define EXPANDED_MCLIP(clip) (clip->flag & MCLIP_DATA_EXPAND) +/* Palette only */ +#define EXPANDED_PALETTE(palette) (palette->flag & PALETTE_DATA_EXPAND) + /* AnimData - NLA mostly... */ #define SEL_ANIMDATA(adt) (adt->flag & ADT_UI_SELECTED) diff --git a/source/blender/editors/include/ED_datafiles.h b/source/blender/editors/include/ED_datafiles.h index 7d509d1243a..333e3d72615 100644 --- a/source/blender/editors/include/ED_datafiles.h +++ b/source/blender/editors/include/ED_datafiles.h @@ -42,6 +42,9 @@ extern char datatoc_preview_blend[]; extern int datatoc_preview_cycles_blend_size; extern char datatoc_preview_cycles_blend[]; +extern int datatoc_preview_grease_pencil_blend_size; +extern char datatoc_preview_grease_pencil_blend[]; + extern int datatoc_blender_icons16_png_size; extern char datatoc_blender_icons16_png[]; @@ -239,6 +242,66 @@ extern char datatoc_mc23_jpg[]; extern int datatoc_mc24_jpg_size; extern char datatoc_mc24_jpg[]; +/* grease pencil sculpt brushes files */ + +extern int datatoc_gp_brush_smooth_png_size; +extern char datatoc_gp_brush_smooth_png[]; + +extern int datatoc_gp_brush_thickness_png_size; +extern char datatoc_gp_brush_thickness_png[]; + +extern int datatoc_gp_brush_strength_png_size; +extern char datatoc_gp_brush_strength_png[]; + +extern int datatoc_gp_brush_grab_png_size; +extern char datatoc_gp_brush_grab_png[]; + +extern int datatoc_gp_brush_push_png_size; +extern char datatoc_gp_brush_push_png[]; + +extern int datatoc_gp_brush_twist_png_size; +extern char datatoc_gp_brush_twist_png[]; + +extern int datatoc_gp_brush_pinch_png_size; +extern char datatoc_gp_brush_pinch_png[]; + +extern int datatoc_gp_brush_randomize_png_size; +extern char datatoc_gp_brush_randomize_png[]; + +extern int datatoc_gp_brush_clone_png_size; +extern char datatoc_gp_brush_clone_png[]; + +extern int datatoc_gp_brush_weight_png_size; +extern char datatoc_gp_brush_weight_png[]; + +extern int datatoc_gp_brush_pencil_png_size; +extern char datatoc_gp_brush_pencil_png[]; + +extern int datatoc_gp_brush_pen_png_size; +extern char datatoc_gp_brush_pen_png[]; + +extern int datatoc_gp_brush_ink_png_size; +extern char datatoc_gp_brush_ink_png[]; + +extern int datatoc_gp_brush_inknoise_png_size; +extern char datatoc_gp_brush_inknoise_png[]; + +extern int datatoc_gp_brush_block_png_size; +extern char datatoc_gp_brush_block_png[]; + +extern int datatoc_gp_brush_marker_png_size; +extern char datatoc_gp_brush_marker_png[]; + +extern int datatoc_gp_brush_fill_png_size; +extern char datatoc_gp_brush_fill_png[]; + +extern int datatoc_gp_brush_erase_soft_png_size; +extern char datatoc_gp_brush_erase_soft_png[]; + +extern int datatoc_gp_brush_erase_hard_png_size; +extern char datatoc_gp_brush_erase_hard_png[]; +extern int datatoc_gp_brush_erase_stroke_png_size; +extern char datatoc_gp_brush_erase_stroke_png[]; #endif /* __ED_DATAFILES_H__ */ diff --git a/source/blender/editors/include/ED_gpencil.h b/source/blender/editors/include/ED_gpencil.h index f1f2ce29e7f..3013b455de4 100644 --- a/source/blender/editors/include/ED_gpencil.h +++ b/source/blender/editors/include/ED_gpencil.h @@ -30,64 +30,44 @@ #ifndef __ED_GPENCIL_H__ #define __ED_GPENCIL_H__ -#include "ED_numinput.h" - struct ID; struct ListBase; -struct bContext; -struct Depsgraph; -struct ScrArea; -struct ARegion; -struct View3D; -struct Object; +struct PointerRNA; +struct rcti; + struct bGPdata; struct bGPDlayer; struct bGPDframe; struct bGPDstroke; -struct bGPDpalette; -struct bGPDpalettecolor; -struct bAnimContext; -struct KeyframeEditData; -struct PointerRNA; +struct bGPDspoint; +struct Brush; + +struct Main; +struct bContext; +struct EvaluationContext; +struct Depsgraph; +struct ScrArea; +struct ARegion; +struct RegionView3D; struct Scene; +struct ToolSettings; struct ViewLayer; -struct wmWindowManager; -struct wmKeyConfig; - - -/* ------------- Grease-Pencil Helpers ---------------- */ -typedef struct tGPDinterpolate_layer { - struct tGPDinterpolate_layer *next, *prev; - - struct bGPDlayer *gpl; /* layer */ - struct bGPDframe *prevFrame; /* frame before current frame (interpolate-from) */ - struct bGPDframe *nextFrame; /* frame after current frame (interpolate-to) */ - struct bGPDframe *interFrame; /* interpolated frame */ - float factor; /* interpolate factor */ +struct View3D; -} tGPDinterpolate_layer; +struct Object; +struct Material; -/* Temporary interpolate operation data */ -typedef struct tGPDinterpolate { - struct Scene *scene; /* current scene from context */ - struct ScrArea *sa; /* area where painting originated */ - struct ARegion *ar; /* region where painting originated */ - struct bGPdata *gpd; /* current GP datablock */ +struct bAnimContext; +struct KeyframeEditData; - int cframe; /* current frame number */ - ListBase ilayers; /* (tGPDinterpolate_layer) layers to be interpolated */ - float shift; /* value for determining the displacement influence */ - float init_factor; /* initial interpolation factor for active layer */ - float low_limit; /* shift low limit (-100%) */ - float high_limit; /* shift upper limit (200%) */ - int flag; /* flag from toolsettings */ +struct wmKeyConfig; +struct wmOperator; +struct wmWindow; +struct wmWindowManager; - NumInput num; /* numeric input */ - void *draw_handle_3d; /* handle for drawing strokes while operator is running 3d stuff */ - void *draw_handle_screen; /* handle for drawing strokes while operator is running screen stuff */ -} tGPDinterpolate; +/* ------------- Grease-Pencil Runtime Data ---------------- */ -/* Temporary 'Stroke Point' data +/* Temporary 'Stroke Point' data (2D / screen-space) * * Used as part of the 'stroke cache' used during drawing of new strokes */ @@ -96,27 +76,43 @@ typedef struct tGPspoint { 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) */ + float uv_fac; /* factor of uv along the stroke */ + float uv_rot; /* uv rotation for dor mode */ } tGPspoint; - -/* Check if 'sketching sessions' are enabled */ -#define GPENCIL_SKETCH_SESSIONS_ON(scene) ((scene)->toolsettings->gpencil_flags & GP_TOOL_FLAG_PAINTSESSIONS_ON) +/* used to sort by zdepth gpencil objects in viewport */ +/* TODO: this could be a system parameter in userprefs screen */ +#define GP_CACHE_BLOCK_SIZE 16 +typedef struct tGPencilSort { + struct Base *base; + float zdepth; +} tGPencilSort; /* ----------- Grease Pencil Tools/Context ------------- */ /* Context-dependent */ -struct bGPdata **ED_gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *ptr); +struct bGPdata **ED_gpencil_data_get_pointers(const struct bContext *C, struct PointerRNA *r_ptr); + struct bGPdata *ED_gpencil_data_get_active(const struct bContext *C); +struct bGPdata *ED_gpencil_data_get_active_evaluated(const struct bContext *C); /* Context independent (i.e. each required part is passed in instead) */ -struct bGPdata **ED_gpencil_data_get_pointers_direct(struct ID *screen_id, struct Scene *scene, - struct ScrArea *sa, struct Object *ob, - struct PointerRNA *ptr); -struct bGPdata *ED_gpencil_data_get_active_direct(struct ID *screen_id, struct Scene *scene, - struct ScrArea *sa, struct Object *ob); +struct bGPdata **ED_gpencil_data_get_pointers_direct( + struct ID *screen_id, + struct ScrArea *sa, + struct Scene *scene, + struct Object *ob, + struct PointerRNA *r_ptr); +struct bGPdata *ED_gpencil_data_get_active_direct( + struct ID *screen_id, + struct ScrArea *sa, + struct Scene *scene, + struct Object *ob); + +bool ED_gpencil_data_owner_is_annotation(struct PointerRNA *owner_ptr); /* 3D View */ -struct bGPdata *ED_gpencil_data_get_active_v3d(struct Scene *scene, struct ViewLayer *view_layer); +struct bGPdata *ED_gpencil_data_get_active_v3d(struct ViewLayer *view_layer); bool ED_gpencil_has_keyframe_v3d(struct Scene *scene, struct Object *ob, int cfra); @@ -124,13 +120,7 @@ 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, - float r_min[3], float r_max[3]); +bool ED_gpencil_stroke_color_use(struct Object *ob, const struct bGPDlayer *gpl, const struct bGPDstroke *gps); /* ----------- Grease Pencil Operators ----------------- */ @@ -150,16 +140,29 @@ void ED_gpencil_strokes_copybuf_free(void); void ED_gpencil_draw_2dimage(const struct bContext *C); void ED_gpencil_draw_view2d(const struct bContext *C, bool onlyv2d); -void ED_gpencil_draw_view3d(struct wmWindowManager *wm, - struct Scene *scene, - struct ViewLayer *view_layer, - struct Depsgraph *depsgraph, - struct View3D *v3d, - struct ARegion *ar, - bool only3d); -void ED_gpencil_draw_ex(struct Scene *scene, struct bGPdata *gpd, int winx, int winy, - const int cfra, const char spacetype); -void ED_gp_draw_interpolation(struct tGPDinterpolate *tgpi, const int type); +void ED_gpencil_draw_view3d( + struct wmWindowManager *wm, + struct Scene *scene, + struct ViewLayer *view_layer, + struct Depsgraph *depsgraph, + struct View3D *v3d, + struct ARegion *ar, + bool only3d); +void ED_gpencil_draw_view3d_annotations( + struct Scene *scene, struct Depsgraph *depsgraph, + struct View3D *v3d, struct ARegion *ar, + bool only3d); +void ED_gpencil_draw_view3d_object( + struct wmWindowManager *wm, + struct Scene *scene, + struct Depsgraph *depsgraph, + struct Object *ob, + struct View3D *v3d, + struct ARegion *ar, + bool only3d); +void ED_gpencil_draw_ex( + struct RegionView3D *rv3d, struct Scene *scene, struct bGPdata *gpd, int winx, int winy, + const int cfra, const char spacetype); /* ----------- Grease-Pencil AnimEdit API ------------------ */ bool ED_gplayer_frames_looper(struct bGPDlayer *gpl, struct Scene *scene, @@ -192,10 +195,45 @@ 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]); +/* get difference matrix */ +void ED_gpencil_parent_location( + const struct Depsgraph *depsgraph, struct Object *obact, struct bGPdata *gpd, + struct bGPDlayer *gpl, float diff_mat[4][4]); /* reset parent matrix for all layers */ -void ED_gpencil_reset_layers_parent(struct bGPdata *gpd); +void ED_gpencil_reset_layers_parent(struct Depsgraph *depsgraph, struct Object *obact, struct bGPdata *gpd); + +/* cursor utilities */ +void ED_gpencil_brush_draw_eraser(struct Brush *brush, int x, int y); + +/* ----------- Add Primitive Utilities -------------- */ + +void ED_gpencil_create_monkey(struct bContext *C, float mat[4][4]); + +/* ------------ Object Utilities ------------ */ +struct Object *ED_add_gpencil_object(struct bContext *C, struct Scene *scene, const float loc[3]); +void ED_gpencil_add_defaults(struct bContext *C); +/* set object modes */ +void ED_gpencil_setup_modes(struct bContext *C, struct bGPdata *gpd, int newmode); + +void ED_gp_project_stroke_to_plane(struct Object *ob, struct RegionView3D *rv3d, struct bGPDstroke *gps, const float origin[3], const int axis); +void ED_gp_project_point_to_plane(struct Object *ob, struct RegionView3D *rv3d, const float origin[3], const int axis, struct bGPDspoint *pt); +void ED_gp_get_drawing_reference(struct View3D *v3d, struct Scene *scene, struct Object *ob, struct bGPDlayer *gpl, char align_flag, float vec[3]); + +/* set sculpt cursor */ +void ED_gpencil_toggle_brush_cursor(struct bContext *C, bool enable, void *customdata); + +/* vertex groups */ +void ED_gpencil_vgroup_assign(struct bContext *C, struct Object *ob, float weight); +void ED_gpencil_vgroup_remove(struct bContext *C, struct Object *ob); +void ED_gpencil_vgroup_select(struct bContext *C, struct Object *ob); +void ED_gpencil_vgroup_deselect(struct bContext *C, struct Object *ob); + +/* join objects */ +int ED_gpencil_join_objects_exec(struct bContext *C, struct wmOperator *op); +/* texture coordinate utilities */ +void ED_gpencil_tpoint_to_point(struct ARegion *ar, float origin[3], const struct tGPspoint *tpt, struct bGPDspoint *pt); +void ED_gpencil_calc_stroke_uv(struct Object *ob, struct bGPDstroke *gps); +void ED_gpencil_update_color_uv(struct Main *bmain, struct Material *mat); #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 64dee410526..45a0680e0c1 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -42,6 +42,7 @@ struct bActionGroup; struct Object; struct ListBase; struct bGPDlayer; +struct Palette; struct MaskLayer; struct Scene; struct View2D; @@ -151,9 +152,11 @@ void scene_to_keylist(struct bDopeSheet *ads, struct Scene *sce, struct DLRBT_Tr /* DopeSheet Summary */ void summary_to_keylist(struct bAnimContext *ac, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Grease Pencil datablock summary */ -void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, struct DLRBT_Tree *keys); +void gpencil_to_keylist(struct bDopeSheet *ads, struct bGPdata *gpd, struct DLRBT_Tree *keys, const bool active); /* Grease Pencil Layer */ void gpl_to_keylist(struct bDopeSheet *ads, struct bGPDlayer *gpl, struct DLRBT_Tree *keys); +/* Palette */ +void palette_to_keylist(struct bDopeSheet *ads, struct Palette *palette, struct DLRBT_Tree *keys, struct DLRBT_Tree *blocks); /* Mask */ void mask_to_keylist(struct bDopeSheet *UNUSED(ads), struct MaskLayer *masklay, struct DLRBT_Tree *keys); diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 38545137740..a39523983ba 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -42,6 +42,7 @@ struct ID; struct Main; struct Menu; struct ModifierData; +struct ShaderFxData; struct Object; struct ReportList; struct Scene; @@ -112,6 +113,7 @@ void ED_keymap_proportional_obmode(struct wmKeyConfig *keyconf, struct wmKeyMap void ED_keymap_proportional_maskmode(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap); void ED_keymap_proportional_editmode(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap, const bool do_connected); +void ED_keymap_editmesh_elem_mode(struct wmKeyConfig *keyconf, struct wmKeyMap *keymap); void ED_object_base_select(struct Base *base, eObjectSelect_Mode mode); void ED_object_base_activate(struct bContext *C, struct Base *base); @@ -260,6 +262,40 @@ bool ED_object_iter_other( bool ED_object_multires_update_totlevels_cb(struct Object *ob, void *totlevel_v); + +/* object_greasepencil_modifier.c */ +struct GpencilModifierData *ED_object_gpencil_modifier_add( + struct ReportList *reports, struct Main *bmain, struct Scene *scene, + struct Object *ob, const char *name, int type); +bool ED_object_gpencil_modifier_remove( + struct ReportList *reports, struct Main *bmain, + struct Object *ob, struct GpencilModifierData *md); +void ED_object_gpencil_modifier_clear( + struct Main *bmain, struct Object *ob); +int ED_object_gpencil_modifier_move_down( + struct ReportList *reports, struct Object *ob, struct GpencilModifierData *md); +int ED_object_gpencil_modifier_move_up( + struct ReportList *reports, struct Object *ob, struct GpencilModifierData *md); +int ED_object_gpencil_modifier_apply( + struct Main *bmain, struct ReportList *reports, struct Depsgraph *depsgraph, + struct Object *ob, struct GpencilModifierData *md, int mode); +int ED_object_gpencil_modifier_copy( + struct ReportList *reports, struct Object *ob, struct GpencilModifierData *md); + +/* object_shader_fx.c */ +struct ShaderFxData *ED_object_shaderfx_add( + struct ReportList *reports, struct Main *bmain, struct Scene *scene, + struct Object *ob, const char *name, int type); +bool ED_object_shaderfx_remove( + struct ReportList *reports, struct Main *bmain, + struct Object *ob, struct ShaderFxData *fx); +void ED_object_shaderfx_clear( + struct Main *bmain, struct Object *ob); +int ED_object_shaderfx_move_down( + struct ReportList *reports, struct Object *ob, struct ShaderFxData *fx); +int ED_object_shaderfx_move_up( + struct ReportList *reports, struct Object *ob, struct ShaderFxData *fx); + /* object_select.c */ void ED_object_select_linked_by_id(struct bContext *C, struct ID *id); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 516b121031e..6b0c59fb557 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -119,7 +119,7 @@ void ED_view3d_cursor3d_position_rotation( float cursor_co[3], float cursor_quat[4]); void ED_view3d_cursor3d_update( struct bContext *C, const int mval[2], - bool use_depth, enum eV3DCursorOrient orientation); + const bool use_depth, enum eV3DCursorOrient orientation); struct Camera *ED_view3d_camera_data_get(struct View3D *v3d, struct RegionView3D *rv3d); diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index ec4c7dddd4c..dec02faf85c 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -1001,6 +1001,30 @@ DEF_ICON(MATCAP_22) DEF_ICON(MATCAP_23) DEF_ICON(MATCAP_24) +/* grease pencil sculpt */ +DEF_ICON(GPBRUSH_SMOOTH) +DEF_ICON(GPBRUSH_THICKNESS) +DEF_ICON(GPBRUSH_STRENGTH) +DEF_ICON(GPBRUSH_GRAB) +DEF_ICON(GPBRUSH_PUSH) +DEF_ICON(GPBRUSH_TWIST) +DEF_ICON(GPBRUSH_PINCH) +DEF_ICON(GPBRUSH_RANDOMIZE) +DEF_ICON(GPBRUSH_CLONE) +DEF_ICON(GPBRUSH_WEIGHT) + +DEF_ICON(GPBRUSH_PENCIL) +DEF_ICON(GPBRUSH_PEN) +DEF_ICON(GPBRUSH_INK) +DEF_ICON(GPBRUSH_INKNOISE) +DEF_ICON(GPBRUSH_BLOCK) +DEF_ICON(GPBRUSH_MARKER) +DEF_ICON(GPBRUSH_CUSTOM) +DEF_ICON(GPBRUSH_FILL) +DEF_ICON(GPBRUSH_ERASE_SOFT) +DEF_ICON(GPBRUSH_ERASE_HARD) +DEF_ICON(GPBRUSH_ERASE_STROKE) + /* vector icons, VICO_ prefix added */ DEF_VICO(SMALL_TRI_RIGHT_VEC) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 087e0c4ab7b..d174a78ee23 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -772,7 +772,7 @@ typedef enum { uiBut *uiDefAutoButR(uiBlock *block, struct PointerRNA *ptr, struct PropertyRNA *prop, int index, const char *name, int icon, int x1, int y1, int x2, int y2); eAutoPropButsReturn uiDefAutoButsRNA( uiLayout *layout, struct PointerRNA *ptr, - bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *), + bool (*check_prop)(struct PointerRNA *ptr, struct PropertyRNA *prop, void *user_data), void *user_data, eButLabelAlign label_align, const bool compact); /* use inside searchfunc to add items */ @@ -935,6 +935,7 @@ enum { UI_TEMPLATE_OP_PROPS_SHOW_TITLE = (1 << 0), UI_TEMPLATE_OP_PROPS_SHOW_EMPTY = (1 << 1), UI_TEMPLATE_OP_PROPS_COMPACT = (1 << 2), + UI_TEMPLATE_OP_PROPS_HIDE_ADVANCED = (1 << 3), }; /* used for transp checkers */ @@ -1021,7 +1022,8 @@ uiLayout *uiLayoutRadial(uiLayout *layout); void uiTemplateHeader(uiLayout *layout, struct bContext *C); void uiTemplateID( uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, - const char *newop, const char *openop, const char *unlinkop, int filter); + const char *newop, const char *openop, const char *unlinkop, + int filter, const bool live_icon); void uiTemplateIDBrowse( uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, int filter); @@ -1051,6 +1053,12 @@ void uiTemplatePathBuilder( uiLayout *layout, struct PointerRNA *ptr, const char *propname, struct PointerRNA *root_ptr, const char *text); uiLayout *uiTemplateModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); +uiLayout *uiTemplateGpencilModifier(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); +void uiTemplateGpencilColorPreview( + uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, + int rows, int cols, float scale, int filter); + +uiLayout *uiTemplateShaderFx(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr); void uiTemplateOperatorRedoProperties(uiLayout *layout, const struct bContext *C); @@ -1085,7 +1093,6 @@ void UI_but_func_operator_search(uiBut *but); void uiTemplateOperatorSearch(uiLayout *layout); eAutoPropButsReturn uiTemplateOperatorPropertyButs( const struct bContext *C, uiLayout *layout, struct wmOperator *op, - bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *), const eButLabelAlign label_align, const short flag); void uiTemplateHeader3D_mode(uiLayout *layout, struct bContext *C); void uiTemplateHeader3D(uiLayout *layout, struct bContext *C); diff --git a/source/blender/editors/include/UI_interface_icons.h b/source/blender/editors/include/UI_interface_icons.h index 651081c46bb..a34c4938b86 100644 --- a/source/blender/editors/include/UI_interface_icons.h +++ b/source/blender/editors/include/UI_interface_icons.h @@ -59,7 +59,7 @@ typedef struct IconFile { /* * Resizable Icons for Blender */ -void UI_icons_init(int first_dyn_id); +void UI_icons_init(void); int UI_icon_get_width(int icon_id); int UI_icon_get_height(int icon_id); diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index 240649c8ab0..d00ab70562c 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -715,7 +715,7 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but) } /* Show header tools for header buttons. */ - if (ui_block_is_menu(but->block) == false) { + if (ui_block_is_popup_any(but->block) == false) { ARegion *ar = CTX_wm_region(C); if (ar && (ar->regiontype == RGN_TYPE_HEADER)) { uiItemMenuF(layout, IFACE_("Header"), ICON_NONE, ED_screens_header_tools_menu_create, NULL); diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 22b82898288..4f77b797ec0 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -47,6 +47,7 @@ #include "DNA_brush_types.h" #include "DNA_curve_types.h" #include "DNA_dynamicpaint_types.h" +#include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" @@ -109,6 +110,7 @@ typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha); #define ICON_TYPE_VECTOR 4 #define ICON_TYPE_GEOM 5 #define ICON_TYPE_EVENT 6 /* draw keymap entries using custom renderer. */ +#define ICON_TYPE_GPLAYER 7 typedef struct DrawInfo { int type; @@ -391,6 +393,28 @@ DEF_VICON_COLORSET_DRAW_NTH(20, 19) #undef DEF_VICON_COLORSET_DRAW_NTH +/* Dynamically render icon instead of rendering a plain color to a texture/buffer + * This is mot strictly a "vicon", as it needs access to icon->obj to get the color info, + * but it works in a very similar way. + */ +static void vicon_gplayer_color_draw(Icon *icon, int x, int y, int w, int h) +{ + bGPDlayer *gpl = (bGPDlayer *)icon->obj; + + /* Just draw a colored rect - Like for vicon_colorset_draw() */ + /* TODO: Make this have rounded corners, and maybe be a bit smaller. + * However, UI_draw_roundbox_aa() draws the colors too dark, so can't be used. + */ + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_I32, 2, GPU_FETCH_INT_TO_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + immUniformColor3fv(gpl->color); + immRecti(pos, x, y, x + w - 1, y + h - 1); + + immUnbindProgram(); +} + + #ifndef WITH_HEADLESS static void init_brush_icons(void) @@ -443,6 +467,30 @@ static void init_brush_icons(void) INIT_BRUSH_ICON(ICON_BRUSH_ROTATE, twist); INIT_BRUSH_ICON(ICON_BRUSH_VERTEXDRAW, vertexdraw); + /* grease pencil sculpt */ + INIT_BRUSH_ICON(ICON_GPBRUSH_SMOOTH, gp_brush_smooth); + INIT_BRUSH_ICON(ICON_GPBRUSH_THICKNESS, gp_brush_thickness); + INIT_BRUSH_ICON(ICON_GPBRUSH_STRENGTH, gp_brush_strength); + INIT_BRUSH_ICON(ICON_GPBRUSH_GRAB, gp_brush_grab); + INIT_BRUSH_ICON(ICON_GPBRUSH_PUSH, gp_brush_push); + INIT_BRUSH_ICON(ICON_GPBRUSH_TWIST, gp_brush_twist); + INIT_BRUSH_ICON(ICON_GPBRUSH_PINCH, gp_brush_pinch); + INIT_BRUSH_ICON(ICON_GPBRUSH_RANDOMIZE, gp_brush_randomize); + INIT_BRUSH_ICON(ICON_GPBRUSH_CLONE, gp_brush_clone); + INIT_BRUSH_ICON(ICON_GPBRUSH_WEIGHT, gp_brush_weight); + + /* grease pencil drawing brushes */ + INIT_BRUSH_ICON(ICON_GPBRUSH_PENCIL, gp_brush_pencil); + INIT_BRUSH_ICON(ICON_GPBRUSH_PEN, gp_brush_pen); + INIT_BRUSH_ICON(ICON_GPBRUSH_INK, gp_brush_ink); + INIT_BRUSH_ICON(ICON_GPBRUSH_INKNOISE, gp_brush_inknoise); + INIT_BRUSH_ICON(ICON_GPBRUSH_BLOCK, gp_brush_block); + INIT_BRUSH_ICON(ICON_GPBRUSH_MARKER, gp_brush_marker); + INIT_BRUSH_ICON(ICON_GPBRUSH_FILL, gp_brush_fill); + INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_SOFT, gp_brush_erase_soft); + INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_HARD, gp_brush_erase_hard); + INIT_BRUSH_ICON(ICON_GPBRUSH_ERASE_STROKE, gp_brush_erase_stroke); + #undef INIT_BRUSH_ICON } @@ -877,6 +925,9 @@ static DrawInfo *icon_create_drawinfo(Icon *icon) else if (icon_data_type == ICON_DATA_STUDIOLIGHT) { di->type = ICON_TYPE_BUFFER; } + else if (icon_data_type == ICON_DATA_GPLAYER) { + di->type = ICON_TYPE_GPLAYER; + } else { BLI_assert(0); } @@ -934,9 +985,8 @@ int UI_icon_get_height(int icon_id) return 0; } -void UI_icons_init(int first_dyn_id) +void UI_icons_init() { - BKE_icons_init(first_dyn_id); #ifndef WITH_HEADLESS init_iconfile_list(&iconfilelist); init_internal_icons(); @@ -1485,6 +1535,15 @@ static void icon_draw_size( GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); } } + else if (di->type == ICON_TYPE_GPLAYER) { + BLI_assert(icon->obj != NULL); + + /* We need to flush widget base first to ensure correct ordering. */ + UI_widgetbase_draw_cache_flush(); + + /* Just draw a colored rect - Like for vicon_colorset_draw() */ + vicon_gplayer_color_draw(icon, (int)x, (int)y, w, h); + } } static void ui_id_preview_image_render_size( @@ -1577,7 +1636,45 @@ static int ui_id_brush_get_icon(const bContext *C, ID *id) } /* reset the icon */ - if (mode == OB_MODE_SCULPT) { + if (ob->mode & OB_MODE_GPENCIL_PAINT) { + switch (br->gpencil_settings->icon_id) { + case GP_BRUSH_ICON_PENCIL: + br->id.icon_id = ICON_GPBRUSH_PENCIL; + break; + case GP_BRUSH_ICON_PEN: + br->id.icon_id = ICON_GPBRUSH_PEN; + break; + case GP_BRUSH_ICON_INK: + br->id.icon_id = ICON_GPBRUSH_INK; + break; + case GP_BRUSH_ICON_INKNOISE: + br->id.icon_id = ICON_GPBRUSH_INKNOISE; + break; + case GP_BRUSH_ICON_BLOCK: + br->id.icon_id = ICON_GPBRUSH_BLOCK; + break; + case GP_BRUSH_ICON_MARKER: + br->id.icon_id = ICON_GPBRUSH_MARKER; + break; + case GP_BRUSH_ICON_FILL: + br->id.icon_id = ICON_GPBRUSH_FILL; + break; + case GP_BRUSH_ICON_ERASE_SOFT: + br->id.icon_id = ICON_GPBRUSH_ERASE_SOFT; + break; + case GP_BRUSH_ICON_ERASE_HARD: + br->id.icon_id = ICON_GPBRUSH_ERASE_HARD; + break; + case GP_BRUSH_ICON_ERASE_STROKE: + br->id.icon_id = ICON_GPBRUSH_ERASE_STROKE; + break; + default: + br->id.icon_id = ICON_GPBRUSH_PEN; + break; + } + return id->icon_id; + } + else if (mode == OB_MODE_SCULPT) { items = rna_enum_brush_sculpt_tool_items; tool = br->sculpt_tool; } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index fa60ac0552b..f7507223f31 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -780,7 +780,7 @@ void ui_draw_preview_item(struct uiFontStyle *fstyle, rcti *rect, const char *na #define UI_POPUP_MENU_TOP (int)(8 * UI_DPI_FAC) #define UI_PIXEL_AA_JITTER 8 -const float ui_pixel_jitter[UI_PIXEL_AA_JITTER][2]; +extern const float ui_pixel_jitter[UI_PIXEL_AA_JITTER][2]; /* interface_style.c */ void uiStyleInit(void); @@ -831,7 +831,9 @@ bool ui_but_is_toggle(const uiBut *but); bool ui_but_is_popover_once_compat(const uiBut *but); extern bool ui_block_is_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; +extern bool ui_block_is_popover(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; extern bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; +extern bool ui_block_is_popup_any(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; /* interface_context_menu.c */ bool ui_popup_context_menu_for_button(struct bContext *C, uiBut *but); diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 82ed4c5acba..f143d418fe3 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -1244,7 +1244,15 @@ void uiItemsFullEnumO( bool free; if (ui_layout_is_radial(layout)) { + /* XXX: While "_all()" guarantees spatial stability, it's bad when an enum has > 8 items total, + * but only a small subset will ever be shown at once (e.g. Mode Switch menu, after the + * introduction of GP editing modes) + */ +#if 0 RNA_property_enum_items_gettexted_all(block->evil_C, &ptr, prop, &item_array, &totitem, &free); +#else + RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, &totitem, &free); +#endif } else { RNA_property_enum_items_gettexted(block->evil_C, &ptr, prop, &item_array, &totitem, &free); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index 59fdf7e672d..aa67d58fd57 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -454,9 +454,9 @@ static void ui_offset_panel_block(uiBlock *block) /* triangle 'icon' for panel header */ void UI_draw_icon_tri(float x, float y, char dir, const float color[4]) { - float f3 = 0.15 * U.widget_unit; - float f5 = 0.25 * U.widget_unit; - float f7 = 0.35 * U.widget_unit; + float f3 = 0.05 * U.widget_unit; + float f5 = 0.15 * U.widget_unit; + float f7 = 0.25 * U.widget_unit; if (dir == 'h') { UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 66f63fef82d..d0f7e1341de 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -109,11 +109,25 @@ bool ui_block_is_menu(const uiBlock *block) ((block->flag & UI_BLOCK_KEEP_OPEN) == 0)); } +bool ui_block_is_popover(const uiBlock *block) +{ + return (block->flag & UI_BLOCK_POPOVER) != 0; +} + bool ui_block_is_pie_menu(const uiBlock *block) { return ((block->flag & UI_BLOCK_RADIAL) != 0); } +bool ui_block_is_popup_any(const uiBlock *block) +{ + return ( + ui_block_is_menu(block) || + ui_block_is_popover(block) || + ui_block_is_pie_menu(block) + ); +} + bool UI_block_is_empty(const uiBlock *block) { for (const uiBut *but = block->buttons.first; but; but = but->next) { diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 36ad516bf7f..ed4c766c596 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -522,6 +522,35 @@ void uiStyleInit(void) BLF_size(blf_mono_font, 12 * U.pixelsize, 72); + /* Set default flags based on UI preferences (not render fonts) */ + { + int flag_enable = 0, flag_disable = 0; + if ((U.text_render & USER_TEXT_DISABLE_HINTING) == 0) { + flag_enable |= BLF_HINTING; + } + else { + flag_disable |= BLF_HINTING; + } + + if (U.text_render & USER_TEXT_DISABLE_AA) { + flag_enable |= BLF_MONOCHROME; + } + else { + flag_disable |= BLF_MONOCHROME; + } + + for (font = U.uifonts.first; font; font = font->next) { + if (font->blf_id != -1) { + BLF_enable(font->blf_id, flag_enable); + BLF_disable(font->blf_id, flag_disable); + } + } + if (blf_mono_font != -1) { + BLF_enable(blf_mono_font, flag_enable); + BLF_disable(blf_mono_font, flag_disable); + } + } + /** * Second for rendering else we get threading problems, * diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 2dcc18af4ea..e81ad1428d1 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -39,6 +39,8 @@ #include "DNA_object_force_types.h" #include "DNA_brush_types.h" #include "DNA_texture_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_shader_fx_types.h" #include "BLI_utildefines.h" #include "BLI_alloca.h" @@ -57,6 +59,7 @@ #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_gpencil_modifier.h" #include "BKE_idcode.h" #include "BKE_idprop.h" #include "BKE_layer.h" @@ -71,6 +74,7 @@ #include "BKE_paint.h" #include "BKE_report.h" #include "BKE_screen.h" +#include "BKE_shader_fx.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -110,7 +114,7 @@ static void template_add_button_search_menu( const bContext *C, uiLayout *layout, uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, uiBlockCreateFunc block_func, void *block_argN, const char * const tip, - const bool use_previews, const bool editable) + const bool use_previews, const bool editable, const bool live_icon) { PointerRNA active_ptr = RNA_property_pointer_get(ptr, prop); ID *id = (active_ptr.data && RNA_struct_is_ID(active_ptr.type)) ? active_ptr.data : NULL; @@ -146,7 +150,14 @@ static void template_add_button_search_menu( } else { but = uiDefBlockButN(block, block_func, block_argN, "", 0, 0, UI_UNIT_X * 1.6, UI_UNIT_Y, tip); - ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); + + if (live_icon) { + int icon = id ? ui_id_icon_get(C, id, false) : RNA_struct_ui_icon(type); + ui_def_but_icon(but, icon, UI_HAS_ICON | UI_BUT_ICON_PREVIEW); + } + else { + ui_def_but_icon(but, RNA_struct_ui_icon(type), UI_HAS_ICON); + } if (id) { /* default dragging of icon for id browse buttons */ UI_but_drag_set_id(but, id); @@ -162,7 +173,8 @@ static uiBlock *template_common_search_menu( const bContext *C, ARegion *region, uiButSearchFunc search_func, void *search_arg, uiButHandleFunc handle_func, void *active_item, - const int preview_rows, const int preview_cols) + const int preview_rows, const int preview_cols, + float scale) { static char search[256]; wmWindow *win = CTX_wm_window(C); @@ -177,8 +189,8 @@ static uiBlock *template_common_search_menu( /* preview thumbnails */ if (preview_rows > 0 && preview_cols > 0) { - const int w = 4 * U.widget_unit * preview_cols; - const int h = 5 * U.widget_unit * preview_rows; + const int w = 4 * U.widget_unit * preview_cols * scale; + const int h = 5 * U.widget_unit * preview_rows * scale; /* fake button, it holds space for search items */ uiDefBut(block, UI_BTYPE_LABEL, 0, "", 10, 26, w, h, NULL, 0, 0, 0, 0, NULL); @@ -237,6 +249,7 @@ typedef struct TemplateID { short filter; int prv_rows, prv_cols; bool preview; + float scale; } TemplateID; /* Search browse menu, assign */ @@ -382,7 +395,7 @@ static uiBlock *id_search_menu(bContext *C, ARegion *ar, void *arg_litem) return template_common_search_menu( C, ar, id_search_cb_p, &template_ui, template_ID_set_property_cb, active_item_ptr.data, - template_ui.prv_rows, template_ui.prv_cols); + template_ui.prv_rows, template_ui.prv_cols, template_ui.scale); } /************************ ID Template ***************************/ @@ -630,7 +643,8 @@ static uiBut *template_id_def_new_but( static void template_ID( bContext *C, uiLayout *layout, TemplateID *template_ui, StructRNA *type, int flag, - const char *newop, const char *openop, const char *unlinkop) + const char *newop, const char *openop, const char *unlinkop, + const bool live_icon) { uiBut *but; uiBlock *block; @@ -655,7 +669,7 @@ static void template_ID( template_add_button_search_menu( C, layout, block, &template_ui->ptr, template_ui->prop, id_search_menu, MEM_dupallocN(template_ui), TIP_(template_id_browse_tip(type)), - use_previews, editable); + use_previews, editable, live_icon); } /* text button with name */ @@ -860,7 +874,8 @@ static void ui_template_id( uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, const char *openop, const char *unlinkop, - int flag, int prv_rows, int prv_cols, int filter, bool use_tabs) + int flag, int prv_rows, int prv_cols, int filter, bool use_tabs, + float scale, bool live_icon) { TemplateID *template_ui; PropertyRNA *prop; @@ -879,6 +894,7 @@ static void ui_template_id( template_ui->prop = prop; template_ui->prv_rows = prv_rows; template_ui->prv_cols = prv_cols; + template_ui->scale = scale; if ((flag & UI_ID_PIN) == 0) { template_ui->filter = filter; @@ -907,7 +923,7 @@ static void ui_template_id( } else { uiLayoutRow(layout, true); - template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop); + template_ID(C, layout, template_ui, type, flag, newop, openop, unlinkop, live_icon); } } @@ -916,13 +932,14 @@ static void ui_template_id( void uiTemplateID( uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, const char *newop, - const char *openop, const char *unlinkop, int filter) + const char *openop, const char *unlinkop, + int filter, const bool live_icon) { ui_template_id( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, - 0, 0, filter, false); + 0, 0, filter, false, 1.0f, live_icon); } void uiTemplateIDBrowse( @@ -933,7 +950,7 @@ void uiTemplateIDBrowse( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME, - 0, 0, filter, false); + 0, 0, filter, false, 1.0f, false); } void uiTemplateIDPreview( @@ -944,7 +961,18 @@ void uiTemplateIDPreview( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE | UI_ID_PREVIEWS, - rows, cols, filter, false); + rows, cols, filter, false, 1.0f, false); +} + +void uiTemplateGpencilColorPreview( + uiLayout *layout, bContext *C, PointerRNA *ptr, const char *propname, + int rows, int cols, float scale, int filter) +{ + ui_template_id( + layout, C, ptr, propname, + NULL, NULL, NULL, + UI_ID_BROWSE | UI_ID_PREVIEWS | UI_ID_DELETE, + rows, cols, filter, false, scale < 0.5f ? 0.5f : scale, false); } /** @@ -960,7 +988,7 @@ void uiTemplateIDTabs( layout, C, ptr, propname, newop, openop, unlinkop, UI_ID_BROWSE | UI_ID_RENAME | UI_ID_DELETE, - 0, 0, filter, true); + 0, 0, filter, true, 1.0f, false); } /************************ ID Chooser Template ***************************/ @@ -1057,12 +1085,12 @@ static uiBlock *template_search_menu(bContext *C, ARegion *region, void *arg_tem return template_common_search_menu( C, region, ui_rna_collection_search_cb, &template_search, template_search_handle_cb, active_ptr.data, - template_search.preview_rows, template_search.preview_cols); + template_search.preview_rows, template_search.preview_cols, 1.0f); } static void template_search_add_button_searchmenu( const bContext *C, uiLayout *layout, uiBlock *block, - TemplateSearch *template_search, const bool editable) + TemplateSearch *template_search, const bool editable, const bool live_icon) { const char *ui_description = RNA_property_ui_description(template_search->search_data.target_prop); @@ -1070,7 +1098,7 @@ static void template_search_add_button_searchmenu( C, layout, block, &template_search->search_data.target_ptr, template_search->search_data.target_prop, template_search_menu, MEM_dupallocN(template_search), ui_description, - template_search->use_previews, editable); + template_search->use_previews, editable, live_icon); } static void template_search_add_button_name( @@ -1116,7 +1144,7 @@ static void template_search_buttons( uiLayoutRow(layout, true); UI_block_align_begin(block); - template_search_add_button_searchmenu(C, layout, block, template_search, editable); + template_search_add_button_searchmenu(C, layout, block, template_search, editable, false); template_search_add_button_name(block, &active_ptr, type); template_search_add_button_operator(block, newop, WM_OP_INVOKE_DEFAULT, ICON_ZOOMIN, editable); template_search_add_button_operator(block, unlinkop, WM_OP_INVOKE_REGION_WIN, ICON_X, editable); @@ -1539,14 +1567,250 @@ uiLayout *uiTemplateModifier(uiLayout *layout, bContext *C, PointerRNA *ptr) return NULL; } +/************************ Grease Pencil Modifier Template *************************/ -/************************ Redo Buttons Template *************************/ +static uiLayout *gpencil_draw_modifier( + uiLayout *layout, Object *ob, + GpencilModifierData *md) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + PointerRNA ptr; + uiBlock *block; + uiLayout *box, *column, *row, *sub; + uiLayout *result = NULL; + + /* create RNA pointer */ + RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr); + + column = uiLayoutColumn(layout, true); + uiLayoutSetContextPointer(column, "modifier", &ptr); + + /* rounded header ------------------------------------------------------------------- */ + box = uiLayoutBox(column); + + row = uiLayoutRow(box, false); + block = uiLayoutGetBlock(row); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + /* Open/Close ................................. */ + uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE); + + /* modifier-type icon */ + uiItemL(row, "", RNA_struct_ui_icon(ptr.type)); + UI_block_emboss_set(block, UI_EMBOSS); + + /* modifier name */ + if (mti->isDisabled && mti->isDisabled(md, 0)) { + uiLayoutSetRedAlert(row, true); + } + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); + uiLayoutSetRedAlert(row, false); + + /* mode enabling buttons */ + UI_block_align_begin(block); + uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); + uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE); + + if (mti->flags & eGpencilModifierTypeFlag_SupportsEditmode) { + sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, false); + uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE); + } + + UI_block_align_end(block); + + /* Up/Down + Delete ........................... */ + UI_block_align_begin(block); + uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_gpencil_modifier_move_up"); + uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_gpencil_modifier_move_down"); + UI_block_align_end(block); -static bool template_operator_redo_property_buts_poll(PointerRNA *UNUSED(ptr), PropertyRNA *prop) + UI_block_emboss_set(block, UI_EMBOSS_NONE); + uiItemO(row, "", ICON_X, "OBJECT_OT_gpencil_modifier_remove"); + UI_block_emboss_set(block, UI_EMBOSS); + + /* modifier settings (under the header) --------------------------------------------------- */ + if (md->mode & eGpencilModifierMode_Expanded) { + /* apply/convert/copy */ + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + + /* only here obdata, the rest of modifiers is ob level */ + UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE); + + uiLayoutSetOperatorContext(row, WM_OP_INVOKE_DEFAULT); + uiItemEnumO(row, "OBJECT_OT_gpencil_modifier_apply", CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Apply"), + 0, "apply_as", MODIFIER_APPLY_DATA); + + uiItemO(row, CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Copy"), ICON_NONE, + "OBJECT_OT_gpencil_modifier_copy"); + + /* result is the layout block inside the box, that we return so that modifier settings can be drawn */ + result = uiLayoutColumn(box, false); + block = uiLayoutAbsoluteBlock(box); + } + + /* error messages */ + if (md->error) { + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + uiItemL(row, md->error, ICON_ERROR); + } + + return result; +} + +uiLayout *uiTemplateGpencilModifier(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - return (RNA_property_tags(prop) & OP_PROP_TAG_ADVANCED) == 0; + Object *ob; + GpencilModifierData *md, *vmd; + int i; + + /* verify we have valid data */ + if (!RNA_struct_is_a(ptr->type, &RNA_GpencilModifier)) { + RNA_warning("Expected modifier on object"); + return NULL; + } + + ob = ptr->id.data; + md = ptr->data; + + if (!ob || !(GS(ob->id.name) == ID_OB)) { + RNA_warning("Expected modifier on object"); + return NULL; + } + + UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE); + + /* find modifier and draw it */ + vmd = ob->greasepencil_modifiers.first; + for (i = 0; vmd; i++, vmd = vmd->next) { + if (md == vmd) + return gpencil_draw_modifier(layout, ob, md); + } + + return NULL; } +/************************ Shader FX Template *************************/ + +static uiLayout *gpencil_draw_shaderfx(uiLayout *layout, Object *ob, + ShaderFxData *md) +{ + const ShaderFxTypeInfo *mti = BKE_shaderfxType_getInfo(md->type); + PointerRNA ptr; + uiBlock *block; + uiLayout *box, *column, *row, *sub; + uiLayout *result = NULL; + + /* create RNA pointer */ + RNA_pointer_create(&ob->id, &RNA_ShaderFx, md, &ptr); + + column = uiLayoutColumn(layout, true); + uiLayoutSetContextPointer(column, "shaderfx", &ptr); + + /* rounded header ------------------------------------------------------------------- */ + box = uiLayoutBox(column); + + row = uiLayoutRow(box, false); + block = uiLayoutGetBlock(row); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + /* Open/Close ................................. */ + uiItemR(row, &ptr, "show_expanded", 0, "", ICON_NONE); + + /* shader-type icon */ + uiItemL(row, "", RNA_struct_ui_icon(ptr.type)); + UI_block_emboss_set(block, UI_EMBOSS); + + /* effect name */ + if (mti->isDisabled && mti->isDisabled(md, 0)) { + uiLayoutSetRedAlert(row, true); + } + uiItemR(row, &ptr, "name", 0, "", ICON_NONE); + uiLayoutSetRedAlert(row, false); + + /* mode enabling buttons */ + UI_block_align_begin(block); + uiItemR(row, &ptr, "show_render", 0, "", ICON_NONE); + uiItemR(row, &ptr, "show_viewport", 0, "", ICON_NONE); + + if (mti->flags & eShaderFxTypeFlag_SupportsEditmode) { + sub = uiLayoutRow(row, true); + uiLayoutSetActive(sub, false); + uiItemR(sub, &ptr, "show_in_editmode", 0, "", ICON_NONE); + } + + UI_block_align_end(block); + + /* Up/Down + Delete ........................... */ + UI_block_align_begin(block); + uiItemO(row, "", ICON_TRIA_UP, "OBJECT_OT_shaderfx_move_up"); + uiItemO(row, "", ICON_TRIA_DOWN, "OBJECT_OT_shaderfx_move_down"); + UI_block_align_end(block); + + UI_block_emboss_set(block, UI_EMBOSS_NONE); + uiItemO(row, "", ICON_X, "OBJECT_OT_shaderfx_remove"); + UI_block_emboss_set(block, UI_EMBOSS); + + /* effect settings (under the header) --------------------------------------------------- */ + if (md->mode & eShaderFxMode_Expanded) { + /* apply/convert/copy */ + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + + /* only here obdata, the rest of effect is ob level */ + UI_block_lock_set(block, BKE_object_obdata_is_libdata(ob), ERROR_LIBDATA_MESSAGE); + + /* result is the layout block inside the box, that we return so that effect settings can be drawn */ + result = uiLayoutColumn(box, false); + block = uiLayoutAbsoluteBlock(box); + } + + /* error messages */ + if (md->error) { + box = uiLayoutBox(column); + row = uiLayoutRow(box, false); + uiItemL(row, md->error, ICON_ERROR); + } + + return result; +} + +uiLayout *uiTemplateShaderFx(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + Object *ob; + ShaderFxData *fx, *vfx; + int i; + + /* verify we have valid data */ + if (!RNA_struct_is_a(ptr->type, &RNA_ShaderFx)) { + RNA_warning("Expected shader fx on object"); + return NULL; + } + + ob = ptr->id.data; + fx = ptr->data; + + if (!ob || !(GS(ob->id.name) == ID_OB)) { + RNA_warning("Expected shader fx on object"); + return NULL; + } + + UI_block_lock_set(uiLayoutGetBlock(layout), (ob && ID_IS_LINKED(ob)), ERROR_LIBDATA_MESSAGE); + + /* find modifier and draw it */ + vfx = ob->shader_fx.first; + for (i = 0; vfx; i++, vfx = vfx->next) { + if (fx == vfx) + return gpencil_draw_shaderfx(layout, ob, fx); + } + + return NULL; +} + +/************************ Redo Buttons Template *************************/ + static void template_operator_redo_property_buts_draw( const bContext *C, wmOperator *op, uiLayout *layout, int layout_flags, @@ -1560,8 +1824,9 @@ static void template_operator_redo_property_buts_draw( else { /* Might want to make label_align adjustable somehow. */ eAutoPropButsReturn return_info = uiTemplateOperatorPropertyButs( - C, layout, op, r_has_advanced ? template_operator_redo_property_buts_poll : NULL, - UI_BUT_LABEL_ALIGN_NONE, layout_flags); + C, layout, op, + UI_BUT_LABEL_ALIGN_NONE, + layout_flags); if (return_info & UI_PROP_BUTS_ANY_FAILED_CHECK) { if (r_has_advanced) { *r_has_advanced = true; @@ -3948,13 +4213,30 @@ static void ui_layout_operator_buts__reset_cb(bContext *UNUSED(C), void *op_pt, } #endif +struct uiTemplateOperatorPropertyPollParam { + const bContext *C; + wmOperator *op; + short flag; +}; + +static bool ui_layout_operator_buts_poll_property( + struct PointerRNA *UNUSED(ptr), struct PropertyRNA *prop, void *user_data) +{ + struct uiTemplateOperatorPropertyPollParam *params = user_data; + if ((params->flag & UI_TEMPLATE_OP_PROPS_HIDE_ADVANCED) && + (RNA_property_tags(prop) & OP_PROP_TAG_ADVANCED)) + { + return false; + } + return params->op->type->poll_property(params->C, params->op, prop); +} + /** * Draw Operator property buttons for redoing execution with different settings. * This function does not initialize the layout, functions can be called on the layout before and after. */ eAutoPropButsReturn uiTemplateOperatorPropertyButs( const bContext *C, uiLayout *layout, wmOperator *op, - bool (*check_prop)(struct PointerRNA *, struct PropertyRNA *), const eButLabelAlign label_align, const short flag) { uiBlock *block = uiLayoutGetBlock(layout); @@ -4013,13 +4295,18 @@ eAutoPropButsReturn uiTemplateOperatorPropertyButs( else { wmWindowManager *wm = CTX_wm_manager(C); PointerRNA ptr; + struct uiTemplateOperatorPropertyPollParam user_data = {.C = C, .op = op, .flag = flag}; RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); uiLayoutSetPropSep(layout, true); /* main draw call */ - return_info = uiDefAutoButsRNA(layout, &ptr, check_prop, label_align, (flag & UI_TEMPLATE_OP_PROPS_COMPACT)); + return_info = uiDefAutoButsRNA( + layout, &ptr, + op->type->poll_property ? ui_layout_operator_buts_poll_property : NULL, + op->type->poll_property ? &user_data : NULL, + label_align, (flag & UI_TEMPLATE_OP_PROPS_COMPACT)); if ((return_info & UI_PROP_BUTS_NONE_ADDED) && (flag & UI_TEMPLATE_OP_PROPS_SHOW_EMPTY)) { uiItemL(layout, IFACE_("No Properties"), ICON_NONE); @@ -4606,7 +4893,7 @@ void uiTemplateCacheFile(uiLayout *layout, bContext *C, PointerRNA *ptr, const c uiLayoutSetContextPointer(layout, "edit_cachefile", &fileptr); - uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, propname, NULL, "CACHEFILE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!file) { return; diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index d080397c488..60aa79e1093 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -159,7 +159,7 @@ uiBut *uiDefAutoButR(uiBlock *block, PointerRNA *ptr, PropertyRNA *prop, int ind */ eAutoPropButsReturn uiDefAutoButsRNA( uiLayout *layout, PointerRNA *ptr, - bool (*check_prop)(PointerRNA *, PropertyRNA *), + bool (*check_prop)(PointerRNA *ptr, PropertyRNA *prop, void *user_data), void *user_data, const eButLabelAlign label_align, const bool compact) { eAutoPropButsReturn return_info = UI_PROP_BUTS_NONE_ADDED; @@ -174,7 +174,7 @@ eAutoPropButsReturn uiDefAutoButsRNA( if (flag & PROP_HIDDEN) { continue; } - if (check_prop && check_prop(ptr, prop) == 0) { + if (check_prop && check_prop(ptr, prop, user_data) == 0) { return_info |= UI_PROP_BUTS_ANY_FAILED_CHECK; continue; } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 52e6e237a58..d0cdba49536 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1083,7 +1083,7 @@ static void widgetbase_set_uniform_colors_ubv( #define MAX_WIDGET_BASE_BATCH 6 #define MAX_WIDGET_PARAMETERS 11 -struct { +static struct { GPUBatch *batch; /* Batch type */ uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]; int count; diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index 47d664eaeb2..72023ebf2ae 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -83,7 +83,7 @@ static struct bThemeState g_theme_state = { void ui_resources_init(void) { - UI_icons_init(BIFICONID_LAST); + UI_icons_init(); } void ui_resources_free(void) @@ -1536,15 +1536,6 @@ void init_userdef_do_versions(Main *bmain) #undef USER_VERSION_ATLEAST #define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST((&(U)), ver, subver) - - if (!USER_VERSION_ATLEAST(269, 9)) { - /* grease pencil - new layer color */ - if (U.gpencil_new_layer_col[3] < 0.1f) { - /* defaults to black, but must at least be visible! */ - U.gpencil_new_layer_col[3] = 0.9f; - } - } - if (!USER_VERSION_ATLEAST(271, 5)) { U.pie_menu_radius = 100; U.pie_menu_threshold = 12; @@ -1582,6 +1573,17 @@ void init_userdef_do_versions(Main *bmain) for (bTheme *btheme = U.themes.first; btheme; btheme = btheme->next) { memcpy(btheme, &U_theme_default, sizeof(*btheme)); } + + /* Annotations - new layer color + * Replace anything that used to be set if it looks like was left + * on the old default (i.e. black), which most users used + */ + if ((U.gpencil_new_layer_col[3] < 0.1f) || (U.gpencil_new_layer_col[0] < 0.1f)) { + /* - New color matches the annotation pencil icon + * - Non-full alpha looks better! + */ + ARRAY_SET_ITEMS(U.gpencil_new_layer_col, 0.38f, 0.61f, 0.78f, 0.9f); + } } /** diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index dfc401c1635..1c56dabb396 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -354,7 +354,7 @@ static int view_scrollright_exec(bContext *C, wmOperator *op) } /* set RNA-Props - only movement in positive x-direction */ - RNA_int_set(op->ptr, "deltax", 20); + RNA_int_set(op->ptr, "deltax", 40); RNA_int_set(op->ptr, "deltay", 0); /* apply movement, then we're done */ @@ -398,7 +398,7 @@ static int view_scrollleft_exec(bContext *C, wmOperator *op) } /* set RNA-Props - only movement in negative x-direction */ - RNA_int_set(op->ptr, "deltax", -20); + RNA_int_set(op->ptr, "deltax", -40); RNA_int_set(op->ptr, "deltay", 0); /* apply movement, then we're done */ diff --git a/source/blender/editors/mesh/editmesh_extrude.c b/source/blender/editors/mesh/editmesh_extrude.c index 061cc3ebc32..c5ef2a06059 100644 --- a/source/blender/editors/mesh/editmesh_extrude.c +++ b/source/blender/editors/mesh/editmesh_extrude.c @@ -365,11 +365,11 @@ void MESH_OT_extrude_repeat(wmOperatorType *ot) #ifdef USE_GIZMO -const float extrude_button_scale = 0.15f; -const float extrude_button_offset_scale = 1.5f; -const float extrude_arrow_scale = 1.0f; -const float extrude_arrow_xyz_axis_scale = 1.0f; -const float extrude_arrow_normal_axis_scale = 1.75f; +static const float extrude_button_scale = 0.15f; +static const float extrude_button_offset_scale = 1.5f; +static const float extrude_arrow_scale = 1.0f; +static const float extrude_arrow_xyz_axis_scale = 1.0f; +static const float extrude_arrow_normal_axis_scale = 1.75f; static const uchar shape_plus[] = { 0x5f, 0xfb, 0x40, 0xee, 0x25, 0xda, 0x11, 0xbf, 0x4, 0xa0, 0x0, 0x80, 0x4, 0x5f, 0x11, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 99756269c1f..c98aef74ae7 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -1040,7 +1040,6 @@ static void knife_init_colors(KnifeColors *colors) static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void *arg) { const KnifeTool_OpData *kcd = arg; - GPU_depth_test(false); glPolygonOffset(1.0f, 1.0f); @@ -1048,14 +1047,16 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void GPU_matrix_push(); GPU_matrix_mul(kcd->ob->obmat); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + if (kcd->mode == MODE_DRAGGING && kcd->is_angle_snapping) { + knifetool_draw_angle_snapping(kcd); + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); if (kcd->mode == MODE_DRAGGING) { - if (kcd->is_angle_snapping) - knifetool_draw_angle_snapping(kcd); - immUniformColor3ubv(kcd->colors.line); GPU_line_width(2.0); @@ -1112,40 +1113,43 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void if (kcd->totlinehit > 0) { KnifeLineHit *lh; - int i; + int i, v, vs; + float fcol[4]; GPU_blend(true); GPU_blend_set_func_separate(GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); - /* draw any snapped verts first */ - immUniformColor4ubv(kcd->colors.point_a); - GPU_point_size(11); - - immBeginAtMost(GPU_PRIM_POINTS, kcd->totlinehit); + GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); + GPU_vertbuf_data_alloc(vert, kcd->totlinehit); lh = kcd->linehits; - for (i = 0; i < kcd->totlinehit; i++, lh++) { + for (i = 0, v = 0, vs = kcd->totlinehit - 1; i < kcd->totlinehit; i++, lh++) { if (lh->v) { - immVertex3fv(pos, lh->cagehit); + GPU_vertbuf_attr_set(vert, pos, v++, lh->cagehit); + } + else { + GPU_vertbuf_attr_set(vert, pos, vs--, lh->cagehit); } } - immEnd(); + GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO); + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + + /* draw any snapped verts first */ + rgba_uchar_to_float(fcol, kcd->colors.point_a); + GPU_batch_uniform_4fv(batch, "color", fcol); + GPU_matrix_bind(batch->interface); + GPU_point_size(11); + GPU_batch_draw_range_ex(batch, 0, v - 1, false); /* now draw the rest */ - immUniformColor4ubv(kcd->colors.curpoint_a); + rgba_uchar_to_float(fcol, kcd->colors.curpoint_a); + GPU_batch_uniform_4fv(batch, "color", fcol); GPU_point_size(7); + GPU_batch_draw_range_ex(batch, vs + 1, kcd->totlinehit - (vs + 1), false); - immBeginAtMost(GPU_PRIM_POINTS, kcd->totlinehit); - - lh = kcd->linehits; - for (i = 0; i < kcd->totlinehit; i++, lh++) { - if (!lh->v) { - immVertex3fv(pos, lh->cagehit); - } - } - - immEnd(); + GPU_batch_program_use_end(batch); + GPU_batch_discard(batch); GPU_blend(false); } @@ -1157,7 +1161,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void immUniformColor3ubv(kcd->colors.line); GPU_line_width(1.0); - immBeginAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2); + GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_LINES, BLI_mempool_len(kcd->kedges) * 2); BLI_mempool_iternew(kcd->kedges, &iter); for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) { @@ -1169,6 +1173,9 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void } immEnd(); + + GPU_batch_draw(batch); + GPU_batch_discard(batch); } if (kcd->totkvert > 0) { @@ -1178,7 +1185,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void immUniformColor3ubv(kcd->colors.point); GPU_point_size(5.0); - immBeginAtMost(GPU_PRIM_POINTS, BLI_mempool_len(kcd->kverts)); + GPUBatch *batch = immBeginBatchAtMost(GPU_PRIM_POINTS, BLI_mempool_len(kcd->kverts)); BLI_mempool_iternew(kcd->kverts, &iter); for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) { @@ -1189,6 +1196,9 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(ar), void } immEnd(); + + GPU_batch_draw(batch); + GPU_batch_discard(batch); } immUnbindProgram(); diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 2e7cf1fc76f..cdb8981801a 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -1462,6 +1462,18 @@ static int edbm_select_mode_exec(bContext *C, wmOperator *op) static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + /* Bypass when in UV non sync-select mode, fall through to keymap that edits. */ + if (CTX_wm_space_image(C)) { + ToolSettings *ts = CTX_data_tool_settings(C); + if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) { + return OPERATOR_PASS_THROUGH; + } + /* Bypass when no action is needed. */ + if (!RNA_struct_property_is_set(op->ptr, "type")) { + return OPERATOR_CANCELLED; + } + } + /* detecting these options based on shift/ctrl here is weak, but it's done * to make this work when clicking buttons or menus */ if (!RNA_struct_property_is_set(op->ptr, "use_extend")) diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 9683bf43968..0d11bf80366 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -6007,10 +6007,10 @@ static int edbm_sort_elements_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static bool edbm_sort_elements_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +static bool edbm_sort_elements_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop) { const char *prop_id = RNA_property_identifier(prop); - const int action = RNA_enum_get(ptr, "type"); + const int action = RNA_enum_get(op->ptr, "type"); /* Only show seed for randomize action! */ if (STREQ(prop_id, "seed")) { @@ -6031,18 +6031,6 @@ static bool edbm_sort_elements_draw_check_prop(PointerRNA *ptr, PropertyRNA *pro return true; } -static void edbm_sort_elements_ui(bContext *C, wmOperator *op) -{ - uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - /* Main auto-draw call. */ - uiDefAutoButsRNA(layout, &ptr, edbm_sort_elements_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); -} - void MESH_OT_sort_elements(wmOperatorType *ot) { static const EnumPropertyItem type_items[] = { @@ -6078,7 +6066,7 @@ void MESH_OT_sort_elements(wmOperatorType *ot) ot->invoke = WM_menu_invoke; ot->exec = edbm_sort_elements_exec; ot->poll = ED_operator_editmesh; - ot->ui = edbm_sort_elements_ui; + ot->poll_property = edbm_sort_elements_poll_property; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/mesh/mesh_ops.c b/source/blender/editors/mesh/mesh_ops.c index d8441ab8bf6..842a1ecab35 100644 --- a/source/blender/editors/mesh/mesh_ops.c +++ b/source/blender/editors/mesh/mesh_ops.c @@ -344,22 +344,8 @@ void ED_keymap_mesh(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "MESH_OT_bevel", BKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "vertex_only", true); - /* selecting */ - for (int i = 0; i < 4; i++) { - const bool is_extend = (i & 1); - const bool is_expand = (i & 2); - const int key_modifier = (is_extend ? KM_SHIFT : 0) | (is_expand ? KM_CTRL : 0); - for (int j = 0; j < 3; j++) { - kmi = WM_keymap_add_item(keymap, "MESH_OT_select_mode", ONEKEY + j, KM_PRESS, key_modifier, 0); - RNA_enum_set(kmi->ptr, "type", SCE_SELECT_VERTEX << j); - if (is_extend) { - RNA_boolean_set(kmi->ptr, "use_extend", true); - } - if (is_expand) { - RNA_boolean_set(kmi->ptr, "use_expand", true); - } - } - } + /* Selec Vert/Edge/Face. */ + ED_keymap_editmesh_elem_mode(keyconf, keymap); /* standard mouse selection goes via space_view3d */ kmi = WM_keymap_add_item(keymap, "MESH_OT_loop_select", SELECTMOUSE, KM_PRESS, KM_ALT, 0); diff --git a/source/blender/editors/object/CMakeLists.txt b/source/blender/editors/object/CMakeLists.txt index 305e3287029..739975a6278 100644 --- a/source/blender/editors/object/CMakeLists.txt +++ b/source/blender/editors/object/CMakeLists.txt @@ -31,6 +31,8 @@ set(INC ../../makesdna ../../makesrna ../../modifiers + ../../gpencil_modifiers + ../../shader_fx ../../python ../../render/extern/include ../../windowmanager @@ -53,6 +55,8 @@ set(SRC object_hook.c object_modes.c object_modifier.c + object_gpencil_modifier.c + object_shader_fx.c object_ops.c object_random.c object_relations.c diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index 8c60dd01e89..1ae441ee2f4 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -71,6 +71,7 @@ #include "BKE_displist.h" #include "BKE_effect.h" #include "BKE_font.h" +#include "BKE_gpencil.h" #include "BKE_lamp.h" #include "BKE_lattice.h" #include "BKE_layer.h" @@ -103,6 +104,7 @@ #include "ED_armature.h" #include "ED_curve.h" +#include "ED_gpencil.h" #include "ED_mball.h" #include "ED_mesh.h" #include "ED_node.h" @@ -985,6 +987,107 @@ void OBJECT_OT_drop_named_image(wmOperatorType *ot) ED_object_add_generic_props(ot, false); } +/********************* Add Gpencil Operator ********************/ + +static int object_gpencil_add_exec(bContext *C, wmOperator *op) +{ + Object *ob = CTX_data_active_object(C); + bGPdata *gpd = (ob && (ob->type == OB_GPENCIL)) ? ob->data : NULL; + + const int type = RNA_enum_get(op->ptr, "type"); + + float loc[3], rot[3]; + unsigned int layer; + bool newob = false; + + /* Hack: Force view-align to be on by default + * since it's not nice for adding shapes in 2D + * for them to end up aligned oddly, but only for Monkey + */ + if ((RNA_struct_property_is_set(op->ptr, "view_align") == false) && + (type == GP_MONKEY)) + { + RNA_boolean_set(op->ptr, "view_align", true); + } + + /* Note: We use 'Y' here (not 'Z'), as */ + WM_operator_view3d_unit_defaults(C, op); + if (!ED_object_add_generic_get_opts(C, op, 'Y', loc, rot, NULL, &layer, NULL)) + return OPERATOR_CANCELLED; + + /* add new object if not currently editing a GP object, + * or if "empty" was chosen (i.e. user wants a blank GP canvas) + */ + if ((gpd == NULL) || (GPENCIL_ANY_MODE(gpd) == false) || (type == GP_EMPTY)) { + const char *ob_name = (type == GP_MONKEY) ? "Suzanne" : NULL; + float radius = RNA_float_get(op->ptr, "radius"); + + ob = ED_object_add_type(C, OB_GPENCIL, ob_name, loc, rot, true, layer); + gpd = ob->data; + newob = true; + + BKE_object_obdata_size_init(ob, GP_OBGPENCIL_DEFAULT_SIZE * radius); + } + else { + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_ADDED, NULL); + } + + /* create relevant geometry */ + switch (type) { + case GP_MONKEY: + { + float radius = RNA_float_get(op->ptr, "radius"); + float mat[4][4]; + + ED_object_new_primitive_matrix(C, ob, loc, rot, mat); + mul_v3_fl(mat[0], radius); + mul_v3_fl(mat[1], radius); + mul_v3_fl(mat[2], radius); + + ED_gpencil_create_monkey(C, mat); + break; + } + + case GP_EMPTY: + /* do nothing */ + break; + + default: + BKE_report(op->reports, RPT_WARNING, "Not implemented"); + break; + } + + /* if this is a new object, initialise default stuff (colors, etc.) */ + if (newob) { + ED_gpencil_add_defaults(C); + } + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_gpencil_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add GPencil"; + ot->description = "Add a grease pencil object to the scene"; + ot->idname = "OBJECT_OT_gpencil_add"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = object_gpencil_add_exec; + ot->poll = ED_operator_scene_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + ED_object_add_unit_props(ot); + ED_object_add_generic_props(ot, false); + + ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", ""); +} + /********************* Add Light Operator ********************/ static const char *get_light_defname(int type) @@ -1479,7 +1582,7 @@ static void make_object_duplilist_real(bContext *C, Scene *scene, Base *base, ob_dst->parent = NULL; BKE_constraints_free(&ob_dst->constraints); - ob_dst->curve_cache = NULL; + ob_dst->runtime.curve_cache = NULL; ob_dst->transflag &= ~OB_DUPLI; copy_m4_m4(ob_dst->obmat, dob->mat); @@ -1638,7 +1741,7 @@ static const EnumPropertyItem convert_target_items[] = { static void convert_ensure_curve_cache(Depsgraph *depsgraph, Scene *scene, Object *ob) { - if (ob->curve_cache == NULL) { + if (ob->runtime.curve_cache == NULL) { /* Force creation. This is normally not needed but on operator * redo we might end up with an object which isn't evaluated yet. */ @@ -1781,6 +1884,10 @@ static int convert_exec(bContext *C, wmOperator *op) if (ob->type == OB_MESH) { BKE_object_free_modifiers(ob, 0); /* after derivedmesh calls! */ } + if (ob->type == OB_GPENCIL) { + BKE_object_free_modifiers(ob, 0); /* after derivedmesh calls! */ + BKE_object_free_shaderfx(ob, 0); + } } } else if (ob->type == OB_MESH && target == OB_CURVE) { @@ -1966,7 +2073,7 @@ static int convert_exec(bContext *C, wmOperator *op) } convert_ensure_curve_cache(depsgraph, scene, baseob); - BKE_mesh_from_metaball(&baseob->curve_cache->disp, newob->data); + BKE_mesh_from_metaball(&baseob->runtime.curve_cache->disp, newob->data); if (obact->type == OB_MBALL) { basact = basen; @@ -2122,6 +2229,10 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer ID_NEW_REMAP_US(obn->mat[a]) else { obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a])); + /* duplicate grease pencil settings */ + if (ob->mat[a]->gp_style) { + obn->mat[a]->gp_style = MEM_dupallocN(ob->mat[a]->gp_style); + } } id_us_min(id); @@ -2222,7 +2333,7 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer ID_NEW_REMAP_US2(obn->data) else { obn->data = ID_NEW_SET(obn->data, BKE_armature_copy(bmain, obn->data)); - BKE_pose_rebuild(bmain, obn, obn->data); + BKE_pose_rebuild(bmain, obn, obn->data, true); didit = 1; } id_us_min(id); @@ -2258,6 +2369,16 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer id_us_min(id); } break; + case OB_GPENCIL: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; } /* check if obdata is copied */ @@ -2482,7 +2603,7 @@ static bool join_poll(bContext *C) if (!ob || ID_IS_LINKED(ob)) return 0; - if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE)) + if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_ARMATURE, OB_GPENCIL)) return ED_operator_screenactive(C); else return 0; @@ -2500,6 +2621,13 @@ static int join_exec(bContext *C, wmOperator *op) BKE_report(op->reports, RPT_ERROR, "Cannot edit external libdata"); return OPERATOR_CANCELLED; } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = (bGPdata *)ob->data; + if ((!gpd) || GPENCIL_ANY_MODE(gpd)) { + BKE_report(op->reports, RPT_ERROR, "This data does not support joining in this mode"); + return OPERATOR_CANCELLED; + } + } if (ob->type == OB_MESH) return join_mesh_exec(C, op); @@ -2507,6 +2635,8 @@ static int join_exec(bContext *C, wmOperator *op) return join_curve_exec(C, op); else if (ob->type == OB_ARMATURE) return join_armature_exec(C, op); + else if (ob->type == OB_GPENCIL) + return ED_gpencil_join_objects_exec(C, op); return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 5210182510f..e8fabdabf17 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -49,6 +49,7 @@ #include "BKE_context.h" #include "BKE_global.h" #include "BKE_image.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_material.h" @@ -357,17 +358,24 @@ static bool is_noncolor_pass(eScenePassType pass_type) } /* if all is good tag image and return true */ -static bool bake_object_check(Scene *scene, Object *ob, ReportList *reports) +static bool bake_object_check(ViewLayer *view_layer, Object *ob, ReportList *reports) { Image *image; + Base *base = BKE_view_layer_base_find(view_layer, ob); void *lock; int i; - if ((ob->lay & scene->lay) == 0) { - BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not on a scene layer", ob->id.name + 2); + if (base == NULL) { + BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not in view layer", ob->id.name + 2); return false; } + if (!(base->flag & BASE_ENABLED_RENDER)) { + BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not enabled for rendering", ob->id.name + 2); + return false; + } + + if (ob->type != OB_MESH) { BKE_reportf(reports, RPT_ERROR, "Object \"%s\" is not a mesh", ob->id.name + 2); return false; @@ -497,7 +505,7 @@ static bool bake_pass_filter_check(eScenePassType pass_type, const int pass_filt } /* before even getting in the bake function we check for some basic errors */ -static bool bake_objects_check(Main *bmain, Scene *scene, Object *ob, ListBase *selected_objects, +static bool bake_objects_check(Main *bmain, ViewLayer *view_layer, Object *ob, ListBase *selected_objects, ReportList *reports, const bool is_selected_to_active) { CollectionPointerLink *link; @@ -508,7 +516,7 @@ static bool bake_objects_check(Main *bmain, Scene *scene, Object *ob, ListBase * if (is_selected_to_active) { int tot_objects = 0; - if (!bake_object_check(scene, ob, reports)) + if (!bake_object_check(view_layer, ob, reports)) return false; for (link = selected_objects->first; link; link = link->next) { @@ -536,7 +544,7 @@ static bool bake_objects_check(Main *bmain, Scene *scene, Object *ob, ListBase * } for (link = selected_objects->first; link; link = link->next) { - if (!bake_object_check(scene, link->ptr.data, reports)) + if (!bake_object_check(view_layer, link->ptr.data, reports)) return false; } } @@ -1204,7 +1212,7 @@ static int bake_exec(bContext *C, wmOperator *op) goto finally; } - if (!bake_objects_check(bkr.main, bkr.scene, bkr.ob, &bkr.selected_objects, bkr.reports, bkr.is_selected_to_active)) { + if (!bake_objects_check(bkr.main, bkr.view_layer, bkr.ob, &bkr.selected_objects, bkr.reports, bkr.is_selected_to_active)) { goto finally; } @@ -1263,7 +1271,7 @@ static void bake_startjob(void *bkv, short *UNUSED(stop), short *do_update, floa return; } - if (!bake_objects_check(bkr->main, bkr->scene, bkr->ob, &bkr->selected_objects, bkr->reports, bkr->is_selected_to_active)) { + if (!bake_objects_check(bkr->main, bkr->view_layer, bkr->ob, &bkr->selected_objects, bkr->reports, bkr->is_selected_to_active)) { bkr->result = OPERATOR_CANCELLED; return; } diff --git a/source/blender/editors/object/object_data_transfer.c b/source/blender/editors/object/object_data_transfer.c index f56fd560946..ad0b091ede9 100644 --- a/source/blender/editors/object/object_data_transfer.c +++ b/source/blender/editors/object/object_data_transfer.c @@ -450,8 +450,9 @@ static bool data_transfer_poll(bContext *C) } /* Used by both OBJECT_OT_data_transfer and OBJECT_OT_datalayout_transfer */ -static bool data_transfer_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +static bool data_transfer_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop) { + PointerRNA *ptr = op->ptr; PropertyRNA *prop_other; const char *prop_id = RNA_property_identifier(prop); @@ -512,19 +513,6 @@ static bool data_transfer_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) return true; } -/* Used by both OBJECT_OT_data_transfer and OBJECT_OT_datalayout_transfer */ -static void data_transfer_ui(bContext *C, wmOperator *op) -{ - uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - /* Main auto-draw call */ - uiDefAutoButsRNA(layout, &ptr, data_transfer_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); -} - /* transfers weight from active to selected */ void OBJECT_OT_data_transfer(wmOperatorType *ot) { @@ -537,10 +525,10 @@ void OBJECT_OT_data_transfer(wmOperatorType *ot) /* API callbacks.*/ ot->poll = data_transfer_poll; + ot->poll_property = data_transfer_poll_property; ot->invoke = WM_menu_invoke; ot->exec = data_transfer_exec; ot->check = data_transfer_check; - ot->ui = data_transfer_ui; /* Flags.*/ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -700,10 +688,10 @@ void OBJECT_OT_datalayout_transfer(wmOperatorType *ot) ot->idname = "OBJECT_OT_datalayout_transfer"; ot->poll = datalayout_transfer_poll; + ot->poll_property = data_transfer_poll_property; ot->invoke = datalayout_transfer_invoke; ot->exec = datalayout_transfer_exec; ot->check = data_transfer_check; - ot->ui = data_transfer_ui; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index a6c3c86922d..a6dad906579 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -100,6 +100,7 @@ #include "ED_screen.h" #include "ED_undo.h" #include "ED_image.h" +#include "ED_gpencil.h" #include "RNA_access.h" #include "RNA_define.h" @@ -1377,6 +1378,9 @@ static void object_clear_mpath(Object *ob) animviz_free_motionpath(ob->mpath); ob->mpath = NULL; ob->avs.path_bakeflag &= ~MOTIONPATH_BAKE_HAS_PATHS; + + /* tag object for copy on write - so removed paths don't still show */ + DEG_id_tag_update(&ob->id, DEG_TAG_COPY_ON_WRITE); } } @@ -1539,7 +1543,6 @@ static const EnumPropertyItem *object_mode_set_itemsf( const EnumPropertyItem *input = rna_enum_object_mode_items; EnumPropertyItem *item = NULL; Object *ob; - bGPdata *gpd; int totitem = 0; if (!C) /* needed for docs */ @@ -1555,7 +1558,9 @@ static const EnumPropertyItem *object_mode_set_itemsf( (input->value == OB_MODE_POSE && (ob->type == OB_ARMATURE)) || (input->value == OB_MODE_PARTICLE_EDIT && use_mode_particle_edit) || (ELEM(input->value, OB_MODE_SCULPT, OB_MODE_VERTEX_PAINT, - OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) || + OB_MODE_WEIGHT_PAINT, OB_MODE_TEXTURE_PAINT) && (ob->type == OB_MESH)) || + (ELEM(input->value, OB_MODE_GPENCIL_EDIT, OB_MODE_GPENCIL_PAINT, + OB_MODE_GPENCIL_SCULPT, OB_MODE_GPENCIL_WEIGHT) && (ob->type == OB_GPENCIL)) || (input->value == OB_MODE_OBJECT)) { RNA_enum_item_add(&item, &totitem, input); @@ -1568,14 +1573,6 @@ static const EnumPropertyItem *object_mode_set_itemsf( RNA_enum_items_add_value(&item, &totitem, input, OB_MODE_OBJECT); } - /* On top of all the rest, GPencil Stroke Edit Mode - * is available if there's a valid gp datablock... - */ - gpd = CTX_data_gpencil_data(C); - if (gpd) { - RNA_enum_items_add_value(&item, &totitem, rna_enum_object_mode_items, OB_MODE_GPENCIL); - } - RNA_enum_item_end(&item, &totitem); *r_free = true; @@ -1600,7 +1597,6 @@ static int object_mode_set_exec(bContext *C, wmOperator *op) { bool use_submode = STREQ(op->idname, "OBJECT_OT_mode_set_or_submode"); Object *ob = CTX_data_active_object(C); - bGPdata *gpd = CTX_data_gpencil_data(C); eObjectMode mode = RNA_enum_get(op->ptr, "mode"); eObjectMode restore_mode = (ob) ? ob->mode : OB_MODE_OBJECT; const bool toggle = RNA_boolean_get(op->ptr, "toggle"); @@ -1620,22 +1616,9 @@ static int object_mode_set_exec(bContext *C, wmOperator *op) } } - if (gpd) { - /* GP Mode is not bound to a specific object. Therefore, - * we don't want it to be actually saved on any objects, - * as weirdness can happen if you select other objects, - * or load old files. - * - * Instead, we use the following 2 rules to ensure that - * the mode selector works as expected: - * 1) If there's no object, we want to enter editmode. - * (i.e. with no object, we're in object mode) - * 2) Otherwise, exit stroke editmode, so that we can - * enter another mode... - */ - if (!ob || (gpd->flag & GP_DATA_STROKE_EDITMODE)) { - WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_EXEC_REGION_WIN, NULL); - } + /* by default the operator assume is a mesh, but if gp object change mode */ + if ((ob != NULL) && (ob->type == OB_GPENCIL) && (mode == OB_MODE_EDIT)) { + mode = OB_MODE_GPENCIL_EDIT; } if (!ob || !ED_object_mode_compat_test(ob, mode)) @@ -1666,6 +1649,14 @@ static int object_mode_set_exec(bContext *C, wmOperator *op) } } + /* if type is OB_GPENCIL, set cursor mode */ + if ((ob) && (ob->type == OB_GPENCIL)) { + if (ob->data) { + bGPdata *gpd = (bGPdata *)ob->data; + ED_gpencil_setup_modes(C, gpd, ob->mode); + } + } + return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_gpencil_modifier.c b/source/blender/editors/object/object_gpencil_modifier.c new file mode 100644 index 00000000000..175fb1706fb --- /dev/null +++ b/source/blender/editors/object/object_gpencil_modifier.c @@ -0,0 +1,637 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2018 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/object/object_gpencil_modifier.c + * \ingroup edobj + */ + + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_report.h" +#include "BKE_object.h" +#include "BKE_gpencil.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "ED_object.h" +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "object_intern.h" + +/******************************** API ****************************/ + +GpencilModifierData *ED_object_gpencil_modifier_add( + ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type) +{ + GpencilModifierData *new_md = NULL; + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(type); + + if (ob->type != OB_GPENCIL) { + BKE_reportf(reports, RPT_WARNING, "Modifiers cannot be added to object '%s'", ob->id.name + 2); + return NULL; + } + + if (mti->flags & eGpencilModifierTypeFlag_Single) { + if (BKE_gpencil_modifiers_findByType(ob, type)) { + BKE_report(reports, RPT_WARNING, "Only one modifier of this type is allowed"); + return NULL; + } + } + + /* get new modifier data to add */ + new_md = BKE_gpencil_modifier_new(type); + + BLI_addtail(&ob->greasepencil_modifiers, new_md); + + if (name) { + BLI_strncpy_utf8(new_md->name, name, sizeof(new_md->name)); + } + + /* make sure modifier data has unique name */ + BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, new_md); + + + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return new_md; +} + +/* Return true if the object has a modifier of type 'type' other than + * the modifier pointed to be 'exclude', otherwise returns false. */ +static bool UNUSED_FUNCTION(gpencil_object_has_modifier)( + const Object *ob, const GpencilModifierData *exclude, + GpencilModifierType type) +{ + GpencilModifierData *md; + + for (md = ob->greasepencil_modifiers.first; md; md = md->next) { + if ((md != exclude) && (md->type == type)) + return true; + } + + return false; +} + +static bool gpencil_object_modifier_remove( + Main *bmain, Object *ob, GpencilModifierData *md, + bool *UNUSED(r_sort_depsgraph)) +{ + /* It seems on rapid delete it is possible to + * get called twice on same modifier, so make + * sure it is in list. */ + if (BLI_findindex(&ob->greasepencil_modifiers, md) == -1) { + return 0; + } + + DEG_relations_tag_update(bmain); + + BLI_remlink(&ob->greasepencil_modifiers, md); + BKE_gpencil_modifier_free(md); + BKE_object_free_derived_caches(ob); + + return 1; +} + +bool ED_object_gpencil_modifier_remove(ReportList *reports, Main *bmain, Object *ob, GpencilModifierData *md) +{ + bool sort_depsgraph = false; + bool ok; + + ok = gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph); + + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Modifier '%s' not in object '%s'", md->name, ob->id.name); + return 0; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return 1; +} + +void ED_object_gpencil_modifier_clear(Main *bmain, Object *ob) +{ + GpencilModifierData *md = ob->greasepencil_modifiers.first; + bool sort_depsgraph = false; + + if (!md) + return; + + while (md) { + GpencilModifierData *next_md; + + next_md = md->next; + + gpencil_object_modifier_remove(bmain, ob, md, &sort_depsgraph); + + md = next_md; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); +} + +int ED_object_gpencil_modifier_move_up(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) +{ + if (md->prev) { + BLI_remlink(&ob->greasepencil_modifiers, md); + BLI_insertlinkbefore(&ob->greasepencil_modifiers, md->prev, md); + } + + return 1; +} + +int ED_object_gpencil_modifier_move_down(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) +{ + if (md->next) { + BLI_remlink(&ob->greasepencil_modifiers, md); + BLI_insertlinkafter(&ob->greasepencil_modifiers, md->next, md); + } + + return 1; +} + +static int gpencil_modifier_apply_obdata( + ReportList *reports, Main *bmain, Depsgraph *depsgraph, Object *ob, GpencilModifierData *md) +{ + const GpencilModifierTypeInfo *mti = BKE_gpencil_modifierType_getInfo(md->type); + + if (mti->isDisabled && mti->isDisabled(md, 0)) { + BKE_report(reports, RPT_ERROR, "Modifier is disabled, skipping apply"); + return 0; + } + + if (ob->type == OB_GPENCIL) { + if (ELEM(NULL, ob, ob->data)) { + return 0; + } + else if (mti->bakeModifier == NULL) { + BKE_report(reports, RPT_ERROR, "Not implemented"); + return 0; + } + mti->bakeModifier(bmain, depsgraph, md, ob); + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + else { + BKE_report(reports, RPT_ERROR, "Cannot apply modifier for this object type"); + return 0; + } + + return 1; +} + +int ED_object_gpencil_modifier_apply( + Main *bmain, ReportList *reports, Depsgraph *depsgraph, + Object *ob, GpencilModifierData *md, int UNUSED(mode)) +{ + + if (ob->type == OB_GPENCIL) { + if (ob->mode != OB_MODE_OBJECT) { + BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied in paint, sculpt or edit mode"); + return 0; + } + + if (((ID *)ob->data)->us > 1) { + BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); + return 0; + } + } + else if (((ID *)ob->data)->us > 1) { + BKE_report(reports, RPT_ERROR, "Modifiers cannot be applied to multi-user data"); + return 0; + } + + if (md != ob->greasepencil_modifiers.first) + BKE_report(reports, RPT_INFO, "Applied modifier was not first, result may not be as expected"); + + if (!gpencil_modifier_apply_obdata(reports, bmain, depsgraph, ob, md)) { + return 0; + } + + BLI_remlink(&ob->greasepencil_modifiers, md); + BKE_gpencil_modifier_free(md); + + return 1; +} + +int ED_object_gpencil_modifier_copy(ReportList *UNUSED(reports), Object *ob, GpencilModifierData *md) +{ + GpencilModifierData *nmd; + + nmd = BKE_gpencil_modifier_new(md->type); + BKE_gpencil_modifier_copyData(md, nmd); + BLI_insertlinkafter(&ob->greasepencil_modifiers, md, nmd); + BKE_gpencil_modifier_unique_name(&ob->greasepencil_modifiers, nmd); + + return 1; +} + +/************************ add modifier operator *********************/ + +static int gpencil_modifier_add_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = ED_object_active_context(C); + int type = RNA_enum_get(op->ptr, "type"); + + if (!ED_object_gpencil_modifier_add(op->reports, bmain, scene, ob, NULL, type)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static const EnumPropertyItem *gpencil_modifier_add_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + Object *ob = ED_object_active_context(C); + EnumPropertyItem *item = NULL; + const EnumPropertyItem *md_item, *group_item = NULL; + const GpencilModifierTypeInfo *mti; + int totitem = 0, a; + + if (!ob) + return rna_enum_object_greasepencil_modifier_type_items; + + for (a = 0; rna_enum_object_greasepencil_modifier_type_items[a].identifier; a++) { + md_item = &rna_enum_object_greasepencil_modifier_type_items[a]; + if (md_item->identifier[0]) { + mti = BKE_gpencil_modifierType_getInfo(md_item->value); + + if (mti->flags & eGpencilModifierTypeFlag_NoUserAdd) + continue; + } + else { + group_item = md_item; + md_item = NULL; + + continue; + } + + if (group_item) { + RNA_enum_item_add(&item, &totitem, group_item); + group_item = NULL; + } + + RNA_enum_item_add(&item, &totitem, md_item); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +void OBJECT_OT_gpencil_modifier_add(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Add Grease Pencil Modifier"; + ot->description = "Add a procedural operation/effect to the active grease pencil object"; + ot->idname = "OBJECT_OT_gpencil_modifier_add"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = gpencil_modifier_add_exec; + ot->poll = ED_operator_object_active_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "type", rna_enum_object_modifier_type_items, eGpencilModifierType_Thick, "Type", ""); + RNA_def_enum_funcs(prop, gpencil_modifier_add_itemf); + ot->prop = prop; +} + +/************************ generic functions for operators using mod names and data context *********************/ + +static int gpencil_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 (!ptr.data) { + CTX_wm_operator_poll_msg_set(C, "Context missing 'modifier'"); + return 0; + } + + if (!ob || ID_IS_LINKED(ob)) return 0; + if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) return 0; + if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) return 0; + + if (ID_IS_STATIC_OVERRIDE(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit modifiers comming from static override"); + return (((GpencilModifierData *)ptr.data)->flag & eGpencilModifierFlag_StaticOverride_Local) != 0; + } + + return 1; +} + +static bool gpencil_edit_modifier_poll(bContext *C) +{ + return gpencil_edit_modifier_poll_generic(C, &RNA_GpencilModifier, 0); +} + +static void gpencil_edit_modifier_properties(wmOperatorType *ot) +{ + RNA_def_string(ot->srna, "modifier", NULL, MAX_NAME, "Modifier", "Name of the modifier to edit"); +} + +static int gpencil_edit_modifier_invoke_properties(bContext *C, wmOperator *op) +{ + GpencilModifierData *md; + + if (RNA_struct_property_is_set(op->ptr, "modifier")) { + return true; + } + else { + PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_GpencilModifier); + if (ptr.data) { + md = ptr.data; + RNA_string_set(op->ptr, "modifier", md->name); + return true; + } + } + + return false; +} + +static GpencilModifierData *gpencil_edit_modifier_property_get(wmOperator *op, Object *ob, int type) +{ + char modifier_name[MAX_NAME]; + GpencilModifierData *md; + RNA_string_get(op->ptr, "modifier", modifier_name); + + md = BKE_gpencil_modifiers_findByName(ob, modifier_name); + + if (md && type != 0 && md->type != type) + md = NULL; + + return md; +} + +/************************ remove modifier operator *********************/ + +static int gpencil_modifier_remove_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_remove(op->reports, bmain, ob, md)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_remove_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_remove(wmOperatorType *ot) +{ + ot->name = "Remove Grease Pencil Modifier"; + ot->description = "Remove a modifier from the active grease pencil object"; + ot->idname = "OBJECT_OT_gpencil_modifier_remove"; + + ot->invoke = gpencil_modifier_remove_invoke; + ot->exec = gpencil_modifier_remove_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} + +/************************ move up modifier operator *********************/ + +static int gpencil_modifier_move_up_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_move_up(op->reports, ob, md)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_move_up_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_move_up(wmOperatorType *ot) +{ + ot->name = "Move Up Modifier"; + ot->description = "Move modifier up in the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_move_up"; + + ot->invoke = gpencil_modifier_move_up_invoke; + ot->exec = gpencil_modifier_move_up_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} + +/************************ move down modifier operator *********************/ + +static int gpencil_modifier_move_down_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_move_down(op->reports, ob, md)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_move_down_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_move_down(wmOperatorType *ot) +{ + ot->name = "Move Down Modifier"; + ot->description = "Move modifier down in the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_move_down"; + + ot->invoke = gpencil_modifier_move_down_invoke; + ot->exec = gpencil_modifier_move_down_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} + +/************************ apply modifier operator *********************/ + +static int gpencil_modifier_apply_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + int apply_as = RNA_enum_get(op->ptr, "apply_as"); + + if (!md || !ED_object_gpencil_modifier_apply(bmain, op->reports, depsgraph, ob, md, apply_as)) { + return OPERATOR_CANCELLED; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_apply_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_apply_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +static const EnumPropertyItem gpencil_modifier_apply_as_items[] = { + {MODIFIER_APPLY_DATA, "DATA", 0, "Object Data", "Apply modifier to the object's data"}, + {MODIFIER_APPLY_SHAPE, "SHAPE", 0, "New Shape", "Apply deform-only modifier to a new shape on this object"}, + {0, NULL, 0, NULL, NULL} +}; + +void OBJECT_OT_gpencil_modifier_apply(wmOperatorType *ot) +{ + ot->name = "Apply Modifier"; + ot->description = "Apply modifier and remove from the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_apply"; + + ot->invoke = gpencil_modifier_apply_invoke; + ot->exec = gpencil_modifier_apply_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_enum(ot->srna, "apply_as", gpencil_modifier_apply_as_items, MODIFIER_APPLY_DATA, "Apply as", "How to apply the modifier to the geometry"); + gpencil_edit_modifier_properties(ot); +} + +/************************ copy modifier operator *********************/ + +static int gpencil_modifier_copy_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + GpencilModifierData *md = gpencil_edit_modifier_property_get(op, ob, 0); + + if (!md || !ED_object_gpencil_modifier_copy(op->reports, ob, md)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int gpencil_modifier_copy_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (gpencil_edit_modifier_invoke_properties(C, op)) + return gpencil_modifier_copy_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_gpencil_modifier_copy(wmOperatorType *ot) +{ + ot->name = "Copy Modifier"; + ot->description = "Duplicate modifier at the same position in the stack"; + ot->idname = "OBJECT_OT_gpencil_modifier_copy"; + + ot->invoke = gpencil_modifier_copy_invoke; + ot->exec = gpencil_modifier_copy_exec; + ot->poll = gpencil_edit_modifier_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + gpencil_edit_modifier_properties(ot); +} diff --git a/source/blender/editors/object/object_group.c b/source/blender/editors/object/object_group.c index 53cabe3759e..72c5fde2955 100644 --- a/source/blender/editors/object/object_group.c +++ b/source/blender/editors/object/object_group.c @@ -411,6 +411,9 @@ static int collection_add_exec(bContext *C, wmOperator *UNUSED(op)) id_fake_user_set(&collection->id); BKE_collection_object_add(bmain, collection, ob); + DEG_id_tag_update(&collection->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; @@ -460,6 +463,9 @@ static int collection_link_exec(bContext *C, wmOperator *op) BKE_collection_object_add(bmain, collection, ob); + DEG_id_tag_update(&collection->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; @@ -500,6 +506,9 @@ static int collection_remove_exec(bContext *C, wmOperator *UNUSED(op)) BKE_collection_object_remove(bmain, collection, ob, false); + DEG_id_tag_update(&collection->id, DEG_TAG_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); return OPERATOR_FINISHED; @@ -531,6 +540,8 @@ static int collection_unlink_exec(bContext *C, wmOperator *UNUSED(op)) BKE_libblock_delete(bmain, collection); + DEG_relations_tag_update(bmain); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL); return OPERATOR_FINISHED; diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 1b5c6df2632..ef8653541f0 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -115,6 +115,7 @@ void OBJECT_OT_armature_add(struct wmOperatorType *ot); void OBJECT_OT_empty_add(struct wmOperatorType *ot); void OBJECT_OT_lightprobe_add(struct wmOperatorType *ot); void OBJECT_OT_drop_named_image(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_add(struct wmOperatorType *ot); void OBJECT_OT_light_add(struct wmOperatorType *ot); void OBJECT_OT_effector_add(struct wmOperatorType *ot); void OBJECT_OT_camera_add(struct wmOperatorType *ot); @@ -175,6 +176,20 @@ void OBJECT_OT_skin_armature_create(struct wmOperatorType *ot); void OBJECT_OT_laplaciandeform_bind(struct wmOperatorType *ot); void OBJECT_OT_surfacedeform_bind(struct wmOperatorType *ot); +/* grease pencil modifiers */ +void OBJECT_OT_gpencil_modifier_add(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_remove(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_move_up(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_move_down(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_apply(struct wmOperatorType *ot); +void OBJECT_OT_gpencil_modifier_copy(struct wmOperatorType *ot); + +/* shader fx */ +void OBJECT_OT_shaderfx_add(struct wmOperatorType *ot); +void OBJECT_OT_shaderfx_remove(struct wmOperatorType *ot); +void OBJECT_OT_shaderfx_move_up(struct wmOperatorType *ot); +void OBJECT_OT_shaderfx_move_down(struct wmOperatorType *ot); + /* object_constraint.c */ void OBJECT_OT_constraint_add(struct wmOperatorType *ot); void OBJECT_OT_constraint_add_with_targets(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index e9bd6fbce8f..b9bfb44f680 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -27,6 +27,7 @@ * actual mode switching logic is per-object type. */ +#include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_workspace_types.h" @@ -71,8 +72,14 @@ static const char *object_mode_op_string(eObjectMode mode) return "PARTICLE_OT_particle_edit_toggle"; if (mode == OB_MODE_POSE) return "OBJECT_OT_posemode_toggle"; - if (mode == OB_MODE_GPENCIL) + if (mode == OB_MODE_GPENCIL_EDIT) return "GPENCIL_OT_editmode_toggle"; + if (mode == OB_MODE_GPENCIL_PAINT) + return "GPENCIL_OT_paintmode_toggle"; + if (mode == OB_MODE_GPENCIL_SCULPT) + return "GPENCIL_OT_sculptmode_toggle"; + if (mode == OB_MODE_GPENCIL_WEIGHT) + return "GPENCIL_OT_weightmode_toggle"; return NULL; } @@ -85,8 +92,6 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) if (ob) { if (mode == OB_MODE_OBJECT) return true; - else if (mode == OB_MODE_GPENCIL) - return true; /* XXX: assume this is the case for now... */ switch (ob->type) { case OB_MESH: @@ -111,6 +116,13 @@ bool ED_object_mode_compat_test(const Object *ob, eObjectMode mode) if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) return true; break; + case OB_GPENCIL: + if (mode & (OB_MODE_EDIT | OB_MODE_GPENCIL_EDIT | OB_MODE_GPENCIL_PAINT | + OB_MODE_GPENCIL_SCULPT | OB_MODE_GPENCIL_WEIGHT)) + { + return true; + } + break; } } diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 11d0fd9f9d5..43f651b0532 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1,31 +1,31 @@ /* - * ***** BEGIN GPL LICENSE BLOCK ***** - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. - * All rights reserved. - * - * Contributor(s): Blender Foundation, 2009 - * - * ***** END GPL LICENSE BLOCK ***** - */ +* ***** BEGIN GPL LICENSE BLOCK ***** +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public License +* as published by the Free Software Foundation; either version 2 +* of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software Foundation, +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +* +* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. +* All rights reserved. +* +* Contributor(s): Blender Foundation, 2009 +* +* ***** END GPL LICENSE BLOCK ***** +*/ /** \file blender/editors/object/object_modifier.c - * \ingroup edobj - */ +* \ingroup edobj +*/ #include <math.h> @@ -117,8 +117,8 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc if (type == eModifierType_ParticleSystem) { /* don't need to worry about the new modifier's name, since that is set to the number - * of particle systems which shouldn't have too many duplicates - */ + * of particle systems which shouldn't have too many duplicates + */ new_md = object_add_particle_system(bmain, scene, ob, name); } else { @@ -182,9 +182,9 @@ ModifierData *ED_object_modifier_add(ReportList *reports, Main *bmain, Scene *sc } /* Return true if the object has a modifier of type 'type' other than - * the modifier pointed to be 'exclude', otherwise returns false. */ +* the modifier pointed to be 'exclude', otherwise returns false. */ static bool object_has_modifier(const Object *ob, const ModifierData *exclude, - ModifierType type) + ModifierType type) { ModifierData *md; @@ -197,16 +197,17 @@ static bool object_has_modifier(const Object *ob, const ModifierData *exclude, } /* If the object data of 'orig_ob' has other users, run 'callback' on - * each of them. - * - * If include_orig is true, the callback will run on 'orig_ob' too. - * - * If the callback ever returns true, iteration will stop and the - * function value will be true. Otherwise the function returns false. - */ -bool ED_object_iter_other(Main *bmain, Object *orig_ob, const bool include_orig, - bool (*callback)(Object *ob, void *callback_data), - void *callback_data) +* each of them. +* +* If include_orig is true, the callback will run on 'orig_ob' too. +* +* If the callback ever returns true, iteration will stop and the +* function value will be true. Otherwise the function returns false. +*/ +bool ED_object_iter_other( + Main *bmain, Object *orig_ob, const bool include_orig, + bool (*callback)(Object *ob, void *callback_data), + void *callback_data) { ID *ob_data_id = orig_ob->data; int users = ob_data_id->us; @@ -247,8 +248,8 @@ static bool object_has_modifier_cb(Object *ob, void *data) } /* Use with ED_object_iter_other(). Sets the total number of levels - * for any multires modifiers on the object to the int pointed to by - * callback_data. */ +* for any multires modifiers on the object to the int pointed to by +* callback_data. */ bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v) { ModifierData *md; @@ -265,20 +266,20 @@ bool ED_object_multires_update_totlevels_cb(Object *ob, void *totlevel_v) /* Return true if no modifier of type 'type' other than 'exclude' */ static bool object_modifier_safe_to_delete(Main *bmain, Object *ob, - ModifierData *exclude, - ModifierType type) + ModifierData *exclude, + ModifierType type) { return (!object_has_modifier(ob, exclude, type) && - !ED_object_iter_other(bmain, ob, false, - object_has_modifier_cb, &type)); + !ED_object_iter_other(bmain, ob, false, + object_has_modifier_cb, &type)); } static bool object_modifier_remove(Main *bmain, Object *ob, ModifierData *md, - bool *r_sort_depsgraph) + bool *r_sort_depsgraph) { /* It seems on rapid delete it is possible to - * get called twice on same modifier, so make - * sure it is in list. */ + * get called twice on same modifier, so make + * sure it is in list. */ if (BLI_findindex(&ob->modifiers, md) == -1) { return 0; } @@ -522,7 +523,7 @@ int ED_object_modifier_convert(ReportList *UNUSED(reports), Main *bmain, Scene * } static int modifier_apply_shape( - Main *bmain, ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md) + Main *bmain, ReportList *reports, Depsgraph *depsgraph, Scene *scene, Object *ob, ModifierData *md) { const ModifierTypeInfo *mti = modifierType_getInfo(md->type); @@ -532,15 +533,15 @@ static int modifier_apply_shape( } /* - * It should be ridiculously easy to extract the original verts that we want - * and form the shape data. We can probably use the CD KEYINDEX layer (or - * whatever I ended up calling it, too tired to check now), though this would - * by necessity have to make some potentially ugly assumptions about the order - * of the mesh data :-/ you can probably assume in 99% of cases that the first - * element of a given index is the original, and any subsequent duplicates are - * copies/interpolates, but that's an assumption that would need to be tested - * and then predominantly stated in comments in a half dozen headers. - */ + * It should be ridiculously easy to extract the original verts that we want + * and form the shape data. We can probably use the CD KEYINDEX layer (or + * whatever I ended up calling it, too tired to check now), though this would + * by necessity have to make some potentially ugly assumptions about the order + * of the mesh data :-/ you can probably assume in 99% of cases that the first + * element of a given index is the original, and any subsequent duplicates are + * copies/interpolates, but that's an assumption that would need to be tested + * and then predominantly stated in comments in a half dozen headers. + */ if (ob->type == OB_MESH) { Mesh *mesh_applied; @@ -563,7 +564,7 @@ static int modifier_apply_shape( key = me->key = BKE_key_add(bmain, (ID *)me); key->type = KEY_RELATIVE; /* if that was the first key block added, then it was the basis. - * Initialize it with the mesh, and add another for the modifier */ + * Initialize it with the mesh, and add another for the modifier */ kb = BKE_keyblock_add(key, NULL); BKE_keyblock_convert_from_mesh(me, key, kb); } @@ -667,8 +668,8 @@ static int modifier_apply_obdata(ReportList *reports, Depsgraph *depsgraph, Scen } int ED_object_modifier_apply( - Main *bmain, ReportList *reports, Depsgraph *depsgraph, - Scene *scene, Object *ob, ModifierData *md, int mode) + Main *bmain, ReportList *reports, Depsgraph *depsgraph, + Scene *scene, Object *ob, ModifierData *md, int mode) { int prev_mode; @@ -692,7 +693,7 @@ int ED_object_modifier_apply( BKE_report(reports, RPT_INFO, "Applied modifier was not first, result may not be as expected"); /* Get evaluated modifier, so object links pointer to evaluated data, - * but still use original object it is applied to the original mesh. */ + * but still use original object it is applied to the original mesh. */ Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); ModifierData *md_eval = (ob_eval) ? modifiers_findByName(ob_eval, md->name) : md; @@ -752,7 +753,7 @@ static int modifier_add_exec(bContext *C, wmOperator *op) } static const EnumPropertyItem *modifier_add_itemf( - bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) { Object *ob = ED_object_active_context(C); EnumPropertyItem *item = NULL; @@ -1165,8 +1166,8 @@ static int multires_higher_levels_delete_exec(bContext *C, wmOperator *op) multiresModifier_del_levels(mmd, scene, ob, 1); ED_object_iter_other(CTX_data_main(C), ob, true, - ED_object_multires_update_totlevels_cb, - &mmd->totlvl); + ED_object_multires_update_totlevels_cb, + &mmd->totlvl); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1210,8 +1211,8 @@ static int multires_subdivide_exec(bContext *C, wmOperator *op) multiresModifier_subdivide(mmd, scene, ob, 0, mmd->simple); ED_object_iter_other(CTX_data_main(C), ob, true, - ED_object_multires_update_totlevels_cb, - &mmd->totlvl); + ED_object_multires_update_totlevels_cb, + &mmd->totlvl); DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); @@ -1387,8 +1388,8 @@ void OBJECT_OT_multires_external_save(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; WM_operator_properties_filesel( - ot, FILE_TYPE_FOLDER | FILE_TYPE_BTX, FILE_SPECIAL, FILE_SAVE, - WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); + ot, FILE_TYPE_FOLDER | FILE_TYPE_BTX, FILE_SPECIAL, FILE_SAVE, + WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA); edit_modifier_properties(ot); } @@ -1480,13 +1481,13 @@ static void modifier_skin_customdata_delete(Object *ob) static bool skin_poll(bContext *C) { return (!CTX_data_edit_object(C) && - edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); + edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); } static bool skin_edit_poll(bContext *C) { return (CTX_data_edit_object(C) && - edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); + edit_modifier_poll_generic(C, &RNA_SkinModifier, (1 << OB_MESH))); } static void skin_root_clear(BMVert *bm_vert, GSet *visited, const int cd_vert_skin_offset) @@ -1579,8 +1580,8 @@ static int skin_loose_mark_clear_exec(bContext *C, wmOperator *op) BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) { MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, - bm_vert->head.data, - CD_MVERT_SKIN); + bm_vert->head.data, + CD_MVERT_SKIN); switch (action) { @@ -1636,8 +1637,8 @@ static int skin_radii_equalize_exec(bContext *C, wmOperator *UNUSED(op)) BM_ITER_MESH (bm_vert, &bm_iter, bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(bm_vert, BM_ELEM_SELECT)) { MVertSkin *vs = CustomData_bmesh_get(&bm->vdata, - bm_vert->head.data, - CD_MVERT_SKIN); + bm_vert->head.data, + CD_MVERT_SKIN); float avg = (vs->radius[0] + vs->radius[1]) * 0.5f; vs->radius[0] = vs->radius[1] = avg; @@ -1664,12 +1665,12 @@ void OBJECT_OT_skin_radii_equalize(wmOperatorType *ot) } static void skin_armature_bone_create(Object *skin_ob, - MVert *mvert, MEdge *medge, - bArmature *arm, - BLI_bitmap *edges_visited, - const MeshElemMap *emap, - EditBone *parent_bone, - int parent_v) + MVert *mvert, MEdge *medge, + bArmature *arm, + BLI_bitmap *edges_visited, + const MeshElemMap *emap, + EditBone *parent_bone, + int parent_v) { int i; @@ -1704,12 +1705,12 @@ static void skin_armature_bone_create(Object *skin_ob, } skin_armature_bone_create(skin_ob, - mvert, medge, - arm, - edges_visited, - emap, - bone, - v); + mvert, medge, + arm, + edges_visited, + emap, + bone, + v); } } @@ -1731,10 +1732,10 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, /* add vertex weights to original mesh */ CustomData_add_layer(&me->vdata, - CD_MDEFORMVERT, - CD_CALLOC, - NULL, - me->totvert); + CD_MDEFORMVERT, + CD_CALLOC, + NULL, + me->totvert); ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); arm_ob = BKE_object_add(bmain, scene, view_layer, OB_ARMATURE, NULL); @@ -1747,19 +1748,19 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, mvert_skin = CustomData_get_layer(&me->vdata, CD_MVERT_SKIN); BKE_mesh_vert_edge_map_create(&emap, &emap_mem, - me->medge, me->totvert, me->totedge); + me->medge, me->totvert, me->totedge); edges_visited = BLI_BITMAP_NEW(me->totedge, "edge_visited"); /* note: we use EditBones here, easier to set them up and use - * edit-armature functions to convert back to regular bones */ + * edit-armature functions to convert back to regular bones */ for (v = 0; v < me->totvert; v++) { if (mvert_skin[v].flag & MVERT_SKIN_ROOT) { EditBone *bone = NULL; /* Unless the skin root has just one adjacent edge, create - * a fake root bone (have it going off in the Y direction - * (arbitrary) */ + * a fake root bone (have it going off in the Y direction + * (arbitrary) */ if (emap[v].count > 1) { bone = ED_armature_ebone_add(arm, "Bone"); @@ -1772,12 +1773,12 @@ static Object *modifier_skin_armature_create(Depsgraph *depsgraph, Main *bmain, if (emap[v].count >= 1) { skin_armature_bone_create(skin_ob, - mvert, me->medge, - arm, - edges_visited, - emap, - bone, - v); + mvert, me->medge, + arm, + edges_visited, + emap, + bone, + v); } } } @@ -2092,7 +2093,7 @@ static int oceanbake_breakjob(void *UNUSED(customdata)) //return *(ob->stop); /* this is not nice yet, need to make the jobs list template better - * for identifying/acting upon various different jobs */ + * for identifying/acting upon various different jobs */ /* but for now we'll reuse the render break... */ return (G.is_break); } @@ -2166,8 +2167,8 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) } och = BKE_ocean_init_cache(omd->cachepath, modifier_path_relbase(bmain, ob), - omd->bakestart, omd->bakeend, omd->wave_scale, - omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution); + omd->bakestart, omd->bakeend, omd->wave_scale, + omd->chop_amount, omd->foam_coverage, omd->foam_fade, omd->resolution); och->time = MEM_mallocN(och->duration * sizeof(float), "foam bake time"); @@ -2176,22 +2177,22 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) /* precalculate time variable before baking */ for (f = omd->bakestart; f <= omd->bakeend; f++) { /* from physics_fluid.c: - * - * XXX: This can't be used due to an anim sys optimization that ignores recalc object animation, - * leaving it for the depgraph (this ignores object animation such as modifier properties though... :/ ) - * --> BKE_animsys_evaluate_all_animation(bmain, eval_time); - * This doesn't work with drivers: - * --> BKE_animsys_evaluate_animdata(&fsDomain->id, fsDomain->adt, eval_time, ADT_RECALC_ALL); - */ + * + * XXX: This can't be used due to an anim sys optimization that ignores recalc object animation, + * leaving it for the depgraph (this ignores object animation such as modifier properties though... :/ ) + * --> BKE_animsys_evaluate_all_animation(bmain, eval_time); + * This doesn't work with drivers: + * --> BKE_animsys_evaluate_animdata(&fsDomain->id, fsDomain->adt, eval_time, ADT_RECALC_ALL); + */ /* Modifying the global scene isn't nice, but we can do it in - * this part of the process before a threaded job is created */ + * this part of the process before a threaded job is created */ //scene->r.cfra = f; //ED_update_for_newframe(bmain, scene); /* ok, this doesn't work with drivers, but is way faster. - * let's use this for now and hope nobody wants to drive the time value... */ + * let's use this for now and hope nobody wants to drive the time value... */ BKE_animsys_evaluate_animdata(CTX_data_depsgraph(C), scene, (ID *)ob, ob->adt, f, ADT_RECALC_ANIM); och->time[i] = omd->time; @@ -2220,7 +2221,7 @@ static int ocean_bake_exec(bContext *C, wmOperator *op) /* setup job */ wm_job = WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, "Ocean Simulation", - WM_JOB_PROGRESS, WM_JOB_TYPE_OBJECT_SIM_OCEAN); + WM_JOB_PROGRESS, WM_JOB_TYPE_OBJECT_SIM_OCEAN); oj = MEM_callocN(sizeof(OceanBakeJob), "ocean bake job"); oj->owner = ob; oj->ocean = ocean; diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index 5a83d085aee..ac2eb60456f 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -40,6 +40,7 @@ #include "BKE_context.h" #include "RNA_access.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" @@ -114,6 +115,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_empty_add); WM_operatortype_append(OBJECT_OT_lightprobe_add); WM_operatortype_append(OBJECT_OT_drop_named_image); + WM_operatortype_append(OBJECT_OT_gpencil_add); WM_operatortype_append(OBJECT_OT_light_add); WM_operatortype_append(OBJECT_OT_camera_add); WM_operatortype_append(OBJECT_OT_speaker_add); @@ -146,6 +148,20 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_skin_radii_equalize); WM_operatortype_append(OBJECT_OT_skin_armature_create); + /* grease pencil modifiers */ + WM_operatortype_append(OBJECT_OT_gpencil_modifier_add); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_remove); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_up); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_move_down); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_apply); + WM_operatortype_append(OBJECT_OT_gpencil_modifier_copy); + + /* shader fx */ + WM_operatortype_append(OBJECT_OT_shaderfx_add); + WM_operatortype_append(OBJECT_OT_shaderfx_remove); + WM_operatortype_append(OBJECT_OT_shaderfx_move_up); + WM_operatortype_append(OBJECT_OT_shaderfx_move_down); + WM_operatortype_append(OBJECT_OT_correctivesmooth_bind); WM_operatortype_append(OBJECT_OT_meshdeform_bind); WM_operatortype_append(OBJECT_OT_explode_refresh); @@ -490,3 +506,26 @@ void ED_keymap_proportional_editmode(struct wmKeyConfig *UNUSED(keyconf), struct RNA_string_set(kmi->ptr, "value_2", "CONNECTED"); } } + +/** + * Map 1..3 to Vert/Edge/Face. + */ +void ED_keymap_editmesh_elem_mode(struct wmKeyConfig *UNUSED(keyconf), struct wmKeyMap *keymap) +{ + for (int i = 0; i < 4; i++) { + const bool is_extend = (i & 1); + const bool is_expand = (i & 2); + const int key_modifier = (is_extend ? KM_SHIFT : 0) | (is_expand ? KM_CTRL : 0); + for (int j = 0; j < 3; j++) { + wmKeyMapItem *kmi = WM_keymap_add_item( + keymap, "MESH_OT_select_mode", ONEKEY + j, KM_PRESS, key_modifier, 0); + RNA_enum_set(kmi->ptr, "type", SCE_SELECT_VERTEX << j); + if (is_extend) { + RNA_boolean_set(kmi->ptr, "use_extend", true); + } + if (is_expand) { + RNA_boolean_set(kmi->ptr, "use_expand", true); + } + } + } +} diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 2c3ff8b6afe..874b36ef42e 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -70,6 +70,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_displist.h" #include "BKE_global.h" +#include "BKE_gpencil.h" #include "BKE_fcurve.h" #include "BKE_idprop.h" #include "BKE_lamp.h" @@ -716,7 +717,7 @@ bool ED_object_parent_set(ReportList *reports, const bContext *C, Scene *scene, if (md) { ((CurveModifierData *)md)->object = par; } - if (par->curve_cache && par->curve_cache->path == NULL) { + if (par->runtime.curve_cache && par->runtime.curve_cache->path == NULL) { DEG_id_tag_update(&par->id, OB_RECALC_DATA); } } @@ -947,13 +948,13 @@ static int parent_set_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent return OPERATOR_INTERFACE; } -static bool parent_set_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +static bool parent_set_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop) { const char *prop_id = RNA_property_identifier(prop); - const int type = RNA_enum_get(ptr, "type"); /* Only show XMirror for PAR_ARMATURE_ENVELOPE and PAR_ARMATURE_AUTO! */ if (STREQ(prop_id, "xmirror")) { + const int type = RNA_enum_get(op->ptr, "type"); if (ELEM(type, PAR_ARMATURE_ENVELOPE, PAR_ARMATURE_AUTO)) return true; else @@ -963,18 +964,6 @@ static bool parent_set_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) return true; } -static void parent_set_ui(bContext *C, wmOperator *op) -{ - uiLayout *layout = op->layout; - wmWindowManager *wm = CTX_wm_manager(C); - PointerRNA ptr; - - RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); - - /* Main auto-draw call. */ - uiDefAutoButsRNA(layout, &ptr, parent_set_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); -} - void OBJECT_OT_parent_set(wmOperatorType *ot) { /* identifiers */ @@ -986,7 +975,7 @@ void OBJECT_OT_parent_set(wmOperatorType *ot) ot->invoke = parent_set_invoke; ot->exec = parent_set_exec; ot->poll = ED_operator_object_active; - ot->ui = parent_set_ui; + ot->poll_property = parent_set_poll_property; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1621,21 +1610,11 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) /**************************** Make Single User ********************************/ -static Object *single_object_users_object(Main *bmain, Scene *scene, Object *ob) +static Object *single_object_users_object(Main *bmain, Object *ob) { /* base gets copy of object */ Object *obn = ID_NEW_SET(ob, BKE_object_copy(bmain, ob)); - /* remap gpencil parenting */ - - if (scene->gpd) { - bGPdata *gpd = scene->gpd; - for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { - if (gpl->parent == ob) { - gpl->parent = obn; - } - } - } id_us_plus(&obn->id); id_us_min(&ob->id); @@ -1660,7 +1639,7 @@ static void single_object_users_collection(Main *bmain, Scene *scene, Collection /* an object may be in more than one collection */ if ((ob->id.newid == NULL) && ((ob->flag & flag) == flag)) { if (!ID_IS_LINKED(ob) && ob->id.us > 1) { - cob->ob = single_object_users_object(bmain, scene, cob->ob); + cob->ob = single_object_users_object(bmain, cob->ob); } } } @@ -1714,6 +1693,7 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in /* collection pointers in scene */ BKE_scene_groups_relink(scene); + /* active camera */ ID_NEW_REMAP(scene->camera); if (v3d) ID_NEW_REMAP(v3d->camera); @@ -1809,7 +1789,7 @@ static void single_obdata_users(Main *bmain, Scene *scene, ViewLayer *view_layer case OB_ARMATURE: DEG_id_tag_update(&ob->id, OB_RECALC_DATA); ob->data = ID_NEW_SET(ob->data, BKE_armature_copy(bmain, ob->data)); - BKE_pose_rebuild(bmain, ob, ob->data); + BKE_pose_rebuild(bmain, ob, ob->data, true); break; case OB_SPEAKER: ob->data = ID_NEW_SET(ob->data, BKE_speaker_copy(bmain, ob->data)); @@ -1817,6 +1797,9 @@ static void single_obdata_users(Main *bmain, Scene *scene, ViewLayer *view_layer case OB_LIGHTPROBE: ob->data = ID_NEW_SET(ob->data, BKE_lightprobe_copy(bmain, ob->data)); break; + case OB_GPENCIL: + ob->data = ID_NEW_SET(ob->data, BKE_gpencil_copy(bmain, ob->data)); + break; default: printf("ERROR %s: can't copy %s\n", __func__, id->name); BLI_assert(!"This should never happen."); @@ -1952,10 +1935,6 @@ void ED_object_single_users(Main *bmain, Scene *scene, const bool full, const bo } } - if (scene->gpd) { - IDP_RelinkProperty(scene->gpd->id.properties); - } - if (scene->world) { IDP_RelinkProperty(scene->world->id.properties); } @@ -2329,6 +2308,7 @@ static int make_override_static_exec(bContext *C, wmOperator *op) if (new_ob != NULL && new_ob->id.override_static != NULL) { if ((base = BKE_view_layer_base_find(view_layer, new_ob)) == NULL) { BKE_collection_object_add_from(bmain, scene, obcollection, new_ob); + base = BKE_view_layer_base_find(view_layer, new_ob); DEG_id_tag_update_ex(bmain, &new_ob->id, DEG_TAG_TRANSFORM | DEG_TAG_BASE_FLAGS_UPDATE); } /* parent to 'collection' empty */ diff --git a/source/blender/editors/object/object_select.c b/source/blender/editors/object/object_select.c index d5f7a93cc6e..c23a1d64ee8 100644 --- a/source/blender/editors/object/object_select.c +++ b/source/blender/editors/object/object_select.c @@ -41,6 +41,7 @@ #include "DNA_armature_types.h" #include "DNA_lamp_types.h" #include "DNA_workspace_types.h" +#include "DNA_gpencil_types.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -88,8 +89,8 @@ * this takes into account the 'restrict selection in 3d view' flag. * deselect works always, the restriction just prevents selection */ -/* Note: send a NC_SCENE|ND_OB_SELECT notifier yourself! (or - * or a NC_SCENE|ND_OB_VISIBLE in case of visibility toggling */ + /* Note: send a NC_SCENE|ND_OB_SELECT notifier yourself! (or + * or a NC_SCENE|ND_OB_VISIBLE in case of visibility toggling */ void ED_object_base_select(Base *base, eObjectSelect_Mode mode) { diff --git a/source/blender/editors/object/object_shader_fx.c b/source/blender/editors/object/object_shader_fx.c new file mode 100644 index 00000000000..681851850a5 --- /dev/null +++ b/source/blender/editors/object/object_shader_fx.c @@ -0,0 +1,469 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2018 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2018 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/object/object_shader_fx.c + * \ingroup edobj + */ + + +#include <math.h> +#include <stdio.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_gpencil_types.h" +#include "DNA_shader_fx_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_math.h" +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_string_utf8.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_main.h" +#include "BKE_shader_fx.h" +#include "BKE_report.h" +#include "BKE_object.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_enum_types.h" + +#include "ED_object.h" +#include "ED_screen.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "object_intern.h" + +/******************************** API ****************************/ + +ShaderFxData *ED_object_shaderfx_add(ReportList *reports, Main *bmain, Scene *UNUSED(scene), Object *ob, const char *name, int type) +{ + ShaderFxData *new_fx = NULL; + const ShaderFxTypeInfo *fxi = BKE_shaderfxType_getInfo(type); + + if (ob->type != OB_GPENCIL) { + BKE_reportf(reports, RPT_WARNING, "Effect cannot be added to object '%s'", ob->id.name + 2); + return NULL; + } + + if (fxi->flags & eShaderFxTypeFlag_Single) { + if (BKE_shaderfx_findByType(ob, type)) { + BKE_report(reports, RPT_WARNING, "Only one Effect of this type is allowed"); + return NULL; + } + } + + /* get new effect data to add */ + new_fx = BKE_shaderfx_new(type); + + BLI_addtail(&ob->shader_fx, new_fx); + + if (name) { + BLI_strncpy_utf8(new_fx->name, name, sizeof(new_fx->name)); + } + + /* make sure effect data has unique name */ + BKE_shaderfx_unique_name(&ob->shader_fx, new_fx); + + bGPdata *gpd = ob->data; + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return new_fx; +} + +/* Return true if the object has a effect of type 'type' other than + * the shaderfx pointed to be 'exclude', otherwise returns false. */ +static bool UNUSED_FUNCTION(object_has_shaderfx)( + const Object *ob, const ShaderFxData *exclude, + ShaderFxType type) +{ + ShaderFxData *fx; + + for (fx = ob->shader_fx.first; fx; fx = fx->next) { + if ((fx != exclude) && (fx->type == type)) + return true; + } + + return false; +} + +static bool object_shaderfx_remove( + Main *bmain, Object *ob, ShaderFxData *fx, + bool *UNUSED(r_sort_depsgraph)) +{ + /* It seems on rapid delete it is possible to + * get called twice on same effect, so make + * sure it is in list. */ + if (BLI_findindex(&ob->shader_fx, fx) == -1) { + return 0; + } + + DEG_relations_tag_update(bmain); + + BLI_remlink(&ob->shader_fx, fx); + BKE_shaderfx_free(fx); + BKE_object_free_derived_caches(ob); + + return 1; +} + +bool ED_object_shaderfx_remove(ReportList *reports, Main *bmain, Object *ob, ShaderFxData *fx) +{ + bool sort_depsgraph = false; + bool ok; + + ok = object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph); + + if (!ok) { + BKE_reportf(reports, RPT_ERROR, "Effect '%s' not in object '%s'", fx->name, ob->id.name); + return 0; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); + + return 1; +} + +void ED_object_shaderfx_clear(Main *bmain, Object *ob) +{ + ShaderFxData *fx = ob->shader_fx.first; + bool sort_depsgraph = false; + + if (!fx) + return; + + while (fx) { + ShaderFxData *next_fx; + + next_fx = fx->next; + + object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph); + + fx = next_fx; + } + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_relations_tag_update(bmain); +} + +int ED_object_shaderfx_move_up(ReportList *UNUSED(reports), Object *ob, ShaderFxData *fx) +{ + if (fx->prev) { + BLI_remlink(&ob->shader_fx, fx); + BLI_insertlinkbefore(&ob->shader_fx, fx->prev, fx); + } + + return 1; +} + +int ED_object_shaderfx_move_down(ReportList *UNUSED(reports), Object *ob, ShaderFxData *fx) +{ + if (fx->next) { + BLI_remlink(&ob->shader_fx, fx); + BLI_insertlinkafter(&ob->shader_fx, fx->next, fx); + } + + return 1; +} + +/************************ add effect operator *********************/ + +static int shaderfx_add_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Object *ob = ED_object_active_context(C); + int type = RNA_enum_get(op->ptr, "type"); + + if (!ED_object_shaderfx_add(op->reports, bmain, scene, ob, NULL, type)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static const EnumPropertyItem *shaderfx_add_itemf( + bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + Object *ob = ED_object_active_context(C); + EnumPropertyItem *item = NULL; + const EnumPropertyItem *fx_item, *group_item = NULL; + const ShaderFxTypeInfo *mti; + int totitem = 0, a; + + if (!ob) + return rna_enum_object_shaderfx_type_items; + + for (a = 0; rna_enum_object_shaderfx_type_items[a].identifier; a++) { + fx_item = &rna_enum_object_shaderfx_type_items[a]; + if (fx_item->identifier[0]) { + mti = BKE_shaderfxType_getInfo(fx_item->value); + + if (mti->flags & eShaderFxTypeFlag_NoUserAdd) + continue; + } + else { + group_item = fx_item; + fx_item = NULL; + + continue; + } + + if (group_item) { + RNA_enum_item_add(&item, &totitem, group_item); + group_item = NULL; + } + + RNA_enum_item_add(&item, &totitem, fx_item); + } + + RNA_enum_item_end(&item, &totitem); + *r_free = true; + + return item; +} + +void OBJECT_OT_shaderfx_add(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name = "Add Effect"; + ot->description = "Add a visual effect to the active object"; + ot->idname = "OBJECT_OT_shaderfx_add"; + + /* api callbacks */ + ot->invoke = WM_menu_invoke; + ot->exec = shaderfx_add_exec; + ot->poll = ED_operator_object_active_editable; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_enum(ot->srna, "type", rna_enum_object_shaderfx_type_items, eShaderFxType_Blur, "Type", ""); + RNA_def_enum_funcs(prop, shaderfx_add_itemf); + ot->prop = prop; +} + +/************************ generic functions for operators using names and data context *********************/ + +static bool edit_shaderfx_poll_generic(bContext *C, StructRNA *rna_type, int obtype_flag) +{ + PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type); + Object *ob = (ptr.id.data) ? ptr.id.data : ED_object_active_context(C); + + if (!ptr.data) { + CTX_wm_operator_poll_msg_set(C, "Context missing 'shaderfx'"); + return 0; + } + + if (!ob || ID_IS_LINKED(ob)) return 0; + if (obtype_flag && ((1 << ob->type) & obtype_flag) == 0) return 0; + if (ptr.id.data && ID_IS_LINKED(ptr.id.data)) return 0; + + if (ID_IS_STATIC_OVERRIDE(ob)) { + CTX_wm_operator_poll_msg_set(C, "Cannot edit shaderfxs comming from static override"); + return (((ShaderFxData *)ptr.data)->flag & eShaderFxFlag_StaticOverride_Local) != 0; + } + + return 1; +} + +static bool edit_shaderfx_poll(bContext *C) +{ + return edit_shaderfx_poll_generic(C, &RNA_ShaderFx, 0); +} + +static void edit_shaderfx_properties(wmOperatorType *ot) +{ + RNA_def_string(ot->srna, "shaderfx", NULL, MAX_NAME, "Shader", "Name of the shaderfx to edit"); +} + +static int edit_shaderfx_invoke_properties(bContext *C, wmOperator *op) +{ + ShaderFxData *fx; + + if (RNA_struct_property_is_set(op->ptr, "shaderfx")) { + return true; + } + else { + PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", &RNA_ShaderFx); + if (ptr.data) { + fx = ptr.data; + RNA_string_set(op->ptr, "shaderfx", fx->name); + return true; + } + } + + return false; +} + +static ShaderFxData *edit_shaderfx_property_get(wmOperator *op, Object *ob, int type) +{ + char shaderfx_name[MAX_NAME]; + ShaderFxData *fx; + RNA_string_get(op->ptr, "shaderfx", shaderfx_name); + + fx = BKE_shaderfx_findByName(ob, shaderfx_name); + + if (fx && type != 0 && fx->type != type) + fx = NULL; + + return fx; +} + +/************************ remove shaderfx operator *********************/ + +static int shaderfx_remove_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Object *ob = ED_object_active_context(C); + ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + + if (!fx || !ED_object_shaderfx_remove(op->reports, bmain, ob, fx)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_shaderfx_invoke_properties(C, op)) + return shaderfx_remove_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_shaderfx_remove(wmOperatorType *ot) +{ + ot->name = "Remove Grease Pencil Modifier"; + ot->description = "Remove a shaderfx from the active grease pencil object"; + ot->idname = "OBJECT_OT_shaderfx_remove"; + + ot->invoke = shaderfx_remove_invoke; + ot->exec = shaderfx_remove_exec; + ot->poll = edit_shaderfx_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_shaderfx_properties(ot); +} + +/************************ move up shaderfx operator *********************/ + +static int shaderfx_move_up_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + + if (!fx || !ED_object_shaderfx_move_up(op->reports, ob, fx)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_shaderfx_invoke_properties(C, op)) + return shaderfx_move_up_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot) +{ + ot->name = "Move Up Modifier"; + ot->description = "Move shaderfx up in the stack"; + ot->idname = "OBJECT_OT_shaderfx_move_up"; + + ot->invoke = shaderfx_move_up_invoke; + ot->exec = shaderfx_move_up_exec; + ot->poll = edit_shaderfx_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_shaderfx_properties(ot); +} + +/************************ move down shaderfx operator *********************/ + +static int shaderfx_move_down_exec(bContext *C, wmOperator *op) +{ + Object *ob = ED_object_active_context(C); + ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0); + + if (!fx || !ED_object_shaderfx_move_down(op->reports, ob, fx)) + return OPERATOR_CANCELLED; + + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob); + + return OPERATOR_FINISHED; +} + +static int shaderfx_move_down_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + if (edit_shaderfx_invoke_properties(C, op)) + return shaderfx_move_down_exec(C, op); + else + return OPERATOR_CANCELLED; +} + +void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot) +{ + ot->name = "Move Down Modifier"; + ot->description = "Move shaderfx down in the stack"; + ot->idname = "OBJECT_OT_shaderfx_move_down"; + + ot->invoke = shaderfx_move_down_invoke; + ot->exec = shaderfx_move_down_exec; + ot->poll = edit_shaderfx_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + edit_shaderfx_properties(ot); +} diff --git a/source/blender/editors/object/object_shapekey.c b/source/blender/editors/object/object_shapekey.c index 5e66dc00fd2..30fb2896670 100644 --- a/source/blender/editors/object/object_shapekey.c +++ b/source/blender/editors/object/object_shapekey.c @@ -378,8 +378,10 @@ static int shape_key_retime_exec(bContext *C, wmOperator *UNUSED(op)) if (!key || !kb) return OPERATOR_CANCELLED; - for (kb = key->block.first; kb; kb = kb->next) - kb->pos = (cfra += 0.1f); + for (kb = key->block.first; kb; kb = kb->next) { + kb->pos = cfra; + cfra += 0.1f; + } DEG_id_tag_update(&ob->id, OB_RECALC_DATA); WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); diff --git a/source/blender/editors/object/object_transform.c b/source/blender/editors/object/object_transform.c index d2a0879464b..96b540251b4 100644 --- a/source/blender/editors/object/object_transform.c +++ b/source/blender/editors/object/object_transform.c @@ -38,6 +38,7 @@ #include "DNA_lamp_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lattice_types.h" @@ -59,6 +60,7 @@ #include "BKE_armature.h" #include "BKE_lattice.h" #include "BKE_tracking.h" +#include "BKE_gpencil.h" #include "DEG_depsgraph.h" @@ -73,6 +75,7 @@ #include "ED_mesh.h" #include "ED_screen.h" #include "ED_view3d.h" +#include "ED_gpencil.h" #include "MEM_guardedalloc.h" @@ -434,7 +437,7 @@ static int apply_objects_internal( /* first check if we can execute */ CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (ELEM(ob->type, OB_MESH, OB_ARMATURE, OB_LATTICE, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT)) { + if (ELEM(ob->type, OB_MESH, OB_ARMATURE, OB_LATTICE, OB_MBALL, OB_CURVE, OB_SURF, OB_FONT, OB_GPENCIL)) { ID *obdata = ob->data; if (ID_REAL_USERS(obdata) > 1) { BKE_reportf(reports, RPT_ERROR, @@ -480,6 +483,37 @@ static int apply_objects_internal( } } + if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + if (gpd) { + if (gpd->layers.first) { + /* Unsupported configuration */ + bool has_unparented_layers = false; + + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* Parented layers aren't supported as we can't easily re-evaluate the scene to sample parent movement */ + if (gpl->parent == NULL) { + has_unparented_layers = true; + break; + } + } + + if (has_unparented_layers == false) { + BKE_reportf(reports, RPT_ERROR, + "Can't apply to a GP datablock where all layers are parented: Object \"%s\", %s \"%s\", aborting", + ob->id.name + 2, BKE_idcode_to_name(ID_GD), gpd->id.name + 2); + changed = false; + } + } + else { + /* No layers/data */ + BKE_reportf(reports, RPT_ERROR, + "Can't apply to GP datablock with no layers: Object \"%s\", %s \"%s\", aborting", + ob->id.name + 2, BKE_idcode_to_name(ID_GD), gpd->id.name + 2); + } + } + } + if (ob->type == OB_LAMP) { Lamp *la = ob->data; if (la->type == LA_AREA) { @@ -587,6 +621,10 @@ static int apply_objects_internal( cu->fsize *= scale; } } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + BKE_gpencil_transform(gpd, mat); + } else if (ob->type == OB_CAMERA) { MovieClip *clip = BKE_object_movieclip_get(scene, ob, false); @@ -1056,6 +1094,69 @@ static int object_origin_set_exec(bContext *C, wmOperator *op) lt->id.tag |= LIB_TAG_DOIT; do_inverse_offset = true; } + else if (ob->type == OB_GPENCIL) { + bGPdata *gpd = ob->data; + float gpcenter[3]; + if (gpd) { + if (centermode == ORIGIN_TO_GEOMETRY) { + zero_v3(gpcenter); + BKE_gpencil_centroid_3D(gpd, gpcenter); + add_v3_v3(gpcenter, ob->obmat[3]); + } + if (centermode == ORIGIN_TO_CURSOR) { + copy_v3_v3(gpcenter, cursor); + } + if ((centermode == ORIGIN_TO_GEOMETRY) || (centermode == ORIGIN_TO_CURSOR)) { + bGPDspoint *pt; + float imat[3][3], bmat[3][3]; + float offset_global[3]; + float offset_local[3]; + int i; + + sub_v3_v3v3(offset_global, gpcenter, ob->obmat[3]); + copy_m3_m4(bmat, obact->obmat); + invert_m3_m3(imat, bmat); + mul_m3_v3(imat, offset_global); + mul_v3_m3v3(offset_local, imat, offset_global); + + float diff_mat[4][4]; + float inverse_diff_mat[4][4]; + + /* recalculate all strokes (all layers are considered without evaluating lock attributtes) */ + for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) { + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat); + /* undo matrix */ + invert_m4_m4(inverse_diff_mat, diff_mat); + for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) { + for (bGPDstroke *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; + + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + float mpt[3]; + mul_v3_m4v3(mpt, inverse_diff_mat, &pt->x); + sub_v3_v3(mpt, offset_local); + mul_v3_m4v3(&pt->x, diff_mat, mpt); + } + } + } + } + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); + + tot_change++; + if (centermode == ORIGIN_TO_GEOMETRY) { + copy_v3_v3(ob->loc, gpcenter); + } + ob->id.tag |= LIB_TAG_DOIT; + do_inverse_offset = true; + } + else { + BKE_report(op->reports, RPT_WARNING, "Grease Pencil Object does not support this set origin option"); + } + } + } /* offset other selected objects */ if (do_inverse_offset && (centermode != GEOMETRY_TO_ORIGIN)) { diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 22000bd2a03..ed7950f3993 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -329,7 +329,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R GPU_matrix_translate_2f(sizex / 2, sizey / 2); G.f |= G_RENDER_OGL; - ED_gpencil_draw_ex(scene, gpd, sizex, sizey, scene->r.cfra, SPACE_SEQ); + ED_gpencil_draw_ex(rv3d, scene, gpd, sizex, sizey, scene->r.cfra, SPACE_SEQ); G.f &= ~G_RENDER_OGL; gp_rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect"); @@ -417,7 +417,7 @@ 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]) +static void UNUSED_FUNCTION(addAlphaOverFloat)(float dest[4], const float source[4]) { /* d = s + (1-alpha_s)d*/ float mul; @@ -431,91 +431,6 @@ static void addAlphaOverFloat(float dest[4], const float source[4]) } -/* add renderlayer and renderpass for each grease pencil layer for using in composition */ -static void add_gpencil_renderpass(const bContext *C, 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; - } - if (oglrender->v3d != NULL && (oglrender->v3d->flag2 & V3D_SHOW_GPENCIL) == 0) { - 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(C, oglrender, rr); - - /* add RendePass composite */ - RenderPass *rp = RE_create_gp_pass(rr, gpl->info, rv->name); - - /* copy image data from rectf */ - // XXX: Needs conversion. - unsigned char *src = (unsigned char *)RE_RenderViewGetById(rr, oglrender->view_id)->rect32; - if (src != NULL) { - float *dest = rp->rect; - - int x, y, rectx, recty; - rectx = rr->rectx; - recty = rr->recty; - for (y = 0; y < recty; y++) { - for (x = 0; x < rectx; x++) { - unsigned char *pixSrc = src + 4 * (rectx * y + x); - if (pixSrc[3] > 0) { - float *pixDest = dest + 4 * (rectx * y + x); - float float_src[4]; - srgb_to_linearrgb_uchar4(float_src, pixSrc); - addAlphaOverFloat(pixDest, float_src); - } - } - } - } - /* 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(const bContext *C, OGLRender *oglrender) { RenderResult *rr; @@ -550,11 +465,6 @@ static void screen_opengl_render_apply(const bContext *C, OGLRender *oglrender) BLI_assert(view_id < oglrender->views_len); RE_SetActiveRenderView(oglrender->re, rv->name); oglrender->view_id = view_id; - /* add grease pencil passes. For sequencer, the render does not include renderpasses - * TODO: The sequencer render of grease pencil should be rethought */ - if (!oglrender->is_sequencer) { - add_gpencil_renderpass(C, oglrender, rr, rv); - } /* render composite */ screen_opengl_render_doit(C, oglrender, rr); } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 3423eedf7ca..069611be35d 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -198,6 +198,7 @@ typedef struct IconPreview { static Main *G_pr_main = NULL; static Main *G_pr_main_cycles = NULL; +static Main *G_pr_main_grease_pencil = NULL; #ifndef WITH_HEADLESS static Main *load_main_from_memory(const void *blend, int blend_size) @@ -227,6 +228,7 @@ void ED_preview_ensure_dbase(void) if (!base_initialized) { G_pr_main = load_main_from_memory(datatoc_preview_blend, datatoc_preview_blend_size); G_pr_main_cycles = load_main_from_memory(datatoc_preview_cycles_blend, datatoc_preview_cycles_blend_size); + G_pr_main_grease_pencil = load_main_from_memory(datatoc_preview_grease_pencil_blend, datatoc_preview_grease_pencil_blend_size); base_initialized = true; } #endif @@ -245,6 +247,9 @@ void ED_preview_free_dbase(void) if (G_pr_main_cycles) BKE_main_free(G_pr_main_cycles); + + if (G_pr_main_grease_pencil) + BKE_main_free(G_pr_main_grease_pencil); } static Scene *preview_get_scene(Main *pr_main) @@ -1102,6 +1107,7 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short sp->id_copy = ip->id_copy; sp->bmain = ip->bmain; sp->own_id_copy = false; + Material *ma = NULL; if (is_render) { BLI_assert(ip->id); @@ -1109,10 +1115,22 @@ static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short * so don't even think of using cycle's bmain for * texture icons */ - if (GS(ip->id->name) != ID_TE) - sp->pr_main = G_pr_main_cycles; - else + if (GS(ip->id->name) != ID_TE) { + /* grease pencil use its own preview file */ + if (GS(ip->id->name) == ID_MA) { + ma = (Material *)ip->id; + } + + if ((ma == NULL) || (ma->gp_style == NULL)) { + sp->pr_main = G_pr_main_cycles; + } + else { + sp->pr_main = G_pr_main_grease_pencil; + } + } + else { sp->pr_main = G_pr_main; + } } common_preview_startjob(sp, stop, do_update, progress); @@ -1274,11 +1292,23 @@ void ED_preview_shader_job(const bContext *C, void *owner, ID *id, ID *parent, M sp->parent = parent; sp->slot = slot; sp->bmain = CTX_data_main(C); + Material *ma = NULL; /* hardcoded preview .blend for Eevee + Cycles, this should be solved * once with custom preview .blend path for external engines */ if ((method != PR_NODE_RENDER) && id_type != ID_TE) { - sp->pr_main = G_pr_main_cycles; + /* grease pencil use its own preview file */ + if (GS(id->name) == ID_MA) { + ma = (Material *)id; + } + + if ((ma == NULL) || (ma->gp_style == NULL)) { + sp->pr_main = G_pr_main_cycles; + } + else { + sp->pr_main = G_pr_main_grease_pencil; + } + } else { sp->pr_main = G_pr_main; diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 8077079a9b5..2dd4f328d06 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -469,6 +469,7 @@ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) { Material *ma = CTX_data_pointer_get_type(C, "material", &RNA_Material).data; Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); PointerRNA ptr, idptr; PropertyRNA *prop; @@ -477,7 +478,12 @@ static int new_material_exec(bContext *C, wmOperator *UNUSED(op)) ma = BKE_material_copy(bmain, ma); } else { - ma = BKE_material_add(bmain, DATA_("Material")); + if ((!ob) || (ob->type != OB_GPENCIL)) { + ma = BKE_material_add(bmain, DATA_("Material")); + } + else { + ma = BKE_material_add_gpencil(bmain, DATA_("Material")); + } ED_node_shader_default(C, &ma->id); ma->use_nodes = true; } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 1a63bc1cd53..18bacee98b9 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -1428,18 +1428,32 @@ static void ed_default_handlers(wmWindowManager *wm, ScrArea *sa, ListBase *hand } if (flag & ED_KEYMAP_GPENCIL) { /* grease pencil */ - /* NOTE: This is now 2 keymaps - One for basic functionality, - * and one that only applies when "Edit Mode" is enabled - * for strokes. + /* NOTE: This is now 4 keymaps - One for basic functionality, + * and others for special stroke modes (edit, paint and sculpt). * - * For now, it's easier to just include both, - * since you hardly want one without the other. + * For now, it's easier to just include all, + * since you hardly want one without the others. */ wmKeyMap *keymap_general = WM_keymap_find(wm->defaultconf, "Grease Pencil", 0, 0); - wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0); - WM_event_add_keymap_handler(handlers, keymap_general); + + wmKeyMap *keymap_edit = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Edit Mode", 0, 0); WM_event_add_keymap_handler(handlers, keymap_edit); + + wmKeyMap *keymap_paint = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint Mode", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint); + + wmKeyMap *keymap_paint_draw = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Draw brush)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint_draw); + + wmKeyMap *keymap_paint_erase = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Erase)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint_erase); + + wmKeyMap *keymap_paint_fill = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Paint (Fill)", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_paint_fill); + + wmKeyMap *keymap_sculpt = WM_keymap_find(wm->defaultconf, "Grease Pencil Stroke Sculpt Mode", 0, 0); + WM_event_add_keymap_handler(handlers, keymap_sculpt); } if (flag & ED_KEYMAP_HEADER) { /* standard keymap for headers regions */ diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 17b1af29010..ecfc9f2cca0 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -34,6 +34,7 @@ #include "DNA_object_types.h" #include "DNA_armature_types.h" +#include "DNA_brush_types.h" #include "DNA_gpencil_types.h" #include "DNA_sequence_types.h" #include "DNA_scene_types.h" @@ -44,10 +45,13 @@ #include "BLI_utildefines.h" +#include "BKE_brush.h" #include "BKE_context.h" #include "BKE_object.h" #include "BKE_action.h" #include "BKE_armature.h" +#include "BKE_paint.h" +#include "BKE_main.h" #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_screen.h" @@ -80,8 +84,7 @@ 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_palette", - "active_gpencil_palettecolor", "active_gpencil_brush", + "active_gpencil_layer", "active_gpencil_frame", "active_gpencil_brush", "active_operator", "selected_editable_fcurves", NULL}; @@ -467,7 +470,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult * (as outlined above - see Campbell's #ifdefs). That causes the get_active function to fail when * called from context. For that reason, we end up using an alternative where we pass everything in! */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { CTX_data_id_pointer_set(result, &gpd->id); @@ -482,7 +485,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult PointerRNA ptr; /* get pointer to Grease Pencil Data */ - gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, scene, sa, obact, &ptr); + gpd_ptr = ED_gpencil_data_get_pointers_direct((ID *)sc, sa, scene, obact, &ptr); if (gpd_ptr) { CTX_data_pointer_set(result, ptr.id.data, ptr.type, ptr.data); @@ -491,7 +494,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "active_gpencil_layer")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); @@ -502,47 +505,17 @@ 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 = BKE_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 = BKE_gpencil_palette_getactive(gpd); - - if (palette) { - bGPDpalettecolor *palcolor = BKE_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 = BKE_gpencil_brush_getactive(scene->toolsettings); + Brush *brush = BKE_brush_getactive_gpencil(scene->toolsettings); if (brush) { - CTX_data_pointer_set(result, &scene->id, &RNA_GPencilBrush, brush); + CTX_data_pointer_set(result, &scene->id, &RNA_Brush, 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); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd); @@ -555,7 +528,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "visible_gpencil_layers")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl; @@ -571,7 +544,7 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "editable_gpencil_layers")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); if (gpd) { bGPDlayer *gpl; @@ -587,24 +560,37 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } else if (CTX_data_equals(member, "editable_gpencil_strokes")) { /* XXX: see comment for gpencil_data case... */ - bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, scene, sa, obact); + bGPdata *gpd = ED_gpencil_data_get_active_direct((ID *)sc, sa, scene, obact); + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); if (gpd) { bGPDlayer *gpl; for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { if (gpencil_layer_is_editable(gpl) && (gpl->actframe)) { - bGPDframe *gpf = gpl->actframe; + bGPDframe *gpf; bGPDstroke *gps; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - 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; + for (gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + 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(obact, gpl, gps) == false) { + continue; + } + + CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); + } } - - CTX_data_list_add(result, &gpd->id, &RNA_GPencilStroke, gps); + } + /* if not multiedit out of loop */ + if (!is_multiedit) { + break; } } } diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index e2d6a9aa6ca..50ac5dfd224 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -2618,11 +2618,14 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) /* populate tree with keyframe nodes */ scene_to_keylist(&ads, scene, &keys, NULL); - gpencil_to_keylist(&ads, scene->gpd, &keys); if (ob) { ob_to_keylist(&ads, ob, &keys, NULL); - gpencil_to_keylist(&ads, ob->gpd, &keys); + + if (ob->type == OB_GPENCIL) { + const bool active = !(scene->flag & SCE_KEYS_NO_SELONLY); + gpencil_to_keylist(&ads, ob->data, &keys, active); + } } { diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index afaad3963d5..9c0c59a7160 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -247,7 +247,7 @@ static void screenshot_cancel(bContext *UNUSED(C), wmOperator *op) screenshot_data_free(op); } -static bool screenshot_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +static bool screenshot_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop, void *UNUSED(user_data)) { const char *prop_id = RNA_property_identifier(prop); @@ -266,7 +266,7 @@ static void screenshot_draw(bContext *UNUSED(C), wmOperator *op) /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); - uiDefAutoButsRNA(layout, &ptr, screenshot_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); + uiDefAutoButsRNA(layout, &ptr, screenshot_draw_check_prop, NULL, UI_BUT_LABEL_ALIGN_NONE, false); } static bool screenshot_poll(bContext *C) diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index 0f796020d9e..86d36ade477 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -29,20 +29,27 @@ #include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_math_vector.h" +#include "BLI_math_color.h" #include "DNA_customdata_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_brush_types.h" +#include "DNA_gpencil_types.h" #include "BKE_brush.h" #include "BKE_context.h" #include "BKE_paint.h" +#include "BKE_gpencil.h" #include "BKE_main.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" #include "ED_paint.h" #include "ED_screen.h" #include "ED_image.h" +#include "ED_gpencil.h" #include "UI_resources.h" #include "WM_api.h" @@ -96,6 +103,43 @@ static void BRUSH_OT_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +static int brush_add_gpencil_exec(bContext *C, wmOperator *UNUSED(op)) +{ + /*int type = RNA_enum_get(op->ptr, "type");*/ + ToolSettings *ts = CTX_data_tool_settings(C); + Paint *paint = &ts->gp_paint->paint; + Brush *br = BKE_paint_brush(paint); + Main *bmain = CTX_data_main(C); + // ePaintMode mode = ePaintGpencil; + + if (br) { + br = BKE_brush_copy(bmain, br); + } + else { + br = BKE_brush_add(bmain, "Brush", OB_MODE_GPENCIL_PAINT); + id_us_min(&br->id); /* fake user only */ + } + + BKE_paint_brush_set(paint, br); + + /* TODO init grease pencil specific data */ + + return OPERATOR_FINISHED; +} + +static void BRUSH_OT_add_gpencil(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Drawing Brush"; + ot->description = "Add brush for grease pencil"; + ot->idname = "BRUSH_OT_add_gpencil"; + + /* api callbacks */ + ot->exec = brush_add_gpencil_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} static int brush_scale_size_exec(bContext *C, wmOperator *op) { @@ -232,7 +276,6 @@ static void PALETTE_OT_color_add(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } - static int palette_color_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Paint *paint = BKE_paint_get_active_from_context(C); @@ -1031,6 +1074,7 @@ void ED_operatortypes_paint(void) /* brush */ WM_operatortype_append(BRUSH_OT_add); + WM_operatortype_append(BRUSH_OT_add_gpencil); WM_operatortype_append(BRUSH_OT_scale_size); WM_operatortype_append(BRUSH_OT_curve_preset); WM_operatortype_append(BRUSH_OT_reset); diff --git a/source/blender/editors/sound/sound_ops.c b/source/blender/editors/sound/sound_ops.c index 3fc68a50057..15f688549a8 100644 --- a/source/blender/editors/sound/sound_ops.c +++ b/source/blender/editors/sound/sound_ops.c @@ -482,7 +482,7 @@ static int sound_mixdown_invoke(bContext *C, wmOperator *op, const wmEvent *even #ifdef WITH_AUDASPACE -static bool sound_mixdown_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +static bool sound_mixdown_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop, void *UNUSED(user_data)) { const char *prop_id = RNA_property_identifier(prop); return !(STREQ(prop_id, "filepath") || @@ -635,7 +635,7 @@ static void sound_mixdown_draw(bContext *C, wmOperator *op) RNA_pointer_create(&wm->id, op->type->srna, op->properties, &ptr); /* main draw call */ - uiDefAutoButsRNA(layout, &ptr, sound_mixdown_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); + uiDefAutoButsRNA(layout, &ptr, sound_mixdown_draw_check_prop, NULL, UI_BUT_LABEL_ALIGN_NONE, false); } #endif // WITH_AUDASPACE diff --git a/source/blender/editors/space_action/action_select.c b/source/blender/editors/space_action/action_select.c index c46d0fdb035..831c461538a 100644 --- a/source/blender/editors/space_action/action_select.c +++ b/source/blender/editors/space_action/action_select.c @@ -51,6 +51,7 @@ #include "BKE_fcurve.h" #include "BKE_nla.h" #include "BKE_context.h" +#include "BKE_gpencil.h" #include "UI_view2d.h" @@ -134,15 +135,20 @@ static void deselect_action_keys(bAnimContext *ac, short test, short sel) /* Now set the flags */ for (ale = anim_data.first; ale; ale = ale->next) { - if (ale->type == ANIMTYPE_GPLAYER) + if (ale->type == ANIMTYPE_GPLAYER) { ED_gplayer_frame_select_set(ale->data, sel); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frame_select_set(ale->data, sel); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, NULL, sel_cb, NULL); + } } /* Cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -283,12 +289,16 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { ED_gplayer_frames_select_border(gpl, rectf.xmin, rectf.xmax, selectmode); } + ale->update |= ANIM_UPDATE_DEPS; break; } #endif case ANIMTYPE_GPLAYER: + { ED_gplayer_frames_select_border(ale->data, rectf.xmin, rectf.xmax, selectmode); + ale->update |= ANIM_UPDATE_DEPS; break; + } case ANIMTYPE_MASKDATABLOCK: { Mask *mask = ale->data; @@ -312,6 +322,7 @@ static void borderselect_action(bAnimContext *ac, const rcti rect, short mode, s } /* cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -493,6 +504,7 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, case ANIMTYPE_GPLAYER: { ED_gplayer_frames_select_region(&ked, ale->data, mode, selectmode); + ale->update |= ANIM_UPDATE_DEPS; break; } case ANIMTYPE_MASKDATABLOCK: @@ -520,6 +532,7 @@ static void region_select_action_keys(bAnimContext *ac, const rctf *rectf_view, } /* cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -707,6 +720,7 @@ static void markers_selectkeys_between(bAnimContext *ac) } else if (ale->type == ANIMTYPE_GPLAYER) { ED_gplayer_frames_select_border(ale->data, min, max, SELECT_ADD); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frames_select_border(ale->data, min, max, SELECT_ADD); @@ -717,6 +731,7 @@ static void markers_selectkeys_between(bAnimContext *ac) } /* Cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -796,17 +811,23 @@ static void columnselect_action_keys(bAnimContext *ac, short mode) ked.f1 = ce->cfra; /* select elements with frame number matching cfraelem */ - if (ale->type == ANIMTYPE_GPLAYER) + if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->data, ce->cfra, SELECT_ADD); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->data, ce->cfra, SELECT_ADD); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } } } /* free elements */ BLI_freelistN(&ked.list); + + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -1081,12 +1102,16 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); ANIM_nla_mapping_apply_fcurve(adt, ale->key_data, 1, 1); } - else if (ale->type == ANIMTYPE_GPLAYER) + else if (ale->type == ANIMTYPE_GPLAYER) { ED_gplayer_frames_select_border(ale->data, ked.f1, ked.f2, select_mode); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_masklayer_frames_select_border(ale->data, ked.f1, ked.f2, select_mode); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } } /* Sync marker support */ @@ -1111,6 +1136,7 @@ static void actkeys_select_leftright(bAnimContext *ac, short leftright, short se } /* Cleanup */ + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -1227,6 +1253,7 @@ static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short s /* select the nominated keyframe on the given frame */ if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->data, selx, select_mode); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->data, selx, select_mode); @@ -1244,12 +1271,14 @@ static void actkeys_mselect_single(bAnimContext *ac, bAnimListElem *ale, short s for (ale = anim_data.first; ale; ale = ale->next) { if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->data, selx, select_mode); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->data, selx, select_mode); } } + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } else { @@ -1294,16 +1323,22 @@ static void actkeys_mselect_column(bAnimContext *ac, short select_mode, float se ked.f1 = selx; /* select elements with frame number matching cfra */ - if (ale->type == ANIMTYPE_GPLAYER) + if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frame(ale->key_data, selx, select_mode); - else if (ale->type == ANIMTYPE_MASKLAYER) + ale->update |= ANIM_UPDATE_DEPS; + } + else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frame(ale->key_data, selx, select_mode); - else + } + else { ANIM_fcurve_keyframes_loop(&ked, ale->key_data, ok_cb, select_cb, NULL); + } } /* free elements */ BLI_freelistN(&ked.list); + + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } @@ -1318,6 +1353,7 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s /* select all keyframes in this channel */ if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frames(ale->data, select_mode); + ale->update = ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frames(ale->data, select_mode); @@ -1335,12 +1371,14 @@ static void actkeys_mselect_channel_only(bAnimContext *ac, bAnimListElem *ale, s for (ale = anim_data.first; ale; ale = ale->next) { if (ale->type == ANIMTYPE_GPLAYER) { ED_gpencil_select_frames(ale->data, select_mode); + ale->update |= ANIM_UPDATE_DEPS; } else if (ale->type == ANIMTYPE_MASKLAYER) { ED_mask_select_frames(ale->data, select_mode); } } + ANIM_animdata_update(ac, &anim_data); ANIM_animdata_freelist(&anim_data); } else { @@ -1473,6 +1511,7 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ /* remove active channel from list of channels for separate treatment (since it's needed later on) */ BLI_remlink(&anim_data, ale); + ale->next = ale->prev = NULL; /* cleanup temporary lists */ BLI_dlrbTree_free(&anim_keys); @@ -1557,6 +1596,12 @@ static void mouse_action_keys(bAnimContext *ac, const int mval[2], short select_ } } + /* flush tagged updates + * NOTE: We temporarily add this channel back to the list so that this can happen + */ + anim_data.first = anim_data.last = ale; + ANIM_animdata_update(ac, &anim_data); + /* free this channel */ MEM_freeN(ale); } diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index faee9c2b7ac..67632f6a53a 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -237,6 +237,7 @@ static int buttons_context_path_data(ButsContextPath *path, int type) else if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) return 1; else if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) return 1; + else if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) return 1; /* try to get an object in the path, no pinning supported here */ else if (buttons_context_path_object(path)) { ob = path->ptr[path->len - 1].data; @@ -260,7 +261,21 @@ static int buttons_context_path_modifier(ButsContextPath *path) if (buttons_context_path_object(path)) { ob = path->ptr[path->len - 1].data; - if (ob && ELEM(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_LATTICE)) + if (ob && ELEM(ob->type, OB_MESH, OB_CURVE, OB_FONT, OB_SURF, OB_LATTICE, OB_GPENCIL)) + return 1; + } + + return 0; +} + +static int buttons_context_path_shaderfx(ButsContextPath *path) +{ + Object *ob; + + if (buttons_context_path_object(path)) { + ob = path->ptr[path->len - 1].data; + + if (ob && ELEM(ob->type, OB_GPENCIL)) return 1; } @@ -485,6 +500,7 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma WorkSpace *workspace = CTX_wm_workspace(C); ID *id; int found; + Object *ob = CTX_data_active_object(C); memset(path, 0, sizeof(*path)); path->flag = flag; @@ -546,6 +562,9 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma case BCONTEXT_MODIFIER: found = buttons_context_path_modifier(path); break; + case BCONTEXT_SHADERFX: + found = buttons_context_path_shaderfx(path); + break; case BCONTEXT_DATA: found = buttons_context_path_data(path, -1); break; @@ -553,7 +572,14 @@ static int buttons_context_path(const bContext *C, ButsContextPath *path, int ma found = buttons_context_path_particle(path); break; case BCONTEXT_MATERIAL: - found = buttons_context_path_material(path); + /* NOTE: Grease Pencil materials use different panels... */ + if (ob && ob->type == OB_GPENCIL) { + /* XXX: Why path_data? */ + found = buttons_context_path_data(path, -1); + } + else { + found = buttons_context_path_material(path); + } break; case BCONTEXT_TEXTURE: found = buttons_context_path_texture(C, path, sbuts->texuser); @@ -626,10 +652,18 @@ void buttons_context_compute(const bContext *C, SpaceButs *sbuts) if (a == BCONTEXT_DATA) { ptr = &path->ptr[path->len - 1]; - if (ptr->type) + if (ptr->type) { sbuts->dataicon = RNA_struct_ui_icon(ptr->type); - else - sbuts->dataicon = ICON_EMPTY_DATA; + } + else { + Object *ob = CTX_data_active_object(C); + if (ob->type == OB_GPENCIL) { + sbuts->dataicon = ICON_GREASEPENCIL; + } + else { + sbuts->dataicon = ICON_EMPTY_DATA; + } + } } } } diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 66684de18ac..de422565abd 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -55,6 +55,7 @@ #include "BKE_layer.h" #include "BKE_linestyle.h" #include "BKE_modifier.h" +#include "BKE_gpencil_modifier.h" #include "BKE_node.h" #include "BKE_paint.h" #include "BKE_particle.h" @@ -152,6 +153,19 @@ static void buttons_texture_modifier_foreach(void *userData, Object *ob, Modifie N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name); } +static void buttons_texture_modifier_gpencil_foreach(void *userData, Object *ob, GpencilModifierData *md, const char *propname) +{ + PointerRNA ptr; + PropertyRNA *prop; + ListBase *users = userData; + + RNA_pointer_create(&ob->id, &RNA_GpencilModifier, md, &ptr); + prop = RNA_struct_find_property(&ptr, propname); + + buttons_texture_user_property_add(users, &ob->id, ptr, prop, + N_("Grease Pencil Modifiers"), RNA_struct_ui_icon(ptr.type), md->name); +} + static void buttons_texture_users_from_context(ListBase *users, const bContext *C, SpaceButs *sbuts) { Scene *scene = NULL; @@ -203,6 +217,9 @@ static void buttons_texture_users_from_context(ListBase *users, const bContext * /* modifiers */ modifiers_foreachTexLink(ob, buttons_texture_modifier_foreach, users); + /* grease pencil modifiers */ + BKE_gpencil_modifiers_foreachTexLink(ob, buttons_texture_modifier_gpencil_foreach, users); + /* particle systems */ if (psys && !limited_mode) { for (a = 0; a < MAX_MTEX; a++) { diff --git a/source/blender/editors/space_buttons/space_buttons.c b/source/blender/editors/space_buttons/space_buttons.c index 6f7a4ca971a..98ff2f67b58 100644 --- a/source/blender/editors/space_buttons/space_buttons.c +++ b/source/blender/editors/space_buttons/space_buttons.c @@ -180,6 +180,9 @@ static void buttons_main_region_layout_properties(const bContext *C, SpaceButs * case BCONTEXT_MODIFIER: contexts[0] = "modifier"; break; + case BCONTEXT_SHADERFX: + contexts[0] = "shaderfx"; + break; case BCONTEXT_CONSTRAINT: contexts[0] = "constraint"; break; @@ -200,8 +203,9 @@ static void buttons_main_region_layout_tool(const bContext *C, ARegion *ar) const char *contexts[3] = {NULL}; const WorkSpace *workspace = CTX_wm_workspace(C); + const int mode = CTX_data_mode_enum(C); + if (workspace->tools_space_type == SPACE_VIEW3D) { - const int mode = CTX_data_mode_enum(C); switch (mode) { case CTX_MODE_EDIT_MESH: ARRAY_SET_ITEMS(contexts, ".mesh_edit"); @@ -245,12 +249,39 @@ static void buttons_main_region_layout_tool(const bContext *C, ARegion *ar) case CTX_MODE_OBJECT: ARRAY_SET_ITEMS(contexts, ".objectmode"); break; + case CTX_MODE_GPENCIL_PAINT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_paint"); + break; + case CTX_MODE_GPENCIL_SCULPT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_sculpt"); + break; + case CTX_MODE_GPENCIL_WEIGHT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_weight"); + break; } } else if (workspace->tools_space_type == SPACE_IMAGE) { /* TODO */ } + /* for grease pencil we don't use tool system yet, so we need check outside + * workspace->tools_space_type because this value is not available + */ + switch (mode) { + case CTX_MODE_GPENCIL_PAINT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_paint"); + break; + case CTX_MODE_GPENCIL_SCULPT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_sculpt"); + break; + case CTX_MODE_GPENCIL_WEIGHT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_weight"); + break; + case CTX_MODE_GPENCIL_EDIT: + ARRAY_SET_ITEMS(contexts, ".greasepencil_edit"); + break; + } + const bool vertical = true; ED_region_panels_layout_ex(C, ar, contexts, -1, vertical); } @@ -495,6 +526,14 @@ static void buttons_area_listener( break; } break; + case NC_GPENCIL: + switch (wmn->data) { + case ND_DATA: + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) + ED_area_tag_redraw(sa); + break; + } + break; case NC_NODE: if (wmn->action == NA_SELECTED) { ED_area_tag_redraw(sa); diff --git a/source/blender/editors/space_clip/clip_buttons.c b/source/blender/editors/space_clip/clip_buttons.c index db5f6c2451c..6953b7cfb71 100644 --- a/source/blender/editors/space_clip/clip_buttons.c +++ b/source/blender/editors/space_clip/clip_buttons.c @@ -107,7 +107,7 @@ void uiTemplateMovieClip(uiLayout *layout, bContext *C, PointerRNA *ptr, const c uiLayoutSetContextPointer(layout, "edit_movieclip", &clipptr); if (!compact) - uiTemplateID(layout, C, ptr, propname, NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, propname, NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (clip) { uiLayout *col; diff --git a/source/blender/editors/space_clip/space_clip.c b/source/blender/editors/space_clip/space_clip.c index 2b98ff43c5f..725c2b7fa6d 100644 --- a/source/blender/editors/space_clip/space_clip.c +++ b/source/blender/editors/space_clip/space_clip.c @@ -236,7 +236,7 @@ static SpaceLink *clip_new(const ScrArea *sa, const Scene *scene) sc = MEM_callocN(sizeof(SpaceClip), "initclip"); sc->spacetype = SPACE_CLIP; sc->flag = SC_SHOW_MARKER_PATTERN | SC_SHOW_TRACK_PATH | - SC_SHOW_GRAPH_TRACKS_MOTION | SC_SHOW_GRAPH_FRAMES | SC_SHOW_GPENCIL; + SC_SHOW_GRAPH_TRACKS_MOTION | SC_SHOW_GRAPH_FRAMES | SC_SHOW_ANNOTATION; sc->zoom = 1.0f; sc->path_length = 20; sc->scopes.track_preview_height = 120; @@ -1196,7 +1196,7 @@ static void clip_main_region_draw(const bContext *C, ARegion *ar) clip_draw_cache_and_notes(C, sc, ar); - if (sc->flag & SC_SHOW_GPENCIL) { + if (sc->flag & SC_SHOW_ANNOTATION) { /* Grease Pencil */ clip_draw_grease_pencil((bContext *)C, true); } @@ -1204,7 +1204,7 @@ static void clip_main_region_draw(const bContext *C, ARegion *ar) /* reset view matrix */ UI_view2d_view_restore(C); - if (sc->flag & SC_SHOW_GPENCIL) { + if (sc->flag & SC_SHOW_ANNOTATION) { /* draw Grease Pencil - screen space only */ clip_draw_grease_pencil((bContext *)C, false); } diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 812a671ff33..cbf685e6ed8 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -43,6 +43,7 @@ #include "MEM_guardedalloc.h" #include "RNA_access.h" +#include "RNA_define.h" #include "ED_fileselect.h" @@ -71,25 +72,33 @@ static void file_panel_operator_header(const bContext *C, Panel *pa) BLI_strncpy(pa->drawname, RNA_struct_ui_name(op->type->srna), sizeof(pa->drawname)); } -static bool file_panel_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) -{ - const char *prop_id = RNA_property_identifier(prop); - return !(STREQ(prop_id, "filepath") || - STREQ(prop_id, "directory") || - STREQ(prop_id, "filename") - ); -} - static void file_panel_operator(const bContext *C, Panel *pa) { SpaceFile *sfile = CTX_wm_space_file(C); wmOperator *op = sfile->op; - // int empty = 1, flag; UI_block_func_set(uiLayoutGetBlock(pa->layout), file_draw_check_cb, NULL, NULL); - uiTemplateOperatorPropertyButs(C, pa->layout, op, file_panel_check_prop, UI_BUT_LABEL_ALIGN_NONE, - UI_TEMPLATE_OP_PROPS_SHOW_EMPTY); + /* Hack: temporary hide.*/ + const char *hide[] = {"filepath", "files", "directory", "filename"}; + for (int i = 0; i < ARRAY_SIZE(hide); i++) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, hide[i]); + if (prop) { + RNA_def_property_flag(prop, PROP_HIDDEN); + } + } + + uiTemplateOperatorPropertyButs( + C, pa->layout, op, UI_BUT_LABEL_ALIGN_NONE, + UI_TEMPLATE_OP_PROPS_SHOW_EMPTY); + + /* Hack: temporary hide.*/ + for (int i = 0; i < ARRAY_SIZE(hide); i++) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, hide[i]); + if (prop) { + RNA_def_property_clear_flag(prop, PROP_HIDDEN); + } + } UI_block_func_set(uiLayoutGetBlock(pa->layout), NULL, NULL, NULL); } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 1d8c6721b64..afcae7a27ab 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -876,7 +876,7 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char if (!compact) { uiTemplateID( layout, C, ptr, propname, - ima ? NULL : "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + ima ? NULL : "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); } if (ima) { diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 29d63493a3c..be91f7abf99 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1410,7 +1410,7 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( return OPERATOR_RUNNING_MODAL; } -static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop, void *UNUSED(user_data)) { const char *prop_id = RNA_property_identifier(prop); @@ -1429,7 +1429,7 @@ static void image_open_draw(bContext *UNUSED(C), wmOperator *op) /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); - uiDefAutoButsRNA(layout, &ptr, image_open_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); + uiDefAutoButsRNA(layout, &ptr, image_open_draw_check_prop, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* image template */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); @@ -2124,7 +2124,7 @@ static void image_save_as_cancel(bContext *UNUSED(C), wmOperator *op) image_save_as_free(op); } -static bool image_save_as_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop) +static bool image_save_as_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop, void *UNUSED(user_data)) { const char *prop_id = RNA_property_identifier(prop); @@ -2149,7 +2149,7 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); - uiDefAutoButsRNA(layout, &ptr, image_save_as_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); + uiDefAutoButsRNA(layout, &ptr, image_save_as_draw_check_prop, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* multiview template */ if (is_multiview) diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index 3830e6d2792..fdf9d6df374 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -32,6 +32,7 @@ #include "DNA_armature_types.h" #include "DNA_curve_types.h" +#include "DNA_gpencil_types.h" #include "DNA_group_types.h" #include "DNA_lattice_types.h" #include "DNA_mesh_types.h" @@ -55,6 +56,7 @@ #include "BKE_particle.h" #include "BKE_editmesh.h" #include "BKE_object.h" +#include "BKE_gpencil.h" #include "ED_info.h" #include "ED_armature.h" @@ -72,6 +74,7 @@ typedef struct SceneStats { int totobj, totobjsel; int totlamp, totlampsel; int tottri; + int totgplayer, totgpframe, totgpstroke, totgppoint; char infostr[MAX_INFO_LEN]; } SceneStats; @@ -85,6 +88,8 @@ typedef struct SceneStatsFmt { char totobj[MAX_INFO_NUM_LEN], totobjsel[MAX_INFO_NUM_LEN]; char totlamp[MAX_INFO_NUM_LEN], totlampsel[MAX_INFO_NUM_LEN]; char tottri[MAX_INFO_NUM_LEN]; + char totgplayer[MAX_INFO_NUM_LEN], totgpframe[MAX_INFO_NUM_LEN]; + char totgpstroke[MAX_INFO_NUM_LEN], totgppoint[MAX_INFO_NUM_LEN]; } SceneStatsFmt; static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) @@ -127,8 +132,8 @@ static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) { int totv = 0, totf = 0, tottri = 0; - if (ob->curve_cache && ob->curve_cache->disp.first) - BKE_displist_count(&ob->curve_cache->disp, &totv, &totf, &tottri); + if (ob->runtime.curve_cache && ob->runtime.curve_cache->disp.first) + BKE_displist_count(&ob->runtime.curve_cache->disp, &totv, &totf, &tottri); totv *= totob; totf *= totob; @@ -144,6 +149,20 @@ static void stats_object(Object *ob, int sel, int totob, SceneStats *stats) } break; } + case OB_GPENCIL: + { + bGPdata *gpd = (bGPdata *)ob->data; + /* GPXX Review if we can move to other place when object change + * maybe to depsgraph evaluation + */ + BKE_gpencil_stats_update(gpd); + + stats->totgplayer = gpd->totlayer; + stats->totgpframe = gpd->totframe; + stats->totgpstroke = gpd->totstroke; + stats->totgppoint = gpd->totpoint; + break; + } } } @@ -442,6 +461,11 @@ static void stats_string(ViewLayer *view_layer) SCENE_STATS_FMT_INT(tottri); + SCENE_STATS_FMT_INT(totgplayer); + SCENE_STATS_FMT_INT(totgpframe); + SCENE_STATS_FMT_INT(totgpstroke); + SCENE_STATS_FMT_INT(totgppoint); + #undef SCENE_STATS_FMT_INT @@ -501,6 +525,14 @@ static void stats_string(ViewLayer *view_layer) ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Bones:%s/%s %s%s"), stats_fmt.totbonesel, stats_fmt.totbone, memstr, gpumemstr); } + else if ((ob) && (ob->type == OB_GPENCIL)) { + ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, + IFACE_("Layers:%s | Frames:%s | Strokes:%s | Points:%s"), + stats_fmt.totgplayer, stats_fmt.totgpframe, stats_fmt.totgpstroke, stats_fmt.totgppoint); + + ofs += BLI_strncpy_rlen(s + ofs, memstr, MAX_INFO_LEN - ofs); + ofs += BLI_strncpy_rlen(s + ofs, gpumemstr, MAX_INFO_LEN - ofs); + } else if (stats_is_object_dynamic_topology_sculpt(ob, object_mode)) { ofs += BLI_snprintf(s + ofs, MAX_INFO_LEN - ofs, IFACE_("Verts:%s | Tris:%s%s"), stats_fmt.totvert, stats_fmt.tottri, gpumemstr); diff --git a/source/blender/editors/space_nla/nla_buttons.c b/source/blender/editors/space_nla/nla_buttons.c index 57464cbf092..40caf919848 100644 --- a/source/blender/editors/space_nla/nla_buttons.c +++ b/source/blender/editors/space_nla/nla_buttons.c @@ -145,6 +145,7 @@ bool nla_panel_context(const bContext *C, PointerRNA *adt_ptr, PointerRNA *nlt_p case ANIMTYPE_DSLINESTYLE: case ANIMTYPE_DSSPK: case ANIMTYPE_DSGPENCIL: + case ANIMTYPE_PALETTE: { /* for these channels, we only do AnimData */ if (ale->adt && adt_ptr) { @@ -287,7 +288,7 @@ static void nla_panel_animdata(const bContext *C, Panel *pa) row = uiLayoutRow(layout, true); uiTemplateID( row, (bContext *)C, &adt_ptr, "action", - "ACTION_OT_new", NULL, "NLA_OT_action_unlink", UI_TEMPLATE_ID_FILTER_ALL); + "ACTION_OT_new", NULL, "NLA_OT_action_unlink", UI_TEMPLATE_ID_FILTER_ALL, false); /* extrapolation */ row = uiLayoutRow(layout, true); diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index 3368ad4fe8d..51177a77f0d 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -182,6 +182,7 @@ static int mouse_nla_channels(bContext *C, bAnimContext *ac, float x, int channe case ANIMTYPE_DSLINESTYLE: case ANIMTYPE_DSSPK: case ANIMTYPE_DSGPENCIL: + case ANIMTYPE_PALETTE: { /* sanity checking... */ if (ale->adt) { diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index d1ad8cb396c..a48a6faf69f 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -744,7 +744,7 @@ static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA PointerRNA iuserptr = RNA_pointer_get(ptr, "image_user"); uiLayoutSetContextPointer(layout, "image_user", &iuserptr); - uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); uiItemR(layout, ptr, "color_space", 0, "", ICON_NONE); uiItemR(layout, ptr, "interpolation", 0, "", ICON_NONE); uiItemR(layout, ptr, "projection", 0, "", ICON_NONE); @@ -775,7 +775,7 @@ static void node_shader_buts_tex_environment(uiLayout *layout, bContext *C, Poin uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID( layout, C, ptr, "image", - "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); node_buts_image_user(layout, C, &iuserptr, &imaptr, &iuserptr, false); @@ -793,7 +793,7 @@ static void node_shader_buts_tex_environment_ex(uiLayout *layout, bContext *C, P uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID( layout, C, ptr, "image", - "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!ima) return; @@ -1274,7 +1274,7 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * uiLayoutSetContextPointer(layout, "image_user", &iuserptr); uiTemplateID( layout, C, ptr, "image", - "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; imaptr = RNA_pointer_get(ptr, "image"); @@ -1304,7 +1304,7 @@ static void node_composit_buts_viewlayers(uiLayout *layout, bContext *C, Pointer const char *layer_name; char scene_name[MAX_ID_NAME - 2]; - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -1418,7 +1418,7 @@ static void node_composit_buts_defocus(uiLayout *layout, bContext *C, PointerRNA col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "use_preview", 0, NULL, ICON_NONE); - uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "scene", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "use_zbuffer", 0, NULL, ICON_NONE); @@ -1985,7 +1985,7 @@ static void node_composit_buts_ycc(uiLayout *layout, bContext *UNUSED(C), Pointe static void node_composit_buts_movieclip(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); } static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1993,7 +1993,7 @@ static void node_composit_buts_movieclip_ex(uiLayout *layout, bContext *C, Point bNode *node = ptr->data; PointerRNA clipptr; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -2007,7 +2007,7 @@ static void node_composit_buts_stabilize2d(uiLayout *layout, bContext *C, Pointe { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -2031,7 +2031,7 @@ static void node_composit_buts_moviedistortion(uiLayout *layout, bContext *C, Po { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (!node->id) return; @@ -2339,7 +2339,7 @@ static void node_composit_buts_mask(uiLayout *layout, bContext *C, PointerRNA *p { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "mask", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); uiItemR(layout, ptr, "use_antialiasing", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "use_feather", 0, NULL, ICON_NONE); @@ -2361,7 +2361,7 @@ static void node_composit_buts_keyingscreen(uiLayout *layout, bContext *C, Point { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, NULL, NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (node->id) { MovieClip *clip = (MovieClip *) node->id; @@ -2397,7 +2397,7 @@ static void node_composit_buts_trackpos(uiLayout *layout, bContext *C, PointerRN { bNode *node = ptr->data; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (node->id) { MovieClip *clip = (MovieClip *) node->id; @@ -2437,7 +2437,7 @@ static void node_composit_buts_planetrackdeform(uiLayout *layout, bContext *C, P bNode *node = ptr->data; NodePlaneTrackDeformData *data = node->storage; - uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "clip", NULL, "CLIP_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); if (node->id) { MovieClip *clip = (MovieClip *) node->id; @@ -2838,7 +2838,7 @@ static void node_texture_buts_proc(uiLayout *layout, bContext *UNUSED(C), Pointe static void node_texture_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) { - uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL); + uiTemplateID(layout, C, ptr, "image", "IMAGE_OT_new", "IMAGE_OT_open", NULL, UI_TEMPLATE_ID_FILTER_ALL, false); } static void node_texture_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -3422,7 +3422,7 @@ bool node_link_bezier_points(View2D *v2d, SpaceNode *snode, bNodeLink *link, flo static float arrow_verts[3][2] = {{-1.0f, 1.0f}, {0.0f, 0.0f}, {-1.0f, -1.0f}}; static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0.7071f, -0.7071f}}; -struct { +static struct { GPUBatch *batch; /* for batching line together */ GPUBatch *batch_single; /* for single line */ GPUVertBuf *inst_vbo; diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index 4b3a3abc642..bba46771674 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -951,18 +951,18 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN /* open/close entirely? */ { uiBut *but; - int but_size = UI_UNIT_X * 0.6f; + int but_size = UI_UNIT_X * 1.2f; /* XXX button uses a custom triangle draw below, so make it invisible without icon */ UI_block_emboss_set(node->block, UI_EMBOSS_NONE); but = uiDefBut(node->block, UI_BTYPE_BUT_TOGGLE, B_REDR, "", - rct->xmin + 0.5f * U.widget_unit - but_size / 2, rct->ymax - NODE_DY / 2.0f - but_size / 2, + rct->xmin + 0.6f * U.widget_unit - but_size / 2, rct->ymax - NODE_DY / 2.2f - but_size / 2, but_size, but_size, NULL, 0, 0, 0, 0, ""); UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); UI_block_emboss_set(node->block, UI_EMBOSS); UI_GetThemeColor4fv(TH_TEXT, color); /* custom draw function for this button */ - UI_draw_icon_tri(rct->xmin + 0.5f * U.widget_unit, rct->ymax - NODE_DY / 2.0f, 'v', color); + UI_draw_icon_tri(rct->xmin + 0.6f * U.widget_unit, rct->ymax - NODE_DY / 2.2f, 'v', color); } nodeLabel(ntree, node, showname, sizeof(showname)); @@ -1071,7 +1071,7 @@ static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, b /* open entirely icon */ { uiBut *but; - int but_size = UI_UNIT_X * 0.6f; + int but_size = UI_UNIT_X * 1.2f; /* XXX button uses a custom triangle draw below, so make it invisible without icon */ UI_block_emboss_set(node->block, UI_EMBOSS_NONE); but = uiDefBut(node->block, UI_BTYPE_BUT_TOGGLE, B_REDR, "", diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index ed7379acca9..3644c8d09e6 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -238,7 +238,7 @@ extern const char *node_context_dir[]; // nodes draw without dpi - the view zoom is flexible #define HIDDEN_RAD (0.75f * U.widget_unit) -#define BASIS_RAD (0.4f * U.widget_unit) +#define BASIS_RAD (0.2f * U.widget_unit) #define NODE_DYS (U.widget_unit / 2) #define NODE_DY U.widget_unit #define NODE_SOCKDY (0.08f * U.widget_unit) diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index ad94615a0d2..e480cac2dcc 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -574,7 +574,7 @@ static TreeTraversalAction layer_collection_find_data_to_edit(TreeElement *te, v return TRAVERSE_CONTINUE; } -static bool collections_view_layer_poll(bContext *C, bool include) +static bool collections_view_layer_poll(bContext *C, bool clear, int flag) { /* Poll function so the right click menu show current state of selected collections. */ SpaceOops *soops = CTX_wm_space_outliner(C); @@ -593,10 +593,10 @@ static bool collections_view_layer_poll(bContext *C, bool include) GSET_ITER(collections_to_edit_iter, data.collections_to_edit) { LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter); - if (include && (lc->flag & LAYER_COLLECTION_EXCLUDE)) { + if (clear && (lc->flag & flag)) { result = true; } - else if (!include && !(lc->flag & LAYER_COLLECTION_EXCLUDE)) { + else if (!clear && !(lc->flag & flag)) { result = true; } } @@ -605,27 +605,47 @@ static bool collections_view_layer_poll(bContext *C, bool include) return result; } -static bool collections_exclude_poll(bContext *C) +static bool collections_exclude_set_poll(bContext *C) { - return collections_view_layer_poll(C, false); + return collections_view_layer_poll(C, false, LAYER_COLLECTION_EXCLUDE); } -static bool collections_include_poll(bContext *C) +static bool collections_exclude_clear_poll(bContext *C) { - return collections_view_layer_poll(C, true); + return collections_view_layer_poll(C, true, LAYER_COLLECTION_EXCLUDE); } -static void layer_collection_exclude_recursive_set(LayerCollection *lc) +static bool collections_holdout_set_poll(bContext *C) +{ + return collections_view_layer_poll(C, false, LAYER_COLLECTION_HOLDOUT); +} + +static bool collections_holdout_clear_poll(bContext *C) +{ + return collections_view_layer_poll(C, true, LAYER_COLLECTION_HOLDOUT); +} + +static bool collections_indirect_only_set_poll(bContext *C) +{ + return collections_view_layer_poll(C, false, LAYER_COLLECTION_INDIRECT_ONLY); +} + +static bool collections_indirect_only_clear_poll(bContext *C) +{ + return collections_view_layer_poll(C, true, LAYER_COLLECTION_INDIRECT_ONLY); +} + +static void layer_collection_flag_recursive_set(LayerCollection *lc, int flag) { for (LayerCollection *nlc = lc->layer_collections.first; nlc; nlc = nlc->next) { - if (lc->flag & LAYER_COLLECTION_EXCLUDE) { - nlc->flag |= LAYER_COLLECTION_EXCLUDE; + if (lc->flag & flag) { + nlc->flag |= flag; } else { - nlc->flag &= ~LAYER_COLLECTION_EXCLUDE; + nlc->flag &= ~flag; } - layer_collection_exclude_recursive_set(nlc); + layer_collection_flag_recursive_set(nlc, flag); } } @@ -636,7 +656,10 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); SpaceOops *soops = CTX_wm_space_outliner(C); struct CollectionEditData data = {.scene = scene, .soops = soops}; - bool include = STREQ(op->idname, "OUTLINER_OT_collection_include_set"); + bool clear = strstr(op->idname, "clear") != NULL; + int flag = strstr(op->idname, "holdout") ? LAYER_COLLECTION_HOLDOUT : + strstr(op->idname, "indirect_only") ? LAYER_COLLECTION_INDIRECT_ONLY : + LAYER_COLLECTION_EXCLUDE; data.collections_to_edit = BLI_gset_ptr_new(__func__); @@ -647,14 +670,14 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op) LayerCollection *lc = BLI_gsetIterator_getKey(&collections_to_edit_iter); if (!(lc->collection->flag & COLLECTION_IS_MASTER)) { - if (include) { - lc->flag &= ~LAYER_COLLECTION_EXCLUDE; + if (clear) { + lc->flag &= ~flag; } else { - lc->flag |= LAYER_COLLECTION_EXCLUDE; + lc->flag |= flag; } - layer_collection_exclude_recursive_set(lc); + layer_collection_flag_recursive_set(lc, flag); } } @@ -671,28 +694,88 @@ static int collection_view_layer_exec(bContext *C, wmOperator *op) void OUTLINER_OT_collection_exclude_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Exclude from View Layer"; + ot->name = "Set Exclude"; ot->idname = "OUTLINER_OT_collection_exclude_set"; ot->description = "Exclude collection from the active view layer"; /* api callbacks */ ot->exec = collection_view_layer_exec; - ot->poll = collections_exclude_poll; + ot->poll = collections_exclude_set_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -void OUTLINER_OT_collection_include_set(wmOperatorType *ot) +void OUTLINER_OT_collection_exclude_clear(wmOperatorType *ot) { /* identifiers */ - ot->name = "Include in View Layer"; - ot->idname = "OUTLINER_OT_collection_include_set"; + ot->name = "Clear Exclude"; + ot->idname = "OUTLINER_OT_collection_exclude_clear"; ot->description = "Include collection in the active view layer"; /* api callbacks */ ot->exec = collection_view_layer_exec; - ot->poll = collections_include_poll; + ot->poll = collections_exclude_clear_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_holdout_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Holdout"; + ot->idname = "OUTLINER_OT_collection_holdout_set"; + ot->description = "Mask collection in the active view layer"; + + /* api callbacks */ + ot->exec = collection_view_layer_exec; + ot->poll = collections_holdout_set_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_holdout_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Holdout"; + ot->idname = "OUTLINER_OT_collection_holdout_clear"; + ot->description = "Clear masking of collection in the active view layer"; + + /* api callbacks */ + ot->exec = collection_view_layer_exec; + ot->poll = collections_holdout_clear_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_indirect_only_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set Indirect Only"; + ot->idname = "OUTLINER_OT_collection_indirect_only_set"; + ot->description = "Set collection to only contribute indirectly (through shadows and reflections) in the view layer"; + + /* api callbacks */ + ot->exec = collection_view_layer_exec; + ot->poll = collections_indirect_only_set_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +void OUTLINER_OT_collection_indirect_only_clear(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Clear Indirect Only"; + ot->idname = "OUTLINER_OT_collection_indirect_only_clear"; + ot->description = "Clear collection contributing only indirectly in the view layer"; + + /* api callbacks */ + ot->exec = collection_view_layer_exec; + ot->poll = collections_indirect_only_clear_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 87f7e1e4fb8..c52214b9846 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -32,6 +32,7 @@ #include "DNA_anim_types.h" #include "DNA_armature_types.h" #include "DNA_gpencil_types.h" +#include "DNA_gpencil_modifier_types.h" #include "DNA_group_types.h" #include "DNA_lamp_types.h" #include "DNA_lightprobe_types.h" @@ -50,6 +51,7 @@ #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_fcurve.h" +#include "BKE_gpencil.h" #include "BKE_global.h" #include "BKE_idcode.h" #include "BKE_layer.h" @@ -438,12 +440,16 @@ static void namebutton_cb(bContext *C, void *tsep, char *oldname) } case TSE_GP_LAYER: { - bGPdata *gpd = (bGPdata *)tselem->id; // id = GP Datablock + bGPdata *gpd = (bGPdata *)tselem->id; /* id = GP Datablock */ bGPDlayer *gpl = te->directdata; + /* always make layer active */ + BKE_gpencil_layer_setactive(gpd, gpl); + // XXX: name needs translation stuff BLI_uniquename(&gpd->layers, gpl, "GP Layer", '.', offsetof(bGPDlayer, info), sizeof(gpl->info)); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, gpd); break; } @@ -872,39 +878,6 @@ static void tselem_draw_icon_uibut(struct DrawIconArg *arg, int icon) } -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) { - /* pass */ - } - else { - PointerRNA ptr; - const float eps = 0.001f; - const bool is_stroke_visible = (gpl->color[3] > eps); - const bool is_fill_visible = (gpl->fill[3] > eps); - float w = 0.5f * UI_UNIT_X; - float h = 0.85f * UI_UNIT_Y; - - RNA_pointer_create(id, &RNA_GPencilLayer, gpl, &ptr); - - UI_block_align_begin(arg->block); - - UI_block_emboss_set(arg->block, is_stroke_visible ? UI_EMBOSS : UI_EMBOSS_NONE); - uiDefButR(arg->block, UI_BTYPE_COLOR, 1, "", arg->xb, arg->yb, w, h, - &ptr, "color", -1, - 0, 0, 0, 0, NULL); - - UI_block_emboss_set(arg->block, is_fill_visible ? UI_EMBOSS : UI_EMBOSS_NONE); - uiDefButR(arg->block, UI_BTYPE_COLOR, 1, "", arg->xb + w, arg->yb, w, h, - &ptr, "fill_color", -1, - 0, 0, 0, 0, NULL); - - UI_block_emboss_set(arg->block, UI_EMBOSS_NONE); - UI_block_align_end(arg->block); - } -} - static void tselem_draw_icon( uiBlock *block, int xmax, float x, float y, TreeStoreElem *tselem, TreeElement *te, float alpha, const bool is_clickable) @@ -969,157 +942,213 @@ static void tselem_draw_icon( case TSE_MODIFIER: { Object *ob = (Object *)tselem->id; - ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr); - switch ((ModifierType)md->type) { - case eModifierType_Subsurf: - ICON_DRAW(ICON_MOD_SUBSURF); - break; - case eModifierType_Armature: - ICON_DRAW(ICON_MOD_ARMATURE); - break; - case eModifierType_Lattice: - ICON_DRAW(ICON_MOD_LATTICE); - break; - case eModifierType_Curve: - ICON_DRAW(ICON_MOD_CURVE); - break; - case eModifierType_Build: - ICON_DRAW(ICON_MOD_BUILD); - break; - case eModifierType_Mirror: - ICON_DRAW(ICON_MOD_MIRROR); - break; - case eModifierType_Decimate: - ICON_DRAW(ICON_MOD_DECIM); - break; - case eModifierType_Wave: - ICON_DRAW(ICON_MOD_WAVE); - break; - case eModifierType_Hook: - ICON_DRAW(ICON_HOOK); - break; - case eModifierType_Softbody: - ICON_DRAW(ICON_MOD_SOFT); - break; - case eModifierType_Boolean: - ICON_DRAW(ICON_MOD_BOOLEAN); - break; - case eModifierType_ParticleSystem: - ICON_DRAW(ICON_MOD_PARTICLES); - break; - case eModifierType_ParticleInstance: - ICON_DRAW(ICON_MOD_PARTICLES); - break; - case eModifierType_EdgeSplit: - ICON_DRAW(ICON_MOD_EDGESPLIT); - break; - case eModifierType_Array: - ICON_DRAW(ICON_MOD_ARRAY); - break; - case eModifierType_UVProject: - case eModifierType_UVWarp: /* TODO, get own icon */ - ICON_DRAW(ICON_MOD_UVPROJECT); - break; - case eModifierType_Displace: - ICON_DRAW(ICON_MOD_DISPLACE); - break; - case eModifierType_Shrinkwrap: - ICON_DRAW(ICON_MOD_SHRINKWRAP); - break; - case eModifierType_Cast: - ICON_DRAW(ICON_MOD_CAST); - break; - case eModifierType_MeshDeform: - case eModifierType_SurfaceDeform: - ICON_DRAW(ICON_MOD_MESHDEFORM); - break; - case eModifierType_Bevel: - ICON_DRAW(ICON_MOD_BEVEL); - break; - case eModifierType_Smooth: - case eModifierType_LaplacianSmooth: - case eModifierType_CorrectiveSmooth: - ICON_DRAW(ICON_MOD_SMOOTH); - break; - case eModifierType_SimpleDeform: - ICON_DRAW(ICON_MOD_SIMPLEDEFORM); - break; - case eModifierType_Mask: - ICON_DRAW(ICON_MOD_MASK); - break; - case eModifierType_Cloth: - ICON_DRAW(ICON_MOD_CLOTH); - break; - case eModifierType_Explode: - ICON_DRAW(ICON_MOD_EXPLODE); - break; - case eModifierType_Collision: - case eModifierType_Surface: - ICON_DRAW(ICON_MOD_PHYSICS); - break; - case eModifierType_Fluidsim: - ICON_DRAW(ICON_MOD_FLUIDSIM); - break; - case eModifierType_Multires: - ICON_DRAW(ICON_MOD_MULTIRES); - break; - case eModifierType_Smoke: - ICON_DRAW(ICON_MOD_SMOKE); - break; - case eModifierType_Solidify: - ICON_DRAW(ICON_MOD_SOLIDIFY); - break; - case eModifierType_Screw: - ICON_DRAW(ICON_MOD_SCREW); - break; - case eModifierType_Remesh: - ICON_DRAW(ICON_MOD_REMESH); - break; - case eModifierType_WeightVGEdit: - case eModifierType_WeightVGMix: - case eModifierType_WeightVGProximity: - ICON_DRAW(ICON_MOD_VERTEX_WEIGHT); - break; - case eModifierType_DynamicPaint: - ICON_DRAW(ICON_MOD_DYNAMICPAINT); - break; - case eModifierType_Ocean: - ICON_DRAW(ICON_MOD_OCEAN); - break; - case eModifierType_Warp: - ICON_DRAW(ICON_MOD_WARP); - break; - case eModifierType_Skin: - ICON_DRAW(ICON_MOD_SKIN); - break; - case eModifierType_Triangulate: - ICON_DRAW(ICON_MOD_TRIANGULATE); - break; - case eModifierType_MeshCache: - ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ - break; - case eModifierType_MeshSequenceCache: - ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ - break; - case eModifierType_Wireframe: - ICON_DRAW(ICON_MOD_WIREFRAME); - break; - case eModifierType_LaplacianDeform: - ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ - break; - case eModifierType_DataTransfer: - ICON_DRAW(ICON_MOD_DATA_TRANSFER); - break; - case eModifierType_NormalEdit: - case eModifierType_WeightedNormal: - ICON_DRAW(ICON_MOD_NORMALEDIT); - break; - /* Default */ - case eModifierType_None: - case eModifierType_ShapeKey: - case NUM_MODIFIER_TYPES: - ICON_DRAW(ICON_DOT); - break; + if (ob->type != OB_GPENCIL) { + ModifierData *md = BLI_findlink(&ob->modifiers, tselem->nr); + switch ((ModifierType)md->type) { + case eModifierType_Subsurf: + ICON_DRAW(ICON_MOD_SUBSURF); + break; + case eModifierType_Armature: + ICON_DRAW(ICON_MOD_ARMATURE); + break; + case eModifierType_Lattice: + ICON_DRAW(ICON_MOD_LATTICE); + break; + case eModifierType_Curve: + ICON_DRAW(ICON_MOD_CURVE); + break; + case eModifierType_Build: + ICON_DRAW(ICON_MOD_BUILD); + break; + case eModifierType_Mirror: + ICON_DRAW(ICON_MOD_MIRROR); + break; + case eModifierType_Decimate: + ICON_DRAW(ICON_MOD_DECIM); + break; + case eModifierType_Wave: + ICON_DRAW(ICON_MOD_WAVE); + break; + case eModifierType_Hook: + ICON_DRAW(ICON_HOOK); + break; + case eModifierType_Softbody: + ICON_DRAW(ICON_MOD_SOFT); + break; + case eModifierType_Boolean: + ICON_DRAW(ICON_MOD_BOOLEAN); + break; + case eModifierType_ParticleSystem: + ICON_DRAW(ICON_MOD_PARTICLES); + break; + case eModifierType_ParticleInstance: + ICON_DRAW(ICON_MOD_PARTICLES); + break; + case eModifierType_EdgeSplit: + ICON_DRAW(ICON_MOD_EDGESPLIT); + break; + case eModifierType_Array: + ICON_DRAW(ICON_MOD_ARRAY); + break; + case eModifierType_UVProject: + case eModifierType_UVWarp: /* TODO, get own icon */ + ICON_DRAW(ICON_MOD_UVPROJECT); + break; + case eModifierType_Displace: + ICON_DRAW(ICON_MOD_DISPLACE); + break; + case eModifierType_Shrinkwrap: + ICON_DRAW(ICON_MOD_SHRINKWRAP); + break; + case eModifierType_Cast: + ICON_DRAW(ICON_MOD_CAST); + break; + case eModifierType_MeshDeform: + case eModifierType_SurfaceDeform: + ICON_DRAW(ICON_MOD_MESHDEFORM); + break; + case eModifierType_Bevel: + ICON_DRAW(ICON_MOD_BEVEL); + break; + case eModifierType_Smooth: + case eModifierType_LaplacianSmooth: + case eModifierType_CorrectiveSmooth: + ICON_DRAW(ICON_MOD_SMOOTH); + break; + case eModifierType_SimpleDeform: + ICON_DRAW(ICON_MOD_SIMPLEDEFORM); + break; + case eModifierType_Mask: + ICON_DRAW(ICON_MOD_MASK); + break; + case eModifierType_Cloth: + ICON_DRAW(ICON_MOD_CLOTH); + break; + case eModifierType_Explode: + ICON_DRAW(ICON_MOD_EXPLODE); + break; + case eModifierType_Collision: + case eModifierType_Surface: + ICON_DRAW(ICON_MOD_PHYSICS); + break; + case eModifierType_Fluidsim: + ICON_DRAW(ICON_MOD_FLUIDSIM); + break; + case eModifierType_Multires: + ICON_DRAW(ICON_MOD_MULTIRES); + break; + case eModifierType_Smoke: + ICON_DRAW(ICON_MOD_SMOKE); + break; + case eModifierType_Solidify: + ICON_DRAW(ICON_MOD_SOLIDIFY); + break; + case eModifierType_Screw: + ICON_DRAW(ICON_MOD_SCREW); + break; + case eModifierType_Remesh: + ICON_DRAW(ICON_MOD_REMESH); + break; + case eModifierType_WeightVGEdit: + case eModifierType_WeightVGMix: + case eModifierType_WeightVGProximity: + ICON_DRAW(ICON_MOD_VERTEX_WEIGHT); + break; + case eModifierType_DynamicPaint: + ICON_DRAW(ICON_MOD_DYNAMICPAINT); + break; + case eModifierType_Ocean: + ICON_DRAW(ICON_MOD_OCEAN); + break; + case eModifierType_Warp: + ICON_DRAW(ICON_MOD_WARP); + break; + case eModifierType_Skin: + ICON_DRAW(ICON_MOD_SKIN); + break; + case eModifierType_Triangulate: + ICON_DRAW(ICON_MOD_TRIANGULATE); + break; + case eModifierType_MeshCache: + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; + case eModifierType_MeshSequenceCache: + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; + case eModifierType_Wireframe: + ICON_DRAW(ICON_MOD_WIREFRAME); + break; + case eModifierType_LaplacianDeform: + ICON_DRAW(ICON_MOD_MESHDEFORM); /* XXX, needs own icon */ + break; + case eModifierType_DataTransfer: + ICON_DRAW(ICON_MOD_DATA_TRANSFER); + break; + case eModifierType_NormalEdit: + case eModifierType_WeightedNormal: + ICON_DRAW(ICON_MOD_NORMALEDIT); + break; + /* Default */ + case eModifierType_None: + case eModifierType_ShapeKey: + + case NUM_MODIFIER_TYPES: + ICON_DRAW(ICON_DOT); + break; + } + } + else { + /* grease pencil modifiers */ + GpencilModifierData *md = BLI_findlink(&ob->greasepencil_modifiers, tselem->nr); + switch ((GpencilModifierType)md->type) { + case eGpencilModifierType_Noise: + ICON_DRAW(ICON_RNDCURVE); + break; + case eGpencilModifierType_Subdiv: + ICON_DRAW(ICON_MOD_SUBSURF); + break; + case eGpencilModifierType_Thick: + ICON_DRAW(ICON_MAN_ROT); + break; + case eGpencilModifierType_Tint: + ICON_DRAW(ICON_COLOR); + break; + case eGpencilModifierType_Instance: + ICON_DRAW(ICON_MOD_ARRAY); + break; + case eGpencilModifierType_Build: + ICON_DRAW(ICON_MOD_BUILD); + break; + case eGpencilModifierType_Opacity: + ICON_DRAW(ICON_MOD_MASK); + break; + case eGpencilModifierType_Color: + ICON_DRAW(ICON_GROUP_VCOL); + break; + case eGpencilModifierType_Lattice: + ICON_DRAW(ICON_MOD_LATTICE); + break; + case eGpencilModifierType_Mirror: + ICON_DRAW(ICON_MOD_MIRROR); + break; + case eGpencilModifierType_Simplify: + ICON_DRAW(ICON_MOD_DECIM); + break; + case eGpencilModifierType_Smooth: + ICON_DRAW(ICON_MOD_SMOOTH); + break; + case eGpencilModifierType_Hook: + ICON_DRAW(ICON_HOOK); + break; + case eGpencilModifierType_Offset: + ICON_DRAW(ICON_MOD_DISPLACE); + break; + + /* Default */ + default: + ICON_DRAW(ICON_DOT); + break; + } } break; } @@ -1186,11 +1215,18 @@ static void tselem_draw_icon( ICON_DRAW(ICON_GROUP); 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); + { + /* indicate whether layer is active */ + bGPDlayer *gpl = te->directdata; + if (gpl->flag & GP_LAYER_ACTIVE) { + ICON_DRAW(ICON_GREASEPENCIL); + } + else { + ICON_DRAW(ICON_DOT); + } break; -#endif + } default: ICON_DRAW(ICON_DOT); break; @@ -1230,6 +1266,9 @@ static void tselem_draw_icon( ICON_CLICK_DRAW(ICON_OUTLINER_OB_EMPTY); } break; + case OB_GPENCIL: + ICON_CLICK_DRAW(ICON_OUTLINER_OB_GREASEPENCIL); break; + break; } } else { @@ -1305,7 +1344,7 @@ static void tselem_draw_icon( case ID_LS: tselem_draw_icon_uibut(&arg, ICON_LINE_DATA); break; case ID_GD: - tselem_draw_icon_uibut(&arg, ICON_GREASEPENCIL); break; + tselem_draw_icon_uibut(&arg, ICON_OUTLINER_DATA_GREASEPENCIL); break; case ID_LP: { LightProbe * lp = (LightProbe *)tselem->id; @@ -1342,16 +1381,9 @@ static void tselem_draw_icon( static void outliner_draw_iconrow_number( const uiFontStyle *fstyle, int offsx, int ys, - const eOLDrawState active, const int num_elements) { - float color[4] = {0.4f, 0.4f, 0.4f, 0.9f}; - copy_v3_fl(color, 0.2f); - if (active != OL_DRAWSEL_NONE) { - copy_v3_fl(color, 0.65f); - color[3] = 1.0f; - } - + float color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; float ufac = 0.25f * UI_UNIT_X; float offset_x = (float) offsx + UI_UNIT_X * 0.35f; @@ -1415,13 +1447,13 @@ static void outliner_draw_iconrow_doit( } /* No inlined icon should be clickable. */ - tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.5f * alpha_fac, false); + tselem_draw_icon(block, xmax, (float)*offsx, (float)ys, tselem, te, 0.8f * alpha_fac, false); te->xs = *offsx; te->ys = ys; te->xend = (short)*offsx + UI_UNIT_X; if (num_elements > 1) { - outliner_draw_iconrow_number(fstyle, *offsx, ys, active, num_elements); + outliner_draw_iconrow_number(fstyle, *offsx, ys, num_elements); } (*offsx) += UI_UNIT_X; } @@ -1449,23 +1481,19 @@ static int tree_element_id_type_to_index(TreeElement *te) } } +typedef struct MergedIconRow { + eOLDrawState active[INDEX_ID_MAX + OB_TYPE_MAX]; + int num_elements[INDEX_ID_MAX + OB_TYPE_MAX]; + TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX]; +} MergedIconRow; + static void outliner_draw_iconrow( bContext *C, uiBlock *block, const uiFontStyle *fstyle, Scene *scene, ViewLayer *view_layer, SpaceOops *soops, - ListBase *lb, int level, int xmax, int *offsx, int ys, float alpha_fac) + ListBase *lb, int level, int xmax, int *offsx, int ys, float alpha_fac, MergedIconRow *merged) { eOLDrawState active; const Object *obact = OBACT(view_layer); - struct { - eOLDrawState active[INDEX_ID_MAX + OB_TYPE_MAX]; - int num_elements[INDEX_ID_MAX + OB_TYPE_MAX]; - TreeElement *tree_element[INDEX_ID_MAX + OB_TYPE_MAX]; - } data = { - .active = {0}, - .num_elements = {0}, - .tree_element = {NULL}, - }; - for (TreeElement *te = lb->first; te; te = te->next) { /* exit drawing early */ if ((*offsx) - UI_UNIT_X > xmax) @@ -1496,13 +1524,13 @@ static void outliner_draw_iconrow( } else { const int index = tree_element_id_type_to_index(te); - data.num_elements[index]++; - if ((data.tree_element[index] == NULL) || - (active > data.active[index])) + merged->num_elements[index]++; + if ((merged->tree_element[index] == NULL) || + (active > merged->active[index])) { - data.tree_element[index] = te; + merged->tree_element[index] = te; } - data.active[index] = MAX2(active, data.active[index]); + merged->active[index] = MAX2(active, merged->active[index]); } } @@ -1510,26 +1538,28 @@ static void outliner_draw_iconrow( if (tselem->type != TSE_R_LAYER) { outliner_draw_iconrow( C, block, fstyle, scene, view_layer, soops, - &te->subtree, level + 1, xmax, offsx, ys, alpha_fac); + &te->subtree, level + 1, xmax, offsx, ys, alpha_fac, merged); } } - for (int i = 0; i < INDEX_ID_MAX; i++) { - const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1; - /* See tree_element_id_type_to_index for the index logic. */ - int index_base = i; - if (i > INDEX_ID_OB) { - index_base += OB_TYPE_MAX; - } - for (int j = 0; j < num_subtypes; j++) { - const int index = index_base + j; - if (data.num_elements[index] != 0) { - outliner_draw_iconrow_doit(block, - data.tree_element[index], - fstyle, - xmax, offsx, ys, alpha_fac, - data.active[index], - data.num_elements[index]); + if (level == 0) { + for (int i = 0; i < INDEX_ID_MAX; i++) { + const int num_subtypes = (i == INDEX_ID_OB) ? OB_TYPE_MAX : 1; + /* See tree_element_id_type_to_index for the index logic. */ + int index_base = i; + if (i > INDEX_ID_OB) { + index_base += OB_TYPE_MAX; + } + for (int j = 0; j < num_subtypes; j++) { + const int index = index_base + j; + if (merged->num_elements[index] != 0) { + outliner_draw_iconrow_doit(block, + merged->tree_element[index], + fstyle, + xmax, offsx, ys, alpha_fac, + merged->active[index], + merged->num_elements[index]); + } } } } @@ -1756,9 +1786,10 @@ static void outliner_draw_tree_element( immUnbindProgram(); } + MergedIconRow merged = {{0}}; outliner_draw_iconrow( C, block, fstyle, scene, view_layer, soops, &te->subtree, 0, xmax, &tempx, - *starty, alpha_fac); + *starty, alpha_fac, &merged); GPU_blend(false); } diff --git a/source/blender/editors/space_outliner/outliner_intern.h b/source/blender/editors/space_outliner/outliner_intern.h index 093ad9361c2..461d4bd7c56 100644 --- a/source/blender/editors/space_outliner/outliner_intern.h +++ b/source/blender/editors/space_outliner/outliner_intern.h @@ -358,7 +358,11 @@ void OUTLINER_OT_collection_objects_deselect(struct wmOperatorType *ot); void OUTLINER_OT_collection_link(struct wmOperatorType *ot); void OUTLINER_OT_collection_instance(struct wmOperatorType *ot); void OUTLINER_OT_collection_exclude_set(struct wmOperatorType *ot); -void OUTLINER_OT_collection_include_set(struct wmOperatorType *ot); +void OUTLINER_OT_collection_exclude_clear(struct wmOperatorType *ot); +void OUTLINER_OT_collection_holdout_set(struct wmOperatorType *ot); +void OUTLINER_OT_collection_holdout_clear(struct wmOperatorType *ot); +void OUTLINER_OT_collection_indirect_only_set(struct wmOperatorType *ot); +void OUTLINER_OT_collection_indirect_only_clear(struct wmOperatorType *ot); /* outliner_utils.c ---------------------------------------------- */ diff --git a/source/blender/editors/space_outliner/outliner_ops.c b/source/blender/editors/space_outliner/outliner_ops.c index 0dd492839c9..576038979d3 100644 --- a/source/blender/editors/space_outliner/outliner_ops.c +++ b/source/blender/editors/space_outliner/outliner_ops.c @@ -457,7 +457,11 @@ void outliner_operatortypes(void) WM_operatortype_append(OUTLINER_OT_collection_link); WM_operatortype_append(OUTLINER_OT_collection_instance); WM_operatortype_append(OUTLINER_OT_collection_exclude_set); - WM_operatortype_append(OUTLINER_OT_collection_include_set); + WM_operatortype_append(OUTLINER_OT_collection_exclude_clear); + WM_operatortype_append(OUTLINER_OT_collection_holdout_set); + WM_operatortype_append(OUTLINER_OT_collection_holdout_clear); + WM_operatortype_append(OUTLINER_OT_collection_indirect_only_set); + WM_operatortype_append(OUTLINER_OT_collection_indirect_only_clear); } static wmKeyMap *outliner_item_drag_drop_modal_keymap(wmKeyConfig *keyconf) @@ -570,6 +574,9 @@ void outliner_keymap(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "OBJECT_OT_move_to_collection", MKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "OBJECT_OT_link_to_collection", MKEY, KM_PRESS, KM_SHIFT, 0); + WM_keymap_add_item(keymap, "OUTLINER_OT_collection_exclude_set", EKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "OUTLINER_OT_collection_exclude_clear", EKEY, KM_PRESS, KM_ALT, 0); + kmi = WM_keymap_add_item(keymap, "OBJECT_OT_hide_view_clear", HKEY, KM_PRESS, KM_ALT, 0); RNA_boolean_set(kmi->ptr, "select", false); kmi = WM_keymap_add_item(keymap, "OBJECT_OT_hide_view_set", HKEY, KM_PRESS, 0, 0); diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 7ab13f36953..ec5e11520a6 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -39,6 +39,7 @@ #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_world_types.h" +#include "DNA_gpencil_types.h" #include "BLI_utildefines.h" #include "BLI_listbase.h" @@ -46,9 +47,11 @@ #include "BKE_armature.h" #include "BKE_collection.h" #include "BKE_context.h" +#include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_main.h" #include "BKE_object.h" +#include "BKE_paint.h" #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_workspace.h" @@ -60,6 +63,7 @@ #include "ED_screen.h" #include "ED_sequencer.h" #include "ED_undo.h" +#include "ED_gpencil.h" #include "WM_api.h" #include "WM_types.h" @@ -470,6 +474,28 @@ static eOLDrawState tree_element_active_defgroup( return OL_DRAWSEL_NONE; } +static eOLDrawState UNUSED_FUNCTION(tree_element_active_gplayer)( + bContext *C, Scene *UNUSED(scene), TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) +{ + bGPdata *gpd = (bGPdata *)tselem->id; + bGPDlayer *gpl = te->directdata; + + /* We can only have a single "active" layer at a time + * and there must always be an active layer... + */ + if (set != OL_SETSEL_NONE) { + if (gpl) { + BKE_gpencil_layer_setactive(gpd, gpl); + WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_SELECTED, gpd); + } + } + else { + return OL_DRAWSEL_NORMAL; + } + + return OL_DRAWSEL_NONE; +} + static eOLDrawState tree_element_active_posegroup( bContext *C, Scene *UNUSED(scene), ViewLayer *view_layer, TreeElement *te, TreeStoreElem *tselem, const eOLSetState set) { @@ -1006,6 +1032,10 @@ static void do_outliner_item_activate_tree_element( } } } + else if (ELEM(te->idcode, ID_GD)) { + /* set grease pencil to object mode */ + WM_operator_name_call(C, "GPENCIL_OT_editmode_toggle", WM_OP_INVOKE_REGION_WIN, NULL); + } else { // rest of types tree_element_active(C, scene, view_layer, soops, te, OL_SETSEL_NORMAL, false); } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 28890e42139..539df3aa085 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -319,8 +319,6 @@ static void outliner_add_scene_contents(SpaceOops *soops, ListBase *lb, Scene *s if (outliner_animdata_test(sce->adt)) outliner_add_element(soops, lb, sce, te, TSE_ANIM_DATA, 0); - /* Grease Pencil */ - outliner_add_element(soops, lb, sce->gpd, te, 0, 0); } TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *customdata) diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 5d2ceced904..3cd5bfc3e58 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -627,7 +627,7 @@ static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) op->customdata = NULL; } -static bool sequencer_add_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +static bool sequencer_add_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop, void *UNUSED(user_data)) { const char *prop_id = RNA_property_identifier(prop); @@ -693,7 +693,7 @@ static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op) /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); - uiDefAutoButsRNA(layout, &ptr, sequencer_add_draw_check_prop, UI_BUT_LABEL_ALIGN_NONE, false); + uiDefAutoButsRNA(layout, &ptr, sequencer_add_draw_check_prop, NULL, UI_BUT_LABEL_ALIGN_NONE, false); /* image template */ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index cefc6cdd814..199ff06cc8b 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -739,7 +739,7 @@ static Sequence *cut_seq_hard(Scene *scene, Sequence *seq, int cutframe) if (!skip_dup) { /* Duplicate AFTER the first change */ - seqn = BKE_sequence_dupli_recursive(scene, scene, seq, SEQ_DUPE_UNIQUE_NAME | SEQ_DUPE_ANIM); + seqn = BKE_sequence_dupli_recursive(scene, scene, seq, SEQ_DUPE_ANIM); } if (seqn) { @@ -848,7 +848,7 @@ static Sequence *cut_seq_soft(Scene *scene, Sequence *seq, int cutframe) if (!skip_dup) { /* Duplicate AFTER the first change */ - seqn = BKE_sequence_dupli_recursive(scene, scene, seq, SEQ_DUPE_UNIQUE_NAME | SEQ_DUPE_ANIM); + seqn = BKE_sequence_dupli_recursive(scene, scene, seq, SEQ_DUPE_ANIM); } if (seqn) { @@ -2091,6 +2091,7 @@ static int sequencer_cut_exec(bContext *C, wmOperator *op) SEQP_BEGIN (ed, seq) { + BKE_sequence_base_unique_name_recursive(&ed->seqbase, seq); if (seq->seq1 || seq->seq2 || seq->seq3) { BKE_sequence_calc(scene, seq); } diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c index e45159124e8..6113922c02e 100644 --- a/source/blender/editors/space_topbar/space_topbar.c +++ b/source/blender/editors/space_topbar/space_topbar.c @@ -168,6 +168,10 @@ static void topbar_main_region_listener(wmWindow *UNUSED(win), ScrArea *UNUSED(s if (wmn->data == ND_SPACE_VIEW3D) ED_region_tag_redraw(ar); break; + case NC_GPENCIL: + if (wmn->data == ND_DATA) + ED_region_tag_redraw(ar); + break; } } diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index c0abbe636c3..3649c6f6dbb 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -80,6 +80,7 @@ #include "BKE_subsurf.h" #include "BKE_unit.h" #include "BKE_tracking.h" +#include "BKE_gpencil.h" #include "BKE_editmesh.h" diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 71bdd2e20c2..eaa6f03c533 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -36,6 +36,7 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_gpencil_types.h" #include "MEM_guardedalloc.h" @@ -335,12 +336,18 @@ static SpaceLink *view3d_new(const ScrArea *UNUSED(sa), const Scene *scene) v3d->gridflag = V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_FLOOR; v3d->flag = V3D_SELECT_OUTLINE; - v3d->flag2 = V3D_SHOW_RECONSTRUCTION | V3D_SHOW_GPENCIL; + v3d->flag2 = V3D_SHOW_RECONSTRUCTION | V3D_SHOW_ANNOTATION; v3d->lens = 50.0f; v3d->near = 0.01f; v3d->far = 1000.0f; + v3d->overlay.gpencil_grid_scale = 1.0; // Scales + v3d->overlay.gpencil_grid_lines = GP_DEFAULT_GRID_LINES; // NUmber of Lines + v3d->overlay.gpencil_paper_opacity = 0.5f; + v3d->overlay.gpencil_grid_axis = V3D_GP_GRID_AXIS_Y; + v3d->overlay.gpencil_grid_opacity = 0.9f; + v3d->bundle_size = 0.2f; v3d->bundle_drawtype = OB_PLAINAXES; @@ -350,6 +357,10 @@ static SpaceLink *view3d_new(const ScrArea *UNUSED(sa), const Scene *scene) v3d->stereo3d_convergence_alpha = 0.15f; v3d->stereo3d_volume_alpha = 0.05f; + /* grease pencil settings */ + v3d->vertex_opacity = 1.0f; + v3d->gp_flag |= V3D_GP_SHOW_EDIT_LINES; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for view3d"); @@ -1406,7 +1417,7 @@ static void space_view3d_listener( switch (wmn->data) { case ND_WORLD_DRAW: case ND_WORLD: - if (v3d->flag3 & V3D_SHOW_WORLD) + if (v3d->shading.background_type & V3D_SHADING_BACKGROUND_WORLD) ED_area_tag_redraw_regiontype(sa, RGN_TYPE_WINDOW); break; } diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 552d84ebb39..46b53a369e3 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -1412,6 +1412,12 @@ ImBuf *ED_view3d_draw_offscreen_imbuf( ofs = NULL; } + GPUFrameBuffer *old_fb = GPU_framebuffer_active_get(); + + if (old_fb) { + GPU_framebuffer_restore(); + } + const bool own_ofs = (ofs == NULL); DRW_opengl_context_enable(); @@ -1556,6 +1562,10 @@ ImBuf *ED_view3d_draw_offscreen_imbuf( DRW_opengl_context_disable(); + if (old_fb) { + GPU_framebuffer_bind(old_fb); + } + if (ibuf->rect_float && ibuf->rect) IMB_rect_from_float(ibuf); @@ -1593,13 +1603,13 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple( v3d.flag2 = V3D_RENDER_OVERRIDE; if (draw_flags & V3D_OFSDRAW_USE_GPENCIL) { - v3d.flag2 |= V3D_SHOW_GPENCIL; + v3d.flag2 |= V3D_SHOW_ANNOTATION; } if (draw_flags & V3D_OFSDRAW_USE_SOLID_TEX) { v3d.flag2 |= V3D_SOLID_TEX; } - v3d.flag3 |= V3D_SHOW_WORLD; + v3d.shading.background_type = V3D_SHADING_BACKGROUND_WORLD; if (draw_flags & V3D_OFSDRAW_USE_CAMERA_DOF) { if (camera->type == OB_CAMERA) { diff --git a/source/blender/editors/space_view3d/view3d_draw_legacy.c b/source/blender/editors/space_view3d/view3d_draw_legacy.c index 94cd4dfc73d..45e4c4b4676 100644 --- a/source/blender/editors/space_view3d/view3d_draw_legacy.c +++ b/source/blender/editors/space_view3d/view3d_draw_legacy.c @@ -881,7 +881,7 @@ void ED_view3d_draw_depth_gpencil( GPU_depth_test(true); - if (v3d->flag2 & V3D_SHOW_GPENCIL) { + if (v3d->flag2 & V3D_SHOW_ANNOTATION) { ED_gpencil_draw_view3d(NULL, scene, view_layer, depsgraph, v3d, ar, true); } diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 2cc1236e5c6..468b33ea9a6 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -52,6 +52,7 @@ #include "BKE_camera.h" #include "BKE_context.h" #include "BKE_font.h" +#include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" @@ -77,7 +78,6 @@ #include "ED_screen.h" #include "ED_transform.h" #include "ED_mesh.h" -#include "ED_gpencil.h" #include "ED_view3d.h" #include "ED_transform_snap_object_context.h" @@ -2811,7 +2811,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) Depsgraph *depsgraph = CTX_data_depsgraph(C); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); bGPdata *gpd = CTX_data_gpencil_data(C); - const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); const bool is_face_map = ((is_gp_edit == false) && ar->gizmo_map && WM_gizmomap_is_any_selected(ar->gizmo_map)); Object *ob_eval = OBACT(view_layer_eval); @@ -2850,9 +2850,7 @@ static int viewselected_exec(bContext *C, wmOperator *op) { /* we're only interested in selected points here... */ if ((gps->flag & GP_STROKE_SELECT) && (gps->flag & GP_STROKE_3DSPACE)) { - if (ED_gpencil_stroke_minmax(gps, true, min, max)) { - ok = true; - } + ok |= BKE_gpencil_stroke_minmax(gps, true, min, max); } } CTX_DATA_END; @@ -4625,7 +4623,7 @@ void VIEW3D_OT_clip_border(wmOperatorType *ot) /* cursor position in vec, result in vec, mval in region coords */ /* note: cannot use event->mval here (called by object_add() */ -void ED_view3d_cursor3d_position(bContext *C, const int mval[2], bool use_depth, float cursor_co[3]) +void ED_view3d_cursor3d_position(bContext *C, const int mval[2], const bool use_depth, float cursor_co[3]) { ARegion *ar = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); diff --git a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c index 388d9a29eff..5778f85a99c 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_navigate.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_navigate.c @@ -131,7 +131,7 @@ struct NavigateGizmoInfo { #define SHAPE_VARS(shape_id) shape = shape_id, .shape_size = ARRAY_SIZE(shape_id) -struct NavigateGizmoInfo g_navigate_params[MPR_TOTAL] = { +static struct NavigateGizmoInfo g_navigate_params[MPR_TOTAL] = { { .opname = "VIEW3D_OT_move", .gizmo = "GIZMO_GT_button_2d", diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index c716692eb9b..fb335d5f922 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -36,13 +36,16 @@ #include "BKE_object.h" #include "BKE_unit.h" +#include "BKE_material.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_gpencil_types.h" #include "DNA_view3d_types.h" #include "BIF_gl.h" +#include "ED_gpencil.h" #include "ED_screen.h" #include "ED_transform_snap_object_context.h" #include "ED_view3d.h" @@ -385,37 +388,28 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) // RulerInfo *ruler_info = gzgroup->customdata; Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + + bGPdata *gpd; bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; - bGPDpalette *palette; - bGPDpalettecolor *palcolor; RulerItem *ruler_item; const char *ruler_name = RULER_ID; bool changed = false; if (scene->gpd == NULL) { - scene->gpd = BKE_gpencil_data_addnew(bmain, "GPencil"); + scene->gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); } + gpd = scene->gpd; - gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); + gpl = BLI_findstring(&gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false); + copy_v4_v4(gpl->color, U.gpencil_new_layer_col); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE; } - /* try to get active palette or create a new one */ - palette = BKE_gpencil_palette_getactive(scene->gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true); - } - /* try to get color with the ruler name or create a new one */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name); - if (palcolor == NULL) { - palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true); - } - gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); BKE_gpencil_free_strokes(gpf); @@ -428,6 +422,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) if (ruler_item->flag & RULERITEM_USE_ANGLE) { gps->totpoints = 3; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -438,6 +433,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) else { gps->totpoints = 2; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -447,9 +443,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, wmGizmoGroup *gzgroup) } gps->flag = GP_STROKE_3DSPACE; gps->thickness = 3; - /* assign color to stroke */ - BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); - gps->palcolor = palcolor; + BLI_addtail(&gpf->strokes, gps); changed = true; } diff --git a/source/blender/editors/space_view3d/view3d_iterators.c b/source/blender/editors/space_view3d/view3d_iterators.c index eae0cf8e459..74071e77901 100644 --- a/source/blender/editors/space_view3d/view3d_iterators.c +++ b/source/blender/editors/space_view3d/view3d_iterators.c @@ -380,7 +380,7 @@ void lattice_foreachScreenVert( Object *obedit = vc->obedit; Lattice *lt = obedit->data; BPoint *bp = lt->editlatt->latt->def; - DispList *dl = obedit->curve_cache ? BKE_displist_find(&obedit->curve_cache->disp, DL_VERTS) : NULL; + DispList *dl = obedit->runtime.curve_cache ? BKE_displist_find(&obedit->runtime.curve_cache->disp, DL_VERTS) : NULL; const float *co = dl ? dl->verts : NULL; int i, N = lt->editlatt->latt->pntsu * lt->editlatt->latt->pntsv * lt->editlatt->latt->pntsw; diff --git a/source/blender/editors/space_view3d/view3d_ruler.c b/source/blender/editors/space_view3d/view3d_ruler.c index 690fc5e3bdb..0475159712b 100644 --- a/source/blender/editors/space_view3d/view3d_ruler.c +++ b/source/blender/editors/space_view3d/view3d_ruler.c @@ -26,9 +26,11 @@ /* defines VIEW3D_OT_ruler modal operator */ +#include "DNA_meshdata_types.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" #include "DNA_gpencil_types.h" +#include "DNA_brush_types.h" #include "MEM_guardedalloc.h" @@ -40,6 +42,7 @@ #include "BKE_context.h" #include "BKE_gpencil.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_unit.h" #include "BIF_gl.h" @@ -51,6 +54,7 @@ #include "WM_api.h" #include "WM_types.h" +#include "ED_gpencil.h" #include "ED_screen.h" #include "ED_view3d.h" #include "ED_transform_snap_object_context.h" @@ -300,37 +304,28 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); + bGPDlayer *gpl; bGPDframe *gpf; bGPDstroke *gps; - bGPDpalette *palette; - bGPDpalettecolor *palcolor; RulerItem *ruler_item; const char *ruler_name = RULER_ID; bool changed = false; + /* FIXME: This needs to be reviewed. Should it keep being done like this? */ if (scene->gpd == NULL) { - scene->gpd = BKE_gpencil_data_addnew(bmain, "GPencil"); + scene->gpd = BKE_gpencil_data_addnew(bmain, "Annotations"); } + bGPdata *gpd = scene->gpd; - gpl = BLI_findstring(&scene->gpd->layers, ruler_name, offsetof(bGPDlayer, info)); + gpl = BLI_findstring(&gpd->layers, ruler_name, offsetof(bGPDlayer, info)); if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(scene->gpd, ruler_name, false); + gpl = BKE_gpencil_layer_addnew(gpd, ruler_name, false); + copy_v4_v4(gpl->color, U.gpencil_new_layer_col); gpl->thickness = 1; gpl->flag |= GP_LAYER_HIDE; } - /* try to get active palette or create a new one */ - palette = BKE_gpencil_palette_getactive(scene->gpd); - if (palette == NULL) { - palette = BKE_gpencil_palette_addnew(scene->gpd, DATA_("GP_Palette"), true); - } - /* try to get color with the ruler name or create a new one */ - palcolor = BKE_gpencil_palettecolor_getbyname(palette, (char *)ruler_name); - if (palcolor == NULL) { - palcolor = BKE_gpencil_palettecolor_addnew(palette, (char *)ruler_name, true); - } - gpf = BKE_gpencil_layer_getframe(gpl, CFRA, true); BKE_gpencil_free_strokes(gpf); @@ -343,6 +338,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) if (ruler_item->flag & RULERITEM_USE_ANGLE) { gps->totpoints = 3; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j++) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -353,6 +349,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) else { gps->totpoints = 2; pt = gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); + gps->dvert = MEM_callocN(sizeof(MDeformVert) * gps->totpoints, "gp_stroke_weights"); for (j = 0; j < 3; j += 2) { copy_v3_v3(&pt->x, ruler_item->co[j]); pt->pressure = 1.0f; @@ -362,9 +359,7 @@ static bool view3d_ruler_to_gpencil(bContext *C, RulerInfo *ruler_info) } gps->flag = GP_STROKE_3DSPACE; gps->thickness = 3; - /* assign color to stroke */ - BLI_strncpy(gps->colorname, palcolor->info, sizeof(gps->colorname)); - gps->palcolor = palcolor; + BLI_addtail(&gpf->strokes, gps); changed = true; } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 7da69c5b2d5..2f65ad1fde4 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -44,6 +44,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_tracking_types.h" +#include "DNA_gpencil_types.h" #include "MEM_guardedalloc.h" @@ -76,6 +77,7 @@ #include "BKE_editmesh.h" #include "BKE_scene.h" #include "BKE_tracking.h" +#include "BKE_workspace.h" #include "DEG_depsgraph.h" @@ -95,6 +97,7 @@ #include "ED_screen.h" #include "ED_sculpt.h" #include "ED_mball.h" +#include "ED_gpencil.h" #include "UI_interface.h" @@ -1675,6 +1678,28 @@ static bool ed_object_select_pick( if ((oldbasact != basact) && (is_obedit == false)) { ED_object_base_activate(C, basact); /* adds notifier */ } + + /* Set special modes for grease pencil + The grease pencil modes are not real modes, but a hack to make the interface + consistent, so need some tricks to keep UI synchronized */ + // XXX: This stuff neeeds reviewing (Aligorith) + if (false && + (((oldbasact) && oldbasact->object->type == OB_GPENCIL) || + (basact->object->type == OB_GPENCIL))) + { + /* set cursor */ + if (ELEM(basact->object->mode, + OB_MODE_GPENCIL_PAINT, + OB_MODE_GPENCIL_SCULPT, + OB_MODE_GPENCIL_WEIGHT)) + { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + else { + /* TODO: maybe is better use restore */ + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + } } DEG_id_tag_update(&scene->id, DEG_TAG_SELECT_UPDATE); diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 7799854db49..6cb0db94099 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -72,6 +72,7 @@ static bool snap_calc_active_center(bContext *C, const bool select_only, float r /* *********************** operators ******************** */ +/** Snaps every individual object center to its nearest point on the grid. **/ static int snap_sel_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) { Depsgraph *depsgraph = CTX_data_depsgraph(C); @@ -212,7 +213,7 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) { /* identifiers */ ot->name = "Snap Selection to Grid"; - ot->description = "Snap selected item(s) to nearest grid division"; + ot->description = "Snap selected item(s) to their nearest grid division"; ot->idname = "VIEW3D_OT_snap_selected_to_grid"; /* api callbacks */ @@ -225,6 +226,12 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) /* *************************************************** */ +/** Snaps the selection as a whole (use_offset=true) or each selected object to the given location. + * + * \param snap_target_global: a location in global space to snap to (eg. 3D cursor or active object). + * \param use_offset: if the selected objects should maintain their relative offsets and be snapped by the selection + * pivot point (median, active), or if every object origin should be snapped to the given location. +**/ static int snap_selected_to_location(bContext *C, const float snap_target_global[3], const bool use_offset) { Depsgraph *depsgraph = CTX_data_depsgraph(C); @@ -434,7 +441,7 @@ void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) { /* identifiers */ ot->name = "Snap Selection to Cursor"; - ot->description = "Snap selected item(s) to cursor"; + ot->description = "Snap selected item(s) to the 3D cursor"; ot->idname = "VIEW3D_OT_snap_selected_to_cursor"; /* api callbacks */ @@ -445,9 +452,13 @@ void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* rna */ - RNA_def_boolean(ot->srna, "use_offset", 1, "Offset", ""); + RNA_def_boolean(ot->srna, "use_offset", 1, "Offset", + "If the selection should be snapped as a whole or by each object center"); } +/* *************************************************** */ + +/** Snaps each selected object to the location of the active selected object. **/ static int snap_selected_to_active_exec(bContext *C, wmOperator *op) { float snap_target_global[3]; @@ -478,6 +489,7 @@ void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot) /* *************************************************** */ +/** Snaps the 3D cursor location to its nearest point on the grid. **/ static int snap_curs_to_grid_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -502,7 +514,7 @@ void VIEW3D_OT_snap_cursor_to_grid(wmOperatorType *ot) { /* identifiers */ ot->name = "Snap Cursor to Grid"; - ot->description = "Snap cursor to nearest grid division"; + ot->description = "Snap 3D cursor to the nearest grid division"; ot->idname = "VIEW3D_OT_snap_cursor_to_grid"; /* api callbacks */ @@ -515,7 +527,8 @@ void VIEW3D_OT_snap_cursor_to_grid(wmOperatorType *ot) /* **************************************************** */ -static void bundle_midpoint(Depsgraph *depsgraph, Scene *scene, Object *ob, float vec[3]) +/** Returns the center position of a tracking marker visible on the viewport (useful to snap to). **/ +static void bundle_midpoint(Depsgraph *depsgraph, Scene *scene, Object *ob, float r_vec[3]) { MovieClip *clip = BKE_object_movieclip_get(scene, ob, false); MovieTracking *tracking; @@ -563,10 +576,11 @@ static void bundle_midpoint(Depsgraph *depsgraph, Scene *scene, Object *ob, floa } if (ok) { - mid_v3_v3v3(vec, min, max); + mid_v3_v3v3(r_vec, min, max); } } +/** Snaps the 3D cursor location to the median point of the selection. **/ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) { Depsgraph *depsgraph = CTX_data_depsgraph(C); @@ -690,7 +704,7 @@ void VIEW3D_OT_snap_cursor_to_selected(wmOperatorType *ot) { /* identifiers */ ot->name = "Snap Cursor to Selected"; - ot->description = "Snap cursor to center of selected item(s)"; + ot->description = "Snap 3D cursor to the middle of the selected item(s)"; ot->idname = "VIEW3D_OT_snap_cursor_to_selected"; /* api callbacks */ @@ -703,9 +717,11 @@ void VIEW3D_OT_snap_cursor_to_selected(wmOperatorType *ot) /* ********************************************** */ -/* this could be exported to be a generic function - * see: calculateCenterActive */ - +/** Calculates the center position of the active object in global space. + * + * Note: this could be exported to be a generic function. + * see: calculateCenterActive +**/ static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3]) { const Depsgraph *depsgraph = CTX_data_depsgraph(C); @@ -770,7 +786,7 @@ void VIEW3D_OT_snap_cursor_to_active(wmOperatorType *ot) { /* identifiers */ ot->name = "Snap Cursor to Active"; - ot->description = "Snap cursor to active item"; + ot->description = "Snap 3D cursor to the active item"; ot->idname = "VIEW3D_OT_snap_cursor_to_active"; /* api callbacks */ @@ -782,7 +798,8 @@ void VIEW3D_OT_snap_cursor_to_active(wmOperatorType *ot) } /* **************************************************** */ -/*New Code - Snap Cursor to Center -*/ + +/** Snaps the 3D cursor location to the origin. **/ static int snap_curs_to_center_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); @@ -802,7 +819,7 @@ void VIEW3D_OT_snap_cursor_to_center(wmOperatorType *ot) { /* identifiers */ ot->name = "Snap Cursor to Center"; - ot->description = "Snap cursor to world origin"; + ot->description = "Snap 3D cursor to the world origin"; ot->idname = "VIEW3D_OT_snap_cursor_to_center"; /* api callbacks */ @@ -815,23 +832,22 @@ void VIEW3D_OT_snap_cursor_to_center(wmOperatorType *ot) /* **************************************************** */ - -bool ED_view3d_minmax_verts(Object *obedit, float min[3], float max[3]) +/** Calculates the bounding box corners (min and max) for \a obedit. The returned values are in global space. **/ +bool ED_view3d_minmax_verts(Object *obedit, float r_min[3], float r_max[3]) { TransVertStore tvs = {NULL}; TransVert *tv; float centroid[3], vec[3], bmat[3][3]; - int a; - /* metaballs are an exception */ + /* Metaballs are an exception. */ if (obedit->type == OB_MBALL) { float ob_min[3], ob_max[3]; bool changed; changed = BKE_mball_minmax_ex(obedit->data, ob_min, ob_max, obedit->obmat, SELECT); if (changed) { - minmax_v3v3_v3(min, max, ob_min); - minmax_v3v3_v3(min, max, ob_max); + minmax_v3v3_v3(r_min, r_max, ob_min); + minmax_v3v3_v3(r_min, r_max, ob_max); } return changed; } @@ -845,12 +861,12 @@ bool ED_view3d_minmax_verts(Object *obedit, float min[3], float max[3]) copy_m3_m4(bmat, obedit->obmat); tv = tvs.transverts; - for (a = 0; a < tvs.transverts_tot; a++, tv++) { + for (int a = 0; a < tvs.transverts_tot; a++, tv++) { copy_v3_v3(vec, (tv->flag & TX_VERT_USE_MAPLOC) ? tv->maploc : tv->loc); mul_m3_v3(bmat, vec); add_v3_v3(vec, obedit->obmat[3]); add_v3_v3(centroid, vec); - minmax_v3v3_v3(min, max, vec); + minmax_v3v3_v3(r_min, r_max, vec); } ED_transverts_free(&tvs); diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 28c59ea387f..464d9402f72 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -45,6 +45,7 @@ #include "DNA_movieclip_types.h" #include "DNA_scene_types.h" /* PET modes */ #include "DNA_workspace_types.h" +#include "DNA_gpencil_types.h" #include "BLI_alloca.h" #include "BLI_utildefines.h" @@ -87,6 +88,7 @@ #include "ED_mesh.h" #include "ED_clip.h" #include "ED_node.h" +#include "ED_gpencil.h" #include "WM_types.h" #include "WM_api.h" @@ -578,6 +580,10 @@ void removeAspectRatio(TransInfo *t, float vec[2]) static void viewRedrawForce(const bContext *C, TransInfo *t) { if (t->options & CTX_GPENCIL_STROKES) { + bGPdata *gpd = ED_gpencil_data_get_active(C); + if (gpd) { + DEG_id_tag_update(&gpd->id, OB_RECALC_DATA); + } WM_event_add_notifier(C, NC_GPENCIL | NA_EDITED, NULL); } else if (t->spacetype == SPACE_VIEW3D) { @@ -1819,7 +1825,23 @@ static void drawHelpline(bContext *UNUSED(C), int x, int y, void *customdata) (float)t->mval[1], }; + +#if 0 /* XXX: Fix from 1c9690e7607bc990cc4a3e6ba839949bb83a78af cannot be used anymore */ + if ((t->flag & T_POINTS) && (t->options & CTX_GPENCIL_STROKES)) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob = tc->obedit; + float vecrot[3]; + copy_v3_v3(vecrot, t->center); + mul_m4_v3(ob->obmat, vecrot); + projectFloatViewEx(t, vecrot, cent, V3D_PROJ_TEST_CLIP_ZERO); + } + } + else { + projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); + } +#else projectFloatViewEx(t, t->center_global, cent, V3D_PROJ_TEST_CLIP_ZERO); +#endif /* Offset the values for the area region. */ const float offset[2] = { @@ -2172,7 +2194,11 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) } } - RNA_property_boolean_set_array(op->ptr, prop, constraint_axis); + /* Only set if needed, so we can hide in the UI when nothing is set. + * See 'transform_poll_property'. */ + if (ELEM(true, UNPACK3(constraint_axis))) { + RNA_property_boolean_set_array(op->ptr, prop, constraint_axis); + } } { @@ -3608,7 +3634,25 @@ static void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, f else sub_v3_v3(vec, td->center); - mul_v3_fl(vec, td->factor); + /* grease pencil falloff */ + if (t->options & CTX_GPENCIL_STROKES) { + bGPDstroke *gps = (bGPDstroke *)td->extra; + mul_v3_fl(vec, td->factor * gps->runtime.multi_frame_falloff); + + /* scale stroke thickness */ + if (td->val) { + snapGridIncrement(t, t->values); + applyNumInput(&t->num, t->values); + + float ratio = t->values[0]; + *td->val = td->ival * ratio * gps->runtime.multi_frame_falloff; + CLAMP_MIN(*td->val, 0.001f); + } + + } + else { + mul_v3_fl(vec, td->factor); + } if (t->flag & (T_OBJECT | T_POSE)) { mul_m3_v3(td->smtx, vec); @@ -3984,6 +4028,20 @@ static void ElementRotation_ex(TransInfo *t, TransDataContainer *tc, TransData * mul_m3_m3m3(totmat, mat, td->mtx); mul_m3_m3m3(smat, td->smtx, totmat); + /* apply gpencil falloff */ + if (t->options & CTX_GPENCIL_STROKES) { + bGPDstroke *gps = (bGPDstroke *)td->extra; + float sx = smat[0][0]; + float sy = smat[1][1]; + float sz = smat[2][2]; + + mul_m3_fl(smat, gps->runtime.multi_frame_falloff); + /* fix scale */ + smat[0][0] = sx; + smat[1][1] = sy; + smat[2][2] = sz; + } + sub_v3_v3v3(vec, td->iloc, center); mul_m3_v3(smat, vec); @@ -4764,7 +4822,21 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) } mul_m3_v3(td->smtx, tvec); - mul_v3_fl(tvec, td->factor); + + if (t->options & CTX_GPENCIL_STROKES) { + /* grease pencil multiframe falloff */ + bGPDstroke *gps = (bGPDstroke *)td->extra; + if (gps != NULL) { + mul_v3_fl(tvec, td->factor * gps->runtime.multi_frame_falloff); + } + else { + mul_v3_fl(tvec, td->factor); + } + } + else { + /* proportional editing falloff */ + mul_v3_fl(tvec, td->factor); + } protectedTransBits(td->protectflag, tvec); diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index c8f74cbcd4f..b24ef4e0ba2 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -560,7 +560,9 @@ static void applyObjectConstraintRot( /* on setup call, use first object */ if (td == NULL) { - td = TRANS_DATA_CONTAINER_FIRST_OK(t)->data; + BLI_assert(tc == NULL); + tc = TRANS_DATA_CONTAINER_FIRST_OK(t); + td = tc->data; } if (t->flag & T_EDIT) { diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index a5706f4a003..d3b7417c4dd 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -90,6 +90,7 @@ #include "BKE_editmesh.h" #include "BKE_tracking.h" #include "BKE_mask.h" +#include "BKE_colortools.h" #include "BIK_api.h" @@ -747,7 +748,7 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb) /* sets transform flags in the bones * returns total number of bones with BONE_TRANSFORM */ -int count_set_pose_transflags(Object *ob, const int mode, short around, bool has_translate_rotate[2]) +int count_set_pose_transflags(Object *ob, const int mode, const short around, bool has_translate_rotate[2]) { bArmature *arm = ob->data; bPoseChannel *pchan; @@ -3609,6 +3610,8 @@ static void posttrans_gpd_clean(bGPdata *gpd) } #endif } + /* set cache flag to dirty */ + DEG_id_tag_update(&gpd->id, OB_RECALC_OB | OB_RECALC_DATA); } static void posttrans_mask_clean(Mask *mask) @@ -8086,7 +8089,14 @@ void flushTransPaintCurve(TransInfo *t) static void createTransGPencil(bContext *C, TransInfo *t) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); \ bGPdata *gpd = ED_gpencil_data_get_active(C); + ToolSettings *ts = CTX_data_tool_settings(C); + + bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); + bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_BRUSHEDIT_FLAG_FRAME_FALLOFF) != 0; + + Object *obact = CTX_data_active_object(C); bGPDlayer *gpl; TransData *td = NULL; float mtx[3][3], smtx[3][3]; @@ -8110,50 +8120,67 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (gpd == NULL) return; + /* initialize falloff curve */ + if (is_multiedit) { + curvemapping_initialize(ts->gp_sculpt.cur_falloff); + } + /* First Pass: Count the number of datapoints required for the strokes, * (and additional info about the configuration - e.g. 2D/3D?) */ for (gpl = gpd->layers.first; gpl; gpl = gpl->next) { /* only editable and visible layers are considered */ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { - bGPDframe *gpf = gpl->actframe; + bGPDframe *gpf; bGPDstroke *gps; + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; + } - 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; - } + for (gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + 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(obact, gpl, gps) == false) { + continue; + } - if (is_prop_edit) { - /* Proportional Editing... */ - if (is_prop_edit_connected) { - /* connected only - so only if selected */ - if (gps->flag & GP_STROKE_SELECT) - tc->data_len += gps->totpoints; - } - else { - /* everything goes - connection status doesn't matter */ - tc->data_len += gps->totpoints; - } - } - else { - /* only selected stroke points are considered */ - if (gps->flag & GP_STROKE_SELECT) { - bGPDspoint *pt; - int i; - - // TODO: 2D vs 3D? - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - if (pt->flag & GP_SPOINT_SELECT) - tc->data_len++; + if (is_prop_edit) { + /* Proportional Editing... */ + if (is_prop_edit_connected) { + /* connected only - so only if selected */ + if (gps->flag & GP_STROKE_SELECT) + tc->data_len += gps->totpoints; + } + else { + /* everything goes - connection status doesn't matter */ + tc->data_len += gps->totpoints; + } + } + else { + /* only selected stroke points are considered */ + if (gps->flag & GP_STROKE_SELECT) { + bGPDspoint *pt; + int i; + + // TODO: 2D vs 3D? + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + if (pt->flag & GP_SPOINT_SELECT) + tc->data_len++; + } + } } } } + /* if not multiedit out of loop */ + if (!is_multiedit) { + break; + } } } } @@ -8180,153 +8207,166 @@ static void createTransGPencil(bContext *C, TransInfo *t) 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); + bGPDframe *init_gpf = gpl->actframe; + if (is_multiedit) { + init_gpf = gpl->frames.first; } + /* init multiframe falloff options */ + int f_init = 0; + int f_end = 0; + + if (use_multiframe_falloff) { + BKE_gpencil_get_range_selected(gpl, &f_init, &f_end); + } + + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, obact, gpd, 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 * - 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... */ - if (gpf->framenum != cfra) { + // XXX: should this be allowed when framelock is enabled? + if ((gpf->framenum != cfra) && (!is_multiedit)) { gpf = BKE_gpencil_frame_addcopy(gpl, cfra); /* in some weird situations (framelock enabled) return NULL */ if (gpf == NULL) { continue; } + if (!is_multiedit) { + init_gpf = gpf; + } } /* Loop over strokes, adding TransData for points as needed... */ - for (gps = gpf->strokes.first; gps; gps = gps->next) { - TransData *head = td; - TransData *tail = td; - bool stroke_ok; - - /* 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; - } - /* What we need to include depends on proportional editing settings... */ - if (is_prop_edit) { - if (is_prop_edit_connected) { - /* A) "Connected" - Only those in selected strokes */ - stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; - } - else { - /* B) All points, always */ - stroke_ok = true; - } - } - else { - /* C) Only selected points in selected strokes */ - stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; - } - - /* Do stroke... */ - if (stroke_ok && gps->totpoints) { - bGPDspoint *pt; - int i; - -#if 0 /* XXX: this isn't needed anymore; cannot calculate center this way or is_prop_edit breaks */ - const float ninv = 1.0f / gps->totpoints; - float center[3] = {0.0f}; - - /* compute midpoint of stroke */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - madd_v3_v3v3fl(center, center, &pt->x, ninv); + for (gpf = init_gpf; gpf; gpf = gpf->next) { + if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) { + + /* if multiframe and falloff, recalculate and save value */ + float falloff = 1.0f; /* by default no falloff */ + if ((is_multiedit) && (use_multiframe_falloff)) { + /* Faloff depends on distance to active frame (relative to the overall frame range) */ + falloff = BKE_gpencil_multiframe_falloff_calc(gpf, gpl->actframe->framenum, + f_init, f_end, ts->gp_sculpt.cur_falloff); } -#endif - /* add all necessary points... */ - for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { - bool point_ok; + for (gps = gpf->strokes.first; gps; gps = gps->next) { + TransData *head = td; + TransData *tail = td; + bool stroke_ok; - /* include point? */ + /* 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(obact, gpl, gps) == false) { + continue; + } + /* What we need to include depends on proportional editing settings... */ if (is_prop_edit) { - /* Always all points in strokes that get included */ - point_ok = true; + if (is_prop_edit_connected) { + /* A) "Connected" - Only those in selected strokes */ + stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; + } + else { + /* B) All points, always */ + stroke_ok = true; + } } else { - /* Only selected points in selected strokes */ - point_ok = (pt->flag & GP_SPOINT_SELECT) != 0; + /* C) Only selected points in selected strokes */ + stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0; } - /* do point... */ - if (point_ok) { - copy_v3_v3(td->iloc, &pt->x); - copy_v3_v3(td->center, &pt->x); // XXX: what about t->around == local? - - td->loc = &pt->x; - - td->flag = 0; + /* Do stroke... */ + if (stroke_ok && gps->totpoints) { + bGPDspoint *pt; + int i; - if (pt->flag & GP_SPOINT_SELECT) - td->flag |= TD_SELECTED; - - /* for other transform modes (e.g. shrink-fatten), need to additional data */ - if (t->mode == TFM_GPENCIL_SHRINKFATTEN) { - td->val = &pt->pressure; - td->ival = pt->pressure; - } + /* save falloff factor */ + gps->runtime.multi_frame_falloff = falloff; - /* screenspace needs special matrices... */ - if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) == 0) { - /* screenspace */ - td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; + /* add all necessary points... */ + for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { + bool point_ok; - /* 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); + /* include point? */ + if (is_prop_edit) { + /* Always all points in strokes that get included */ + point_ok = true; } 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... */ - if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) { - td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; - // XXX: matrices may need to be different? + /* Only selected points in selected strokes */ + point_ok = (pt->flag & GP_SPOINT_SELECT) != 0; } - /* 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 */ + /* do point... */ + if (point_ok) { + copy_v3_v3(td->iloc, &pt->x); + copy_v3_v3(td->center, &pt->x); // XXX: what about t->around == local? + + td->loc = &pt->x; + + td->flag = 0; + + if (pt->flag & GP_SPOINT_SELECT) + td->flag |= TD_SELECTED; + + /* for other transform modes (e.g. shrink-fatten), need to additional data + * but never for scale or mirror + */ + if ((t->mode != TFM_RESIZE) && (t->mode != TFM_MIRROR)) { + td->val = &pt->pressure; + td->ival = pt->pressure; + } + + /* screenspace needs special matrices... */ + if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) == 0) { + /* screenspace */ + td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; + + /* 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... */ + if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) { + td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ; + // XXX: matrices may need to be different? + } + + /* apply parent transformations */ + 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; + + /* save pointer to object */ + td->ob = obact; + + td++; + tail++; } } - /* Triangulation must be calculated again, so save the stroke for recalc function */ - td->extra = gps; - td++; - tail++; + /* March over these points, and calculate the proportional editing distances */ + if (is_prop_edit && (head != tail)) { + /* XXX: for now, we are similar enough that this works... */ + calc_distanceCurveVerts(head, tail - 1); + } } } - - /* March over these points, and calculate the proportional editing distances */ - if (is_prop_edit && (head != tail)) { - /* XXX: for now, we are similar enough that this works... */ - calc_distanceCurveVerts(head, tail - 1); - } + } + /* if not multiedit out of loop */ + if (!is_multiedit) { + break; } } } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 3618d57b3ed..a2377166dff 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -74,6 +74,7 @@ #include "BKE_armature.h" #include "BKE_curve.h" #include "BKE_fcurve.h" +#include "BKE_gpencil.h" #include "BKE_lattice.h" #include "BKE_library.h" #include "BKE_main.h" @@ -105,6 +106,7 @@ #include "ED_curve.h" /* for curve_editnurbs */ #include "ED_clip.h" #include "ED_screen.h" +#include "ED_gpencil.h" #include "WM_types.h" #include "WM_api.h" @@ -379,7 +381,8 @@ static void recalcData_actedit(TransInfo *t) /* flush transform values back to actual coordinates */ flushTransIntFrameActionData(t); } - else { + + if (ac.datatype != ANIMCONT_MASK) { /* get animdata blocks visible in editor, assuming that these will be the ones where things changed */ filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_ANIMDATA); ANIM_animdata_filter(&ac, &anim_data, filter, ac.data, ac.datatype); @@ -1311,7 +1314,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } /* GPencil editing context */ - if ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)) { + if (GPENCIL_ANY_MODE(gpd)) { t->options |= CTX_GPENCIL_STROKES; } @@ -1823,6 +1826,21 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) } r_center[2] = 0.0f; } + else if (t->options & CTX_GPENCIL_STROKES) { + /* move cursor in local space */ + TransData *td = NULL; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + float mat[3][3], imat[3][3]; + + td = tc->data; + Object *ob = td->ob; + + sub_v3_v3v3(r_center, r_center, ob->obmat[3]); + copy_m3_m4(mat, ob->obmat); + invert_m3_m3(imat, mat); + mul_m3_v3(imat, r_center); + } + } } void calculateCenterCursor2D(TransInfo *t, float r_center[2]) diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index cbc2b312512..83dfa06f37d 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -107,7 +107,7 @@ #define MAN_SCALE_C (MAN_SCALE_X | MAN_SCALE_Y | MAN_SCALE_Z) /* threshold for testing view aligned gizmo axis */ -struct { +static struct { float min, max; } g_tw_axis_range[2] = { /* Regular range */ @@ -604,6 +604,7 @@ int ED_transform_calc_gizmo_stats( ScrArea *sa = CTX_wm_area(C); ARegion *ar = CTX_wm_region(C); Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); Object *obedit = CTX_data_edit_object(C); View3D *v3d = sa->spacedata.first; @@ -611,7 +612,7 @@ int ED_transform_calc_gizmo_stats( Base *base; Object *ob = OBACT(view_layer); bGPdata *gpd = CTX_data_gpencil_data(C); - const bool is_gp_edit = ((gpd) && (gpd->flag & GP_DATA_STROKE_EDITMODE)); + const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); int a, totsel = 0; const int pivot_point = scene->toolsettings->transform_pivot_point; @@ -728,10 +729,8 @@ int ED_transform_calc_gizmo_stats( /* 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); - } + /* calculate difference matrix */ + ED_gpencil_parent_location(depsgraph, ob, gpd, gpl, diff_mat); for (bGPDstroke *gps = gpl->actframe->strokes.first; gps; gps = gps->next) { /* skip strokes that are invalid for current view */ diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 9a9526537d1..1f1cc646f20 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -510,6 +510,36 @@ static int transform_invoke(bContext *C, wmOperator *op, const wmEvent *event) } } +static bool transform_poll_property(const bContext *UNUSED(C), wmOperator *op, const PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + /* Orientation/Constraints. */ + { + /* Hide orientation axis if no constraints are set, since it wont be used. */ + PropertyRNA *prop_con = RNA_struct_find_property(op->ptr, "constraint_axis"); + if (prop_con && !RNA_property_is_set(op->ptr, prop_con)) { + if (STRPREFIX(prop_id, "constraint")) { + return false; + } + } + } + + /* Proportional Editing. */ + { + PropertyRNA *prop_pet = RNA_struct_find_property(op->ptr, "proportional"); + if (prop_pet && (prop_pet != prop) && + (RNA_property_enum_get(op->ptr, prop_pet) == PROP_EDIT_OFF)) + { + if (STRPREFIX(prop_id, "proportional")) { + return false; + } + } + } + + return true; +} + void Transform_Properties(struct wmOperatorType *ot, int flags) { PropertyRNA *prop; @@ -618,6 +648,7 @@ static void TRANSFORM_OT_translate(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; RNA_def_float_vector_xyz(ot->srna, "value", 3, NULL, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); @@ -643,6 +674,7 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); @@ -676,6 +708,7 @@ static void TRANSFORM_OT_skin_resize(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = skin_resize_poll; + ot->poll_property = transform_poll_property; RNA_def_float_vector(ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Vector", "", -FLT_MAX, FLT_MAX); @@ -698,6 +731,7 @@ static void TRANSFORM_OT_trackball(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; /* Maybe we could use float_vector_xyz here too? */ RNA_def_float_rotation(ot->srna, "value", 2, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -FLT_MAX, FLT_MAX); @@ -721,6 +755,7 @@ static void TRANSFORM_OT_rotate(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); @@ -747,6 +782,7 @@ static void TRANSFORM_OT_tilt(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editcurve_3d; + ot->poll_property = transform_poll_property; RNA_def_float_rotation(ot->srna, "value", 0, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); @@ -769,6 +805,7 @@ static void TRANSFORM_OT_bend(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_region_view3d_active; + ot->poll_property = transform_poll_property; RNA_def_float_rotation(ot->srna, "value", 1, NULL, -FLT_MAX, FLT_MAX, "Angle", "", -M_PI * 2, M_PI * 2); @@ -791,6 +828,7 @@ static void TRANSFORM_OT_shear(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX); @@ -814,6 +852,7 @@ static void TRANSFORM_OT_push_pull(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Distance", "", -FLT_MAX, FLT_MAX); @@ -836,6 +875,7 @@ static void TRANSFORM_OT_shrink_fatten(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh; + ot->poll_property = transform_poll_property; RNA_def_float(ot->srna, "value", 0, -FLT_MAX, FLT_MAX, "Offset", "", -FLT_MAX, FLT_MAX); @@ -861,6 +901,7 @@ static void TRANSFORM_OT_tosphere(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; RNA_def_float_factor(ot->srna, "value", 0, 0, 1, "Factor", "", 0, 1); @@ -883,6 +924,7 @@ static void TRANSFORM_OT_mirror(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; Transform_Properties(ot, P_CONSTRAINT | P_PROPORTIONAL | P_GPENCIL_EDIT | P_CENTER); } @@ -903,6 +945,7 @@ static void TRANSFORM_OT_edge_slide(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh_region_view3d; + ot->poll_property = transform_poll_property; RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); @@ -935,6 +978,7 @@ static void TRANSFORM_OT_vert_slide(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh_region_view3d; + ot->poll_property = transform_poll_property; RNA_def_float_factor(ot->srna, "value", 0, -10.0f, 10.0f, "Factor", "", -1.0f, 1.0f); RNA_def_boolean(ot->srna, "use_even", false, "Even", @@ -964,6 +1008,7 @@ static void TRANSFORM_OT_edge_crease(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_editmesh; + ot->poll_property = transform_poll_property; RNA_def_float_factor(ot->srna, "value", 0, -1.0f, 1.0f, "Factor", "", -1.0f, 1.0f); @@ -1074,6 +1119,7 @@ static void TRANSFORM_OT_transform(struct wmOperatorType *ot) ot->modal = transform_modal; ot->cancel = transform_cancel; ot->poll = ED_operator_screenactive; + ot->poll_property = transform_poll_property; prop = RNA_def_enum(ot->srna, "mode", rna_enum_transform_mode_types, TFM_TRANSLATION, "Mode", ""); RNA_def_property_flag(prop, PROP_HIDDEN); diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index b67fd22dbff..54253e36351 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -1462,7 +1462,7 @@ void snapSequenceBounds(TransInfo *t, const int mval[2]) float xmouse, ymouse; int frame; int mframe; - TransSeq *ts = t->custom.type.data; + TransSeq *ts = TRANS_DATA_CONTAINER_FIRST_SINGLE(t)->custom.type.data; /* reuse increment, strictly speaking could be another snap mode, but leave as is */ if (!(t->modifiers & MOD_SNAP_INVERT)) return; diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index e19320fa220..6dfcfe11af2 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -111,6 +111,10 @@ typedef struct SnapObjectData_EditMesh { SnapObjectData sd; BVHTreeFromEditMesh *bvh_trees[3]; + /* It's like a boundbox. It is tested first to avoid + * to create a bvhtree for all the edited objects. */ + float min[3], max[3]; + } SnapObjectData_EditMesh; struct SnapObjectContext { @@ -156,6 +160,39 @@ struct SnapObjectContext { typedef void(*IterSnapObjsCallback)(SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data); +static void min_max_from_bmesh( + BMesh *bm, float r_min[3], float r_max[3]) +{ + BMIter iter; + BMVert *eve; + + INIT_MINMAX(r_min, r_max); + BM_ITER_MESH(eve, &iter, bm, BM_VERTS_OF_MESH) { + minmax_v3v3_v3(r_min, r_max, eve->co); + } +} + +static SnapObjectData_Mesh *snap_object_data_mesh_create(SnapObjectContext *sctx) +{ + SnapObjectData_Mesh *sod = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); + sod->sd.type = SNAP_MESH; + /* start assuming that it has each of these element types */ + sod->has_looptris = true; + sod->has_loose_edge = true; + sod->has_loose_vert = true; + + return sod; +} + +static SnapObjectData_EditMesh *snap_object_data_editmesh_create(SnapObjectContext *sctx, BMesh *bm) +{ + SnapObjectData_EditMesh *sod = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); + sod->sd.type = SNAP_EDIT_MESH; + min_max_from_bmesh(bm, sod->min, sod->max); + + return sod; +} + /** * Walks through all objects in the scene to create the list of objets to snap. * @@ -169,9 +206,9 @@ static void iter_snap_objects( IterSnapObjsCallback sob_callback, void *data) { - ViewLayer *view_layer = DEG_get_evaluated_view_layer(sctx->depsgraph); - Object *obedit = params->use_object_edit_cage ? OBEDIT_FROM_VIEW_LAYER(view_layer) : NULL; + ViewLayer *view_layer = DEG_get_input_view_layer(sctx->depsgraph); const eSnapSelect snap_select = params->snap_select; + const bool use_object_edit_cage = params->use_object_edit_cage; Base *base_act = view_layer->basact; for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { @@ -179,20 +216,17 @@ static void iter_snap_objects( !((snap_select == SNAP_NOT_SELECTED && ((base->flag & BASE_SELECTED) || (base->flag_legacy & BA_WAS_SEL))) || (snap_select == SNAP_NOT_ACTIVE && base == base_act))) { - bool use_obedit; - Object *obj = base->object; - if (obj->transflag & OB_DUPLI) { + Object *obj_eval = DEG_get_evaluated_object(sctx->depsgraph, base->object); + if (obj_eval->transflag & OB_DUPLI) { DupliObject *dupli_ob; - ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj); + ListBase *lb = object_duplilist(sctx->depsgraph, sctx->scene, obj_eval); for (dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { - use_obedit = obedit && dupli_ob->ob->data == obedit->data; - sob_callback(sctx, use_obedit, use_obedit ? obedit : dupli_ob->ob, dupli_ob->mat, data); + sob_callback(sctx, use_object_edit_cage, dupli_ob->ob, dupli_ob->mat, data); } free_object_duplilist(lb); } - use_obedit = obedit && obj->data == obedit->data; - sob_callback(sctx, use_obedit, use_obedit ? obedit : obj, obj->obmat, data); + sob_callback(sctx, use_object_edit_cage, obj_eval, obj_eval->obmat, data); } } } @@ -372,13 +406,12 @@ static bool raycastMesh( sod = *sod_p; } else { - sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); - sod->sd.type = SNAP_MESH; + sod = *sod_p = snap_object_data_mesh_create(sctx); } BVHTreeFromMesh *treedata = &sod->treedata; - /* The tree is owned by the DM and may have been freed since we last used. */ + /* The tree is owned by the Mesh and may have been freed since we last used. */ if (treedata->tree) { BLI_assert(treedata->cached); if (!bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) { @@ -502,14 +535,30 @@ static bool raycastEditMesh( SnapObjectData_EditMesh *sod = NULL; BVHTreeFromEditMesh *treedata = NULL; + Object *em_ob = em->ob; + + BLI_assert(em_ob->data == BKE_object_get_pre_modified_mesh(ob)); void **sod_p; - if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + /* Use `em->ob` as the key in ghash since the editmesh is used + * to create bvhtree and is the same for each linked object. */ + if (BLI_ghash_ensure_p(sctx->cache.object_map, em_ob, &sod_p)) { sod = *sod_p; } else { - sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); - sod->sd.type = SNAP_EDIT_MESH; + sod = *sod_p = snap_object_data_editmesh_create(sctx, em->bm); + } + + { + float min[3], max[3]; + mul_v3_m4v3(min, obmat, sod->min); + mul_v3_m4v3(max, obmat, sod->max); + + if (!isect_ray_aabb_v3_simple( + ray_start, ray_dir, min, max, NULL, NULL)) + { + return retval; + } } if (sod->bvh_trees[2] == NULL) { @@ -517,7 +566,17 @@ static bool raycastEditMesh( } treedata = sod->bvh_trees[2]; + BVHCache *em_bvh_cache = ((Mesh *)em_ob->data)->runtime.bvh_cache; + + if (sctx->callbacks.edit_mesh.test_face_fn == NULL) { + /* The tree is owned by the Mesh and may have been freed since we last used! */ + if (!bvhcache_has_tree(em_bvh_cache, treedata->tree)) { + free_bvhtree_from_editmesh(treedata); + } + } + if (treedata->tree == NULL) { + BVHCache **bvh_cache = NULL; BLI_bitmap *elem_mask = NULL; int looptri_num_active = -1; @@ -527,7 +586,15 @@ static bool raycastEditMesh( em->bm, elem_mask, sctx->callbacks.edit_mesh.test_face_fn, sctx->callbacks.edit_mesh.user_data); } - bvhtree_from_editmesh_looptri_ex(treedata, em, elem_mask, looptri_num_active, 0.0f, 4, 6, NULL); + else { + /* Only cache if bvhtree is created without a mask. + * This helps keep a standardized bvhtree in cache. */ + bvh_cache = &em_bvh_cache; + } + + bvhtree_from_editmesh_looptri_ex( + treedata, em, elem_mask, looptri_num_active, + 0.0f, 4, 6, bvh_cache); if (elem_mask) { MEM_freeN(elem_mask); @@ -669,7 +736,7 @@ static bool raycastObj( switch (ob->type) { case OB_MESH: - if (use_obedit) { + if (use_obedit && BKE_object_is_in_editmode(ob)) { BMEditMesh *em = BKE_editmesh_from_object(ob); retval = raycastEditMesh( sctx, @@ -718,7 +785,7 @@ struct RaycastObjUserData { bool ret; }; -static void raycast_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, float obmat[4][4], void *data) +static void raycast_obj_cb(SnapObjectContext *sctx, bool use_obedit, Object *ob, float obmat[4][4], void *data) { struct RaycastObjUserData *dt = data; @@ -726,7 +793,7 @@ static void raycast_obj_cb(SnapObjectContext *sctx, bool is_obedit, Object *ob, sctx, dt->ray_start, dt->ray_dir, ob, obmat, dt->ob_index++, - is_obedit, dt->use_occlusion_test, + use_obedit, dt->use_occlusion_test, dt->ray_depth, dt->r_loc, dt->r_no, dt->r_index, dt->r_ob, dt->r_obmat, @@ -1118,6 +1185,13 @@ static short snap_mesh_polygon( }; SnapObjectData *sod = BLI_ghash_lookup(sctx->cache.object_map, ob); + if (sod == NULL) { + /* The object is in edit mode, and the key used + * was the object referenced in BMEditMesh */ + BMEditMesh *em = BKE_editmesh_from_object(ob); + sod = BLI_ghash_lookup(sctx->cache.object_map, em->ob); + } + BLI_assert(sod != NULL); if (sod->type == SNAP_MESH) { @@ -1335,6 +1409,8 @@ static short snapArmature( dist_squared_to_projected_aabb_precalc( &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + use_obedit = use_obedit && BKE_object_is_in_editmode(ob); + if (use_obedit == false) { /* Test BoundBox */ BoundBox *bb = BKE_armature_boundbox_get(ob); @@ -1469,6 +1545,8 @@ static short snapCurve( dist_squared_to_projected_aabb_precalc( &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + use_obedit = use_obedit && BKE_object_is_in_editmode(ob); + if (use_obedit == false) { /* Test BoundBox */ BoundBox *bb = BKE_curve_boundbox_get(ob); @@ -1779,12 +1857,7 @@ static short snapMesh( sod = *sod_p; } else { - sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); - sod->sd.type = SNAP_MESH; - /* start assuming that it has each of these element types */ - sod->has_looptris = true; - sod->has_loose_edge = true; - sod->has_loose_vert = true; + sod = *sod_p = snap_object_data_mesh_create(sctx); } BVHTreeFromMesh *treedata, dummy_treedata; @@ -1792,7 +1865,7 @@ static short snapMesh( treedata = &sod->treedata; bvhtree = sod->bvhtree; - /* the tree is owned by the DM and may have been freed since we last used! */ + /* The tree is owned by the Mesh and may have been freed since we last used! */ if ((sod->has_looptris && treedata->tree && !bvhcache_has_tree(me->runtime.bvh_cache, treedata->tree)) || (sod->has_loose_edge && bvhtree[0] && !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[0])) || (sod->has_loose_vert && bvhtree[1] && !bvhcache_has_tree(me->runtime.bvh_cache, bvhtree[1]))) @@ -1982,21 +2055,57 @@ static short snapEditMesh( SnapObjectData_EditMesh *sod = NULL; BVHTreeFromEditMesh *treedata_vert = NULL, *treedata_edge = NULL; + Object *em_ob = em->ob; + + BLI_assert(em_ob->data == BKE_object_get_pre_modified_mesh(ob)); + UNUSED_VARS_NDEBUG(ob); void **sod_p; - if (BLI_ghash_ensure_p(sctx->cache.object_map, ob, &sod_p)) { + /* Use `em->ob` as the key in ghash since the editmesh is used + * to create bvhtree and is the same for each linked object. */ + if (BLI_ghash_ensure_p(sctx->cache.object_map, em_ob, &sod_p)) { sod = *sod_p; } else { - sod = *sod_p = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(*sod)); - sod->sd.type = SNAP_EDIT_MESH; + sod = *sod_p = snap_object_data_editmesh_create(sctx, em->bm); } + float dist_px_sq = SQUARE(*dist_px); + + { + float min[3], max[3]; + mul_v3_m4v3(min, obmat, sod->min); + mul_v3_m4v3(max, obmat, sod->max); + + /* In vertex and edges you need to get the pixel distance from ray to BoundBox, see: T46099, T46816 */ + struct DistProjectedAABBPrecalc data_precalc; + dist_squared_to_projected_aabb_precalc( + &data_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + + bool dummy[3]; + float bb_dist_px_sq = dist_squared_to_projected_aabb( + &data_precalc, min, max, dummy); + + if (bb_dist_px_sq > dist_px_sq) { + return 0; + } + } + + BVHCache *em_bvh_cache = ((Mesh *)em_ob->data)->runtime.bvh_cache; + if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (sod->bvh_trees[0] == NULL) { sod->bvh_trees[0] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(**sod->bvh_trees)); } treedata_vert = sod->bvh_trees[0]; + + if (sctx->callbacks.edit_mesh.test_vert_fn == NULL) { + /* The tree is owned by the Mesh and may have been freed since we last used! */ + if (!bvhcache_has_tree(em_bvh_cache, treedata_vert->tree)) { + free_bvhtree_from_editmesh(treedata_vert); + } + } + if (treedata_vert->tree == NULL) { BLI_bitmap *verts_mask = NULL; int verts_num_active = -1; @@ -2006,9 +2115,13 @@ static short snapEditMesh( BM_VERTS_OF_MESH, em->bm, verts_mask, (bool(*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_vert_fn, sctx->callbacks.edit_mesh.user_data); + + bvhtree_from_editmesh_verts_ex(treedata_vert, em, verts_mask, verts_num_active, 0.0f, 2, 6); + MEM_freeN(verts_mask); + } + else { + bvhtree_from_editmesh_verts(treedata_vert, em, 0.0f, 2, 6, &em_bvh_cache); } - bvhtree_from_editmesh_verts_ex(treedata_vert, em, verts_mask, verts_num_active, 0.0f, 2, 6); - MEM_SAFE_FREE(verts_mask); } } @@ -2017,6 +2130,14 @@ static short snapEditMesh( sod->bvh_trees[1] = BLI_memarena_calloc(sctx->cache.mem_arena, sizeof(**sod->bvh_trees)); } treedata_edge = sod->bvh_trees[1]; + + if (sctx->callbacks.edit_mesh.test_edge_fn == NULL) { + /* The tree is owned by the Mesh and may have been freed since we last used! */ + if (!bvhcache_has_tree(em_bvh_cache, treedata_edge->tree)) { + free_bvhtree_from_editmesh(treedata_edge); + } + } + if (treedata_edge->tree == NULL) { BLI_bitmap *edges_mask = NULL; int edges_num_active = -1; @@ -2027,9 +2148,12 @@ static short snapEditMesh( (bool(*)(BMElem *, void *))sctx->callbacks.edit_mesh.test_edge_fn, sctx->callbacks.edit_mesh.user_data); + bvhtree_from_editmesh_edges_ex(treedata_edge, em, edges_mask, edges_num_active, 0.0f, 2, 6); + MEM_freeN(edges_mask); + } + else { + bvhtree_from_editmesh_edges(treedata_edge, em, 0.0f, 2, 6, &em_bvh_cache); } - bvhtree_from_editmesh_edges_ex(treedata_edge, em, edges_mask, edges_num_active, 0.0f, 2, 6); - MEM_SAFE_FREE(edges_mask); } } @@ -2043,7 +2167,7 @@ static short snapEditMesh( BVHTreeNearest nearest = { .index = -1, - .dist_sq = SQUARE(*dist_px), + .dist_sq = dist_px_sq, }; int last_index = nearest.index; short elem = SCE_SNAP_MODE_VERTEX; @@ -2119,7 +2243,7 @@ static short snapObject( switch (ob->type) { case OB_MESH: - if (use_obedit) { + if (use_obedit && BKE_object_is_in_editmode(ob)) { BMEditMesh *em = BKE_editmesh_from_object(ob); retval = snapEditMesh( sctx, snapdata, ob, em, obmat, @@ -2156,7 +2280,12 @@ static short snapObject( dist_px, r_loc, r_no, r_index); break; - + case OB_GPENCIL: + retval = snapEmpty( + snapdata, ob, obmat, + dist_px, + r_loc, r_no, r_index); + break; case OB_CAMERA: retval = snapCamera( sctx, snapdata, ob, obmat, diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 14592149579..7c1dc148dde 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -51,9 +51,12 @@ #include "BKE_screen.h" #include "BKE_layer.h" #include "BKE_undo_system.h" +#include "BKE_workspace.h" +#include "BKE_paint.h" #include "ED_gpencil.h" #include "ED_render.h" +#include "ED_object.h" #include "ED_screen.h" #include "ED_undo.h" @@ -110,6 +113,7 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); Scene *scene = CTX_data_scene(C); + ScrArea *sa = CTX_wm_area(C); /* undo during jobs are running can easily lead to freeing data using by jobs, * or they can just lead to freezing job in some other cases */ @@ -122,6 +126,12 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) if (ED_gpencil_session_active()) { return ED_undo_gpencil_step(C, step, undoname); } + if (sa && (sa->spacetype == SPACE_VIEW3D)) { + Object *obact = CTX_data_active_object(C); + if (obact && (obact->type == OB_GPENCIL)) { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + } UndoStep *step_data_from_name = NULL; int step_for_callback = step; @@ -156,6 +166,23 @@ static int ed_undo_step(bContext *C, int step, const char *undoname) else { BKE_undosys_step_undo_compat_only(wm->undo_stack, C, step); } + + /* Set special modes for grease pencil */ + if (sa && (sa->spacetype == SPACE_VIEW3D)) { + Object *obact = CTX_data_active_object(C); + if (obact && (obact->type == OB_GPENCIL)) { + /* set cursor */ + if (ELEM(obact->mode, OB_MODE_GPENCIL_PAINT, OB_MODE_GPENCIL_SCULPT, OB_MODE_GPENCIL_WEIGHT)) { + ED_gpencil_toggle_brush_cursor(C, true, NULL); + } + else { + ED_gpencil_toggle_brush_cursor(C, false, NULL); + } + /* set workspace mode */ + Base *basact = CTX_data_active_base(C); + ED_object_base_activate(C, basact); + } + } } /* App-Handlers (post). */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 51e2c1b6334..32bf32b03ab 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -80,6 +80,7 @@ #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" @@ -4404,6 +4405,15 @@ void ED_keymap_uvedit(wmKeyConfig *keyconf) RNA_string_set(kmi->ptr, "data_path", "tool_settings.use_uv_sculpt"); #endif + /* Select Element (Sync Select: on) */ + ED_keymap_editmesh_elem_mode(keyconf, keymap); + /* Hack to prevent fall-through, when the button isn't visible. */ + WM_keymap_add_item(keymap, "MESH_OT_select_mode", FOURKEY, KM_PRESS, 0, 0); + /* Select Element (Sync Select: off) */ + WM_keymap_add_context_enum_set_items( + keymap, rna_enum_mesh_select_mode_uv_items, "tool_settings.uv_select_mode", + ONEKEY, KM_PRESS, 0, 0); + /* Mark edge seam */ WM_keymap_add_item(keymap, "UV_OT_mark_seam", EKEY, KM_PRESS, KM_CTRL, 0); |