diff options
Diffstat (limited to 'source/blender/src/gpencil.c')
-rw-r--r-- | source/blender/src/gpencil.c | 272 |
1 files changed, 196 insertions, 76 deletions
diff --git a/source/blender/src/gpencil.c b/source/blender/src/gpencil.c index b9488d20200..f6703fbe66b 100644 --- a/source/blender/src/gpencil.c +++ b/source/blender/src/gpencil.c @@ -63,6 +63,7 @@ #include "BKE_armature.h" #include "BKE_curve.h" #include "BKE_image.h" +#include "BKE_library.h" #include "BIF_gl.h" #include "BIF_glutil.h" @@ -111,7 +112,7 @@ void free_gpencil_strokes (bGPDframe *gpf) gpsn= gps->next; /* free stroke memory arrays, then stroke itself */ - MEM_freeN(gps->points); + if (gps->points) MEM_freeN(gps->points); BLI_freelinkN(&gpf->strokes, gps); } } @@ -134,7 +135,7 @@ void free_gpencil_frames (bGPDlayer *gpl) } } -/* Free all of the gp-layers for a viewport (list should be &G.vd->gpd or so) */ +/* Free all of the gp-layers for a viewport (list should be &gpd->layers or so) */ void free_gpencil_layers (ListBase *list) { bGPDlayer *gpl, *gpln; @@ -254,13 +255,63 @@ bGPdata *gpencil_data_addnew (void) /* -------- Data Duplication ---------- */ +/* make a copy of a given gpencil frame */ +bGPDframe *gpencil_frame_duplicate (bGPDframe *src) +{ + bGPDstroke *gps, *gpsd; + bGPDframe *dst; + + /* error checking */ + if (src == NULL) + return NULL; + + /* make a copy of the source frame */ + dst= MEM_dupallocN(src); + + /* copy strokes */ + dst->strokes.first = dst->strokes.last= NULL; + for (gps= src->strokes.first; gps; gps= gps->next) { + /* make copy of source stroke, then adjust pointer to points too */ + gpsd= MEM_dupallocN(gps); + gpsd->points= MEM_dupallocN(gps->points); + + BLI_addtail(&dst->strokes, gpsd); + } + + /* return new frame */ + return dst; +} + +/* make a copy of a given gpencil layer */ +bGPDlayer *gpencil_layer_duplicate (bGPDlayer *src) +{ + bGPDframe *gpf, *gpfd; + bGPDlayer *dst; + + /* error checking */ + if (src == NULL) + return NULL; + + /* make a copy of source layer */ + dst= MEM_dupallocN(src); + + /* copy frames */ + dst->frames.first= dst->frames.last= NULL; + for (gpf= src->frames.first; gpf; gpf= gpf->next) { + /* make a copy of source stroke */ + gpfd= gpencil_frame_duplicate(gpf); + BLI_addtail(&dst->frames, gpfd); + } + + /* return new layer */ + return dst; +} + /* make a copy of a given gpencil datablock */ bGPdata *gpencil_data_duplicate (bGPdata *src) { + bGPDlayer *gpl, *gpld; bGPdata *dst; - bGPDlayer *gpld, *gpls; - bGPDframe *gpfd, *gpfs; - bGPDstroke *gps; /* error checking */ if (src == NULL) @@ -270,25 +321,11 @@ bGPdata *gpencil_data_duplicate (bGPdata *src) dst= MEM_dupallocN(src); /* copy layers */ - duplicatelist(&dst->layers, &src->layers); - - for (gpld=dst->layers.first, gpls=src->layers.first; gpld && gpls; - gpld=gpld->next, gpls=gpls->next) - { - /* copy frames */ - duplicatelist(&gpld->frames, &gpls->frames); - - for (gpfd=gpld->frames.first, gpfs=gpls->frames.first; gpfd && gpfs; - gpfd=gpfd->next, gpfs=gpfs->next) - { - /* copy strokes */ - duplicatelist(&gpfd->strokes, &gpfs->strokes); - - for (gps= gpfd->strokes.first; gps; gps= gps->next) - { - gps->points= MEM_dupallocN(gps->points); - } - } + dst->layers.first= dst->layers.last= NULL; + for (gpl= src->layers.first; gpl; gpl= gpl->next) { + /* make a copy of source layer and its data */ + gpld= gpencil_layer_duplicate(gpl); + BLI_addtail(&dst->layers, gpld); } /* return new */ @@ -414,6 +451,30 @@ short gpencil_data_setactive (ScrArea *sa, bGPdata *gpd) return 0; } +/* return the ScrArea that has the given GP-datablock + * - assumes that only searching in current screen + * - is based on GP-datablocks only being able to + * exist for one area at a time (i.e. not multiuser) + */ +ScrArea *gpencil_data_findowner (bGPdata *gpd) +{ + ScrArea *sa; + + /* error checking */ + if (gpd == NULL) + return NULL; + + /* loop over all scrareas for current screen, and check if that area has this gpd */ + for (sa= G.curscreen->areabase.first; sa; sa= sa->next) { + /* use get-active func to see if match */ + if (gpencil_data_getactive(sa) == gpd) + return sa; + } + + /* not found */ + return NULL; +} + /* -------- GP-Frame API ---------- */ /* delete the last stroke of the given frame */ @@ -538,6 +599,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew) else { /* unresolved errogenous situation! */ printf("Error: cannot find appropriate gp-frame \n"); + /* gpl->actframe should still be NULL */ } } else { @@ -546,6 +608,7 @@ bGPDframe *gpencil_layer_getframe (bGPDlayer *gpl, int cframe, short addnew) gpl->actframe= gpencil_frame_addnew(gpl, cframe); else { /* don't do anything... this may be when no frames yet! */ + /* gpl->actframe should still be NULL */ } } @@ -791,7 +854,7 @@ static void gp_stroke_to_bezier (bGPDlayer *gpl, bGPDstroke *gps, Curve *cu) /* set settings */ bezt->h1= bezt->h2= HD_FREE; bezt->f1= bezt->f2= bezt->f3= SELECT; - bezt->radius = bezt->weight = pt->pressure * gpl->thickness; + bezt->radius = bezt->weight = pt->pressure * gpl->thickness * 0.1; } /* must calculate handles or else we crash */ @@ -816,17 +879,20 @@ static void gp_layer_to_curve (bGPdata *gpd, bGPDlayer *gpl, short mode) /* only convert if there are any strokes on this layer's frame to convert */ if (gpf->strokes.first == NULL) return; - - /* initialise the curve */ - cu= add_curve(gpl->info, 1); - cu->flag |= CU_3D; - /* init the curve object (remove rotation and assign curve data to it) */ + /* init the curve object (remove rotation and get curve data from it) + * - must clear transforms set on object, as those skew our results + */ add_object_draw(OB_CURVE); ob= OBACT; ob->loc[0]= ob->loc[1]= ob->loc[2]= 0; ob->rot[0]= ob->rot[1]= ob->rot[2]= 0; - ob->data= cu; + cu= ob->data; + cu->flag |= CU_3D; + + /* rename object and curve to layer name */ + rename_id((ID *)ob, gpl->info); + rename_id((ID *)cu, gpl->info); /* add points to curve */ for (gps= gpf->strokes.first; gps; gps= gps->next) { @@ -865,25 +931,26 @@ static void gp_stroke_to_bonechain (bGPDlayer *gpl, bGPDstroke *gps, bArmature * VecCopyf(ebo->tail, p3db); /* add new bone - note: sync with editarmature.c::add_editbone() */ - BLI_strncpy(ebo->name, "Stroke", 32); - unique_editbone_name(bones, ebo->name); - - BLI_addtail(bones, ebo); - - ebo->flag |= BONE_CONNECTED; - ebo->weight= 1.0F; - ebo->dist= 0.25F; - ebo->xwidth= 0.1; - ebo->zwidth= 0.1; - ebo->ease1= 1.0; - ebo->ease2= 1.0; - ebo->rad_head= pt->pressure * gpl->thickness * 0.1; - ebo->rad_tail= ptn->pressure * gpl->thickness * 0.1; - ebo->segments= 1; - ebo->layer= arm->layer; + { + BLI_strncpy(ebo->name, "Stroke", 32); + unique_editbone_name(bones, ebo->name); + + BLI_addtail(bones, ebo); + + ebo->flag |= BONE_CONNECTED; + ebo->weight= 1.0F; + ebo->dist= 0.25F; + ebo->xwidth= 0.1; + ebo->zwidth= 0.1; + ebo->ease1= 1.0; + ebo->ease2= 1.0; + ebo->rad_head= pt->pressure * gpl->thickness * 0.1; + ebo->rad_tail= ptn->pressure * gpl->thickness * 0.1; + ebo->segments= 1; + ebo->layer= arm->layer; + } /* set parenting */ - // TODO: also adjust roll.... ebo->parent= prev; } } @@ -904,22 +971,43 @@ static void gp_layer_to_armature (bGPdata *gpd, bGPDlayer *gpl, short mode) /* only convert if there are any strokes on this layer's frame to convert */ if (gpf->strokes.first == NULL) return; - - /* initialise the armature */ - arm= add_armature(gpl->info); - /* init the armature object (remove rotation and assign armature data to it) */ + /* init the armature object (remove rotation and assign armature data to it) + * - must clear transforms set on object, as those skew our results + */ add_object_draw(OB_ARMATURE); ob= OBACT; ob->loc[0]= ob->loc[1]= ob->loc[2]= 0; ob->rot[0]= ob->rot[1]= ob->rot[2]= 0; - ob->data= arm; + arm= ob->data; + + /* rename object and armature to layer name */ + rename_id((ID *)ob, gpl->info); + rename_id((ID *)arm, gpl->info); /* convert segments to bones, strokes to bone chains */ for (gps= gpf->strokes.first; gps; gps= gps->next) { gp_stroke_to_bonechain(gpl, gps, arm, &bones); } + /* adjust roll of bones + * - set object as EditMode object, but need to clear afterwards! + * - use 'align to world z-up' option + */ + { + /* set our data as if we're in editmode to fool auto_align_armature() */ + G.obedit= ob; + G.edbo.first = bones.first; + G.edbo.last = bones.last; + + /* WARNING: need to make sure this magic number doesn't change */ + auto_align_armature(2); + + /* clear editbones (not needed anymore) */ + G.edbo.first= G.edbo.last= NULL; + G.obedit= NULL; + } + /* flush editbones to armature */ editbones_to_armature(&bones, ob); if (bones.first) BLI_freelistN(&bones); @@ -990,12 +1078,15 @@ void gpencil_convert_menu (void) /* maximum sizes of gp-session buffer */ #define GP_STROKE_BUFFER_MAX 5000 -/* Hardcoded sensitivity thresholds... */ +/* Macros for accessing sensitivity thresholds... */ /* minimum number of pixels mouse should move before new point created */ -#define MIN_MANHATTEN_PX U.gp_manhattendist +#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 +#define MIN_EUCLIDEAN_PX (U.gp_euclideandist) +/* macro to test if only converting endpoints - only for use when converting! */ +#define GP_BUFFER2STROKE_ENDPOINTS ((gpd->flag & GP_DATA_EDITPAINT) && (G.qual & LR_CTRLKEY)) + /* ------ */ /* Temporary 'Stroke' Operation data */ @@ -1013,6 +1104,10 @@ typedef struct tGPsdata { short mval[2]; /* current mouse-position */ short mvalo[2]; /* previous recorded mouse-position */ + + short pressure; /* current stylus pressure */ + short opressure; /* previous stylus pressure */ + short radius; /* radius of influence for eraser */ } tGPsdata; @@ -1180,6 +1275,34 @@ static short gp_stroke_addpoint (tGPsdata *p, short mval[2], float pressure) return GP_STROKEADD_NORMAL; } +/* smooth a stroke (in buffer) before storing it */ +static void gp_stroke_smooth (tGPsdata *p) +{ + bGPdata *gpd= p->gpd; + int i=0, cmx=gpd->sbuffer_size; + + /* only smooth if smoothing is enabled, and we're not doing a straight line */ + if ( !(U.gp_settings & GP_PAINT_DOSMOOTH) || GP_BUFFER2STROKE_ENDPOINTS) + return; + + /* don't try if less than 2 points in buffer */ + if ((cmx <= 2) || (gpd->sbuffer == NULL)) + return; + + /* apply weighting-average (note doing this along path sequentially does introduce slight error) */ + for (i=0; i < gpd->sbuffer_size; i++) { + tGPspoint *pc= (((tGPspoint *)gpd->sbuffer) + i); + tGPspoint *pb= (i-1 > 0)?(pc-1):(pc); + tGPspoint *pa= (i-2 > 0)?(pc-2):(pb); + tGPspoint *pd= (i+1 < cmx)?(pc+1):(pc); + tGPspoint *pe= (i+2 < cmx)?(pc+2):(pd); + + pc->x= (short)(0.1*pa->x + 0.2*pb->x + 0.4*pc->x + 0.2*pd->x + 0.1*pe->x); + pc->y= (short)(0.1*pa->y + 0.2*pb->y + 0.4*pc->y + 0.2*pd->y + 0.1*pe->y); + } +} + + /* make a new stroke from the buffer data */ static void gp_stroke_newfrombuffer (tGPsdata *p) { @@ -1188,9 +1311,6 @@ static void gp_stroke_newfrombuffer (tGPsdata *p) bGPDspoint *pt; tGPspoint *ptc; int i, totelem; - - /* macro to test if only converting endpoints */ - #define GP_BUFFER2STROKE_ENDPOINTS ((gpd->flag & GP_DATA_EDITPAINT) && (G.qual & LR_CTRLKEY)) /* get total number of points to allocate space for: * - in 'Draw Mode', holding the Ctrl-Modifier will only take endpoints @@ -1261,9 +1381,6 @@ static void gp_stroke_newfrombuffer (tGPsdata *p) /* add stroke to frame */ BLI_addtail(&p->gpf->strokes, gps); - - /* undefine macro to test if only converting endpoints */ - #undef GP_BUFFER2STROKE_ENDPOINTS } /* --- 'Eraser' for 'Paint' Tool ------ */ @@ -1662,7 +1779,6 @@ static void gp_paint_initstroke (tGPsdata *p, short paintmode) break; case SPACE_SEQ: { - /* for now, this is not applicable here... */ p->gpd->sbuffer_sflag |= GP_STROKE_2DIMAGE; } break; @@ -1682,6 +1798,9 @@ static void gp_paint_strokeend (tGPsdata *p) { /* check if doing eraser or not */ if ((p->gpd->sbuffer_sflag & GP_STROKE_ERASER) == 0) { + /* smooth stroke before transferring? */ + gp_stroke_smooth(p); + /* transfer stroke to frame */ gp_stroke_newfrombuffer(p); } @@ -1715,7 +1834,6 @@ static void gp_paint_cleanup (tGPsdata *p) short gpencil_paint (short mousebutton, short paintmode) { tGPsdata p; - float opressure, pressure; short ok = GP_STROKEADD_NORMAL; /* init paint-data */ @@ -1735,14 +1853,15 @@ short gpencil_paint (short mousebutton, short paintmode) /* init drawing-device settings */ getmouseco_areawin(p.mval); - pressure = get_pressure(); + p.pressure = get_pressure(); p.mvalo[0]= p.mval[0]; p.mvalo[1]= p.mval[1]; - opressure= pressure; + p.opressure= p.pressure; - /* radius for eraser circle is thickness^2 */ - p.radius= p.gpl->thickness * p.gpl->thickness; + /* radius for eraser circle is defined in userprefs now */ + // TODO: make this more easily tweaked... + p.radius= U.gp_eraser; /* start drawing eraser-circle (if applicable) */ if (paintmode == GP_PAINTMODE_ERASER) @@ -1754,8 +1873,8 @@ short gpencil_paint (short mousebutton, short paintmode) * - not erasing */ if (paintmode != GP_PAINTMODE_ERASER) { - if (!(pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) { - gp_stroke_addpoint(&p, p.mval, pressure); + if (!(p.pressure >= 0.99f) || (p.gpd->flag & GP_DATA_EDITPAINT)) { + gp_stroke_addpoint(&p, p.mval, p.pressure); } } @@ -1763,7 +1882,7 @@ short gpencil_paint (short mousebutton, short paintmode) do { /* get current user input */ getmouseco_areawin(p.mval); - pressure = get_pressure(); + p.pressure = get_pressure(); /* only add current point to buffer if mouse moved (otherwise wait until it does) */ if (paintmode == GP_PAINTMODE_ERASER) { @@ -1775,10 +1894,11 @@ short gpencil_paint (short mousebutton, short paintmode) p.mvalo[0]= p.mval[0]; p.mvalo[1]= p.mval[1]; + p.opressure= p.pressure; } else if (gp_stroke_filtermval(&p, p.mval, p.mvalo)) { /* try to add point */ - ok= gp_stroke_addpoint(&p, p.mval, pressure); + ok= gp_stroke_addpoint(&p, p.mval, p.pressure); /* handle errors while adding point */ if ((ok == GP_STROKEADD_FULL) || (ok == GP_STROKEADD_OVERFLOW)) { @@ -1786,8 +1906,8 @@ short gpencil_paint (short mousebutton, short paintmode) gp_paint_strokeend(&p); /* start a new stroke, starting from previous point */ - gp_stroke_addpoint(&p, p.mvalo, opressure); - ok= gp_stroke_addpoint(&p, p.mval, pressure); + gp_stroke_addpoint(&p, p.mvalo, p.opressure); + ok= gp_stroke_addpoint(&p, p.mval, p.pressure); } else if (ok == GP_STROKEADD_INVALID) { /* the painting operation cannot continue... */ @@ -1802,7 +1922,7 @@ short gpencil_paint (short mousebutton, short paintmode) p.mvalo[0]= p.mval[0]; p.mvalo[1]= p.mval[1]; - opressure= pressure; + p.opressure= p.pressure; } else BIF_wait_for_statechange(); @@ -1820,7 +1940,7 @@ short gpencil_paint (short mousebutton, short paintmode) /* check size of buffer before cleanup, to determine if anything happened here */ if (paintmode == GP_PAINTMODE_ERASER) { - ok= 1; // fixme + ok= 1; /* assume that we did something... */ draw_sel_circle(NULL, p.mvalo, 0, p.radius, 0); } else |