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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLukas Tönne <lukas.toenne@gmail.com>2018-08-12 14:52:49 +0300
committerLukas Tönne <lukas.toenne@gmail.com>2018-08-12 14:52:49 +0300
commitdc2d841b7c50565302af2986d62ddbd29c332acd (patch)
tree324f234e56fde77fb80dfdef0c7c21f01968f71b /source/blender/editors/gpencil/gpencil_utils.c
parent27b28e437d974ebbafa234205941c07aa0ab546c (diff)
parent4b6fa4d897a0bb3252b16383492b526d2cef3920 (diff)
Merge branch 'blender2.8' into hair_guideshair_guides
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_utils.c')
-rw-r--r--source/blender/editors/gpencil/gpencil_utils.c1264
1 files changed, 912 insertions, 352 deletions
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, &region_rect);
+ return BLI_rcti_isect_pt_v(&region_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);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
}
/* ******************************************************** */