/* helper call for drawing influence/time control curves for a given NLA-strip */ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc) { const float yheight = ymaxc - yminc; /* drawing color is simply a light-gray */ // TODO: is this color suitable? // XXX nasty hacked color for now... which looks quite bad too... glColor3f(0.7f, 0.7f, 0.7f); /* draw with AA'd line */ glEnable(GL_LINE_SMOOTH); glEnable(GL_BLEND); /* influence -------------------------- */ if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { FCurve *fcu = list_find_fcurve(&strip->fcurves, "influence", 0); float cfra; /* plot the curve (over the strip's main region) */ glBegin(GL_LINE_STRIP); /* sample at 1 frame intervals, and draw * - min y-val is yminc, max is y-maxc, so clamp in those regions */ for (cfra = strip->start; cfra <= strip->end; cfra += 1.0f) { float y = evaluate_fcurve(fcu, cfra); CLAMP(y, 0.0f, 1.0f); glVertex2f(cfra, ((y * yheight) + yminc)); } glEnd(); // GL_LINE_STRIP } else { /* use blend in/out values only if both aren't zero */ if ((IS_EQF(strip->blendin, 0.0f) && IS_EQF(strip->blendout, 0.0f)) == 0) { glBegin(GL_LINE_STRIP); /* start of strip - if no blendin, start straight at 1, otherwise from 0 to 1 over blendin frames */ if (IS_EQF(strip->blendin, 0.0f) == 0) { glVertex2f(strip->start, yminc); glVertex2f(strip->start + strip->blendin, ymaxc); } else glVertex2f(strip->start, ymaxc); /* end of strip */ if (IS_EQF(strip->blendout, 0.0f) == 0) { glVertex2f(strip->end - strip->blendout, ymaxc); glVertex2f(strip->end, yminc); } else glVertex2f(strip->end, ymaxc); glEnd(); // GL_LINE_STRIP } } /* time -------------------------- */ // XXX do we want to draw this curve? in a different color too? /* turn off AA'd lines */ glDisable(GL_LINE_SMOOTH); glDisable(GL_BLEND); }
static void gp_stroke_norm_curve_weights(Curve *cu, const float minmax_weights[2]) { Nurb *nu; const float delta = minmax_weights[0]; float fac; int i; /* when delta == minmax_weights[0] == minmax_weights[1], we get div by zero [#35686] */ if (IS_EQF(delta, minmax_weights[1])) fac = 1.0f; else fac = 1.0f / (minmax_weights[1] - delta); for (nu = cu->nurb.first; nu; nu = nu->next) { if (nu->bezt) { BezTriple *bezt = nu->bezt; for (i = 0; i < nu->pntsu; i++, bezt++) { bezt->weight = (bezt->weight - delta) * fac; } } else if (nu->bp) { BPoint *bp = nu->bp; for (i = 0; i < nu->pntsu; i++, bp++) { bp->weight = (bp->weight - delta) * fac; } } } }
static int poselib_remove_exec(bContext *C, wmOperator *op) { Object *ob = get_poselib_object(C); bAction *act = (ob) ? ob->poselib : NULL; TimeMarker *marker; int marker_index; FCurve *fcu; PropertyRNA *prop; /* check if valid poselib */ if (act == NULL) { BKE_report(op->reports, RPT_ERROR, "Object does not have pose lib data"); return OPERATOR_CANCELLED; } prop = RNA_struct_find_property(op->ptr, "pose"); if (RNA_property_is_set(op->ptr, prop)) { marker_index = RNA_property_enum_get(op->ptr, prop); } else { marker_index = act->active_marker - 1; } /* get index (and pointer) of pose to remove */ marker = BLI_findlink(&act->markers, marker_index); if (marker == NULL) { BKE_reportf(op->reports, RPT_ERROR, "Invalid pose specified %d", marker_index); return OPERATOR_CANCELLED; } /* remove relevant keyframes */ for (fcu = act->curves.first; fcu; fcu = fcu->next) { BezTriple *bezt; unsigned int i; if (fcu->bezt) { for (i = 0, bezt = fcu->bezt; i < fcu->totvert; i++, bezt++) { /* check if remove */ if (IS_EQF(bezt->vec[1][0], (float)marker->frame)) { delete_fcurve_key(fcu, i, 1); break; } } } } /* remove poselib from list */ BLI_freelinkN(&act->markers, marker); /* fix active pose number */ act->active_marker = 0; /* send notifiers for this - using keyframe editing notifiers, since action * may be being shown in anim editors as active action */ WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_EDITED, NULL); /* done */ return OPERATOR_FINISHED; }
static void rna_NlaStrip_end_frame_set(PointerRNA *ptr, float value) { NlaStrip *data = (NlaStrip *)ptr->data; /* clamp value to lie within valid limits * - must not have zero or negative length strip, so cannot start before the first frame * + some minimum-strip-length threshold * - cannot end later than the start of the next strip (if present) * -> but if it was a transition, we could go up to the start of the end - some flexibility threshold * as long as we re-adjust the transition afterwards */ if (data->next) { if (data->next->type == NLASTRIP_TYPE_TRANSITION) { CLAMP(value, data->start + NLASTRIP_MIN_LEN_THRESH, data->next->end - NLASTRIP_MIN_LEN_THRESH); /* readjust the transition to stick to the endpoints of the action-clips */ data->next->start = value; } else { CLAMP(value, data->start + NLASTRIP_MIN_LEN_THRESH, data->next->start); } } else { CLAMP(value, data->start + NLASTRIP_MIN_LEN_THRESH, MAXFRAME); } data->end = value; /* calculate the lengths the strip and its action (if applicable) */ if (data->type == NLASTRIP_TYPE_CLIP) { float len, actlen; len = data->end - data->start; actlen = data->actend - data->actstart; if (IS_EQF(actlen, 0.0f)) actlen = 1.0f; /* now, adjust the 'scale' setting to reflect this (so that this change can be valid) */ data->scale = len / ((actlen) * data->repeat); } }
/* main call for drawing a single NLA-strip */ static void nla_draw_strip(SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc) { short nonSolo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) && (nlt->flag & NLATRACK_SOLO) == 0); float color[3]; /* get color of strip */ nla_strip_get_color_inside(adt, strip, color); /* draw extrapolation info first (as backdrop) * - but this should only be drawn if track has some contribution */ if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && (nonSolo == 0)) { /* enable transparency... */ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_BLEND); switch (strip->extendmode) { /* since this does both sides, only do the 'before' side, and leave the rest to the next case */ case NLASTRIP_EXTEND_HOLD: /* only need to draw here if there's no strip before since * it only applies in such a situation */ if (strip->prev == NULL) { /* set the drawing color to the color of the strip, but with very faint alpha */ glColor4f(color[0], color[1], color[2], 0.15f); /* draw the rect to the edge of the screen */ glBegin(GL_QUADS); glVertex2f(v2d->cur.xmin, yminc); glVertex2f(v2d->cur.xmin, ymaxc); glVertex2f(strip->start, ymaxc); glVertex2f(strip->start, yminc); glEnd(); } /* fall-through */ /* this only draws after the strip */ case NLASTRIP_EXTEND_HOLD_FORWARD: /* only need to try and draw if the next strip doesn't occur immediately after */ if ((strip->next == NULL) || (IS_EQF(strip->next->start, strip->end) == 0)) { /* set the drawing color to the color of the strip, but this time less faint */ glColor4f(color[0], color[1], color[2], 0.3f); /* draw the rect to the next strip or the edge of the screen */ glBegin(GL_QUADS); glVertex2f(strip->end, yminc); glVertex2f(strip->end, ymaxc); if (strip->next) { glVertex2f(strip->next->start, ymaxc); glVertex2f(strip->next->start, yminc); } else { glVertex2f(v2d->cur.xmax, ymaxc); glVertex2f(v2d->cur.xmax, yminc); } glEnd(); } break; } glDisable(GL_BLEND); } /* draw 'inside' of strip itself */ if (nonSolo == 0) { /* strip is in normal track */ glColor3fv(color); UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */ UI_draw_roundbox_shade_x(GL_POLYGON, strip->start, yminc, strip->end, ymaxc, 0.0, 0.5, 0.1); } else { /* strip is in disabled track - make less visible */ glColor4f(color[0], color[1], color[2], 0.1f); glEnable(GL_BLEND); glRectf(strip->start, yminc, strip->end, ymaxc); glDisable(GL_BLEND); } /* draw strip's control 'curves' * - only if user hasn't hidden them... */ if ((snla->flag & SNLA_NOSTRIPCURVES) == 0) nla_draw_strip_curves(strip, yminc, ymaxc); /* draw strip outline * - color used here is to indicate active vs non-active */ if (strip->flag & NLASTRIP_FLAG_ACTIVE) { /* strip should appear 'sunken', so draw a light border around it */ glColor3f(0.9f, 1.0f, 0.9f); // FIXME: hardcoded temp-hack colors } else { /* strip should appear to stand out, so draw a dark border around it */ glColor3f(0.0f, 0.0f, 0.0f); } /* - line style: dotted for muted */ if (strip->flag & NLASTRIP_FLAG_MUTED) setlinestyle(4); /* draw outline */ UI_draw_roundbox_shade_x(GL_LINE_LOOP, strip->start, yminc, strip->end, ymaxc, 0.0, 0.0, 0.1); /* if action-clip strip, draw lines delimiting repeats too (in the same color as outline) */ if ((strip->type == NLASTRIP_TYPE_CLIP) && IS_EQF(strip->repeat, 1.0f) == 0) { float repeatLen = (strip->actend - strip->actstart) * strip->scale; int i; /* only draw lines for whole-numbered repeats, starting from the first full-repeat * up to the last full repeat (but not if it lies on the end of the strip) */ for (i = 1; i < strip->repeat; i++) { float repeatPos = strip->start + (repeatLen * i); /* don't draw if line would end up on or after the end of the strip */ if (repeatPos < strip->end) fdrawline(repeatPos, yminc + 4, repeatPos, ymaxc - 4); } } /* or if meta-strip, draw lines delimiting extents of sub-strips (in same color as outline, if more than 1 exists) */ else if ((strip->type == NLASTRIP_TYPE_META) && (strip->strips.first != strip->strips.last)) { NlaStrip *cs; float y = (ymaxc - yminc) / 2.0f + yminc; /* only draw first-level of child-strips, but don't draw any lines on the endpoints */ for (cs = strip->strips.first; cs; cs = cs->next) { /* draw start-line if not same as end of previous (and only if not the first strip) * - on upper half of strip */ if ((cs->prev) && IS_EQF(cs->prev->end, cs->start) == 0) fdrawline(cs->start, y, cs->start, ymaxc); /* draw end-line if not the last strip * - on lower half of strip */ if (cs->next) fdrawline(cs->end, yminc, cs->end, y); } } /* reset linestyle */ setlinestyle(0); }
static void add_bezt_to_keyblocks_list(DLRBT_Tree *blocks, BezTriple *first_bezt, BezTriple *beztn) { ActKeyBlock *new_ab = NULL; BezTriple *prev = NULL; /* get the BezTriple immediately before the given one which has the same value */ if (beztn != first_bezt) { /* XXX: Unless I'm overlooking some details from the past, this should be sufficient? * The old code did some elaborate stuff trying to find keyframe columns for * the given BezTriple, then step backwards to the column before that, and find * an appropriate BezTriple with matching values there. Maybe that was warranted * in the past, but now, that list is only ever filled with keyframes from the * current FCurve. * * -- Aligorith (20140415) */ prev = beztn - 1; } /* check if block needed - same value(s)? * -> firstly, handles must have same central value as each other * -> secondly, handles which control that section of the curve must be constant */ if (prev == NULL) return; if (IS_EQF(beztn->vec[1][1], prev->vec[1][1]) == 0) return; if (IS_EQF(beztn->vec[1][1], beztn->vec[0][1]) == 0) return; if (IS_EQF(prev->vec[1][1], prev->vec[2][1]) == 0) return; /* if there are no blocks already, just add as root */ if (blocks->root == NULL) { /* just add this as the root, then call the tree-balancing functions to validate */ new_ab = bezts_to_new_actkeyblock(prev, beztn); blocks->root = (DLRBT_Node *)new_ab; } else { ActKeyBlock *ab, *abn = NULL; /* try to find a keyblock that starts on the previous beztriple, and add a new one if none start there * Note: we perform a tree traversal here NOT a standard linked-list traversal... * Note: we can't search from end to try to optimize this as it causes errors there's * an A ___ B |---| B situation */ // FIXME: here there is a bug where we are trying to get the summary for the following channels // A|--------------|A ______________ B|--------------|B // A|------------------------------------------------|A // A|----|A|---|A|-----------------------------------|A for (ab = blocks->root; ab; ab = abn) { /* check if this is a match, or whether we go left or right * NOTE: we now use a float threshold to prevent precision errors causing problems with summaries */ if (IS_EQT(ab->start, prev->vec[1][0], BEZT_BINARYSEARCH_THRESH)) { /* set selection status and 'touched' status */ if (BEZSELECTED(beztn)) ab->sel = SELECT; ab->modified++; /* done... no need to insert */ return; } else { ActKeyBlock **abnp = NULL; /* branch to go down - used to hook new blocks to parents */ /* check if go left or right, but if not available, add new node */ if (ab->start < prev->vec[1][0]) abnp = &ab->right; else abnp = &ab->left; /* if this does not exist, add a new node, otherwise continue... */ if (*abnp == NULL) { /* add a new node representing this, and attach it to the relevant place */ new_ab = bezts_to_new_actkeyblock(prev, beztn); new_ab->parent = ab; *abnp = new_ab; break; } else abn = *abnp; } } } /* now, balance the tree taking into account this newly added node */ BLI_dlrbTree_insert(blocks, (DLRBT_Node *)new_ab); }
/* clear rotation of object */ static void object_clear_rot(Object *ob) { /* clear rotations that aren't locked */ if (ob->protectflag & (OB_LOCK_ROTX|OB_LOCK_ROTY|OB_LOCK_ROTZ|OB_LOCK_ROTW)) { if (ob->protectflag & OB_LOCK_ROT4D) { /* perform clamping on a component by component basis */ if (ob->rotmode == ROT_MODE_AXISANGLE) { if ((ob->protectflag & OB_LOCK_ROTW) == 0) ob->rotAngle= ob->drotAngle= 0.0f; if ((ob->protectflag & OB_LOCK_ROTX) == 0) ob->rotAxis[0]= ob->drotAxis[0]= 0.0f; if ((ob->protectflag & OB_LOCK_ROTY) == 0) ob->rotAxis[1]= ob->drotAxis[1]= 0.0f; if ((ob->protectflag & OB_LOCK_ROTZ) == 0) ob->rotAxis[2]= ob->drotAxis[2]= 0.0f; /* check validity of axis - axis should never be 0,0,0 (if so, then we make it rotate about y) */ if (IS_EQF(ob->rotAxis[0], ob->rotAxis[1]) && IS_EQF(ob->rotAxis[1], ob->rotAxis[2])) ob->rotAxis[1] = 1.0f; if (IS_EQF(ob->drotAxis[0], ob->drotAxis[1]) && IS_EQF(ob->drotAxis[1], ob->drotAxis[2])) ob->drotAxis[1]= 1.0f; } else if (ob->rotmode == ROT_MODE_QUAT) { if ((ob->protectflag & OB_LOCK_ROTW) == 0) ob->quat[0]= ob->dquat[0]= 1.0f; if ((ob->protectflag & OB_LOCK_ROTX) == 0) ob->quat[1]= ob->dquat[1]= 0.0f; if ((ob->protectflag & OB_LOCK_ROTY) == 0) ob->quat[2]= ob->dquat[2]= 0.0f; if ((ob->protectflag & OB_LOCK_ROTZ) == 0) ob->quat[3]= ob->dquat[3]= 0.0f; // TODO: does this quat need normalising now? } else { /* the flag may have been set for the other modes, so just ignore the extra flag... */ if ((ob->protectflag & OB_LOCK_ROTX) == 0) ob->rot[0]= ob->drot[0]= 0.0f; if ((ob->protectflag & OB_LOCK_ROTY) == 0) ob->rot[1]= ob->drot[1]= 0.0f; if ((ob->protectflag & OB_LOCK_ROTZ) == 0) ob->rot[2]= ob->drot[2]= 0.0f; } } else { /* perform clamping using euler form (3-components) */ // FIXME: deltas are not handled for these cases yet... float eul[3], oldeul[3], quat1[4] = {0}; if (ob->rotmode == ROT_MODE_QUAT) { copy_qt_qt(quat1, ob->quat); quat_to_eul(oldeul, ob->quat); } else if (ob->rotmode == ROT_MODE_AXISANGLE) { axis_angle_to_eulO(oldeul, EULER_ORDER_DEFAULT, ob->rotAxis, ob->rotAngle); } else { copy_v3_v3(oldeul, ob->rot); } eul[0]= eul[1]= eul[2]= 0.0f; if (ob->protectflag & OB_LOCK_ROTX) eul[0]= oldeul[0]; if (ob->protectflag & OB_LOCK_ROTY) eul[1]= oldeul[1]; if (ob->protectflag & OB_LOCK_ROTZ) eul[2]= oldeul[2]; if (ob->rotmode == ROT_MODE_QUAT) { eul_to_quat(ob->quat, eul); /* quaternions flip w sign to accumulate rotations correctly */ if ((quat1[0]<0.0f && ob->quat[0]>0.0f) || (quat1[0]>0.0f && ob->quat[0]<0.0f)) { mul_qt_fl(ob->quat, -1.0f); } } else if (ob->rotmode == ROT_MODE_AXISANGLE) { eulO_to_axis_angle(ob->rotAxis, &ob->rotAngle,eul, EULER_ORDER_DEFAULT); } else { copy_v3_v3(ob->rot, eul); } } } // Duplicated in source/blender/editors/armature/editarmature.c else { if (ob->rotmode == ROT_MODE_QUAT) { unit_qt(ob->quat); unit_qt(ob->dquat); } else if (ob->rotmode == ROT_MODE_AXISANGLE) { unit_axis_angle(ob->rotAxis, &ob->rotAngle); unit_axis_angle(ob->drotAxis, &ob->drotAngle); } else { zero_v3(ob->rot); zero_v3(ob->drot); } } }
/* draw a given stroke in 2d */ static void gp_draw_stroke_2d(bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, bool debug, int offsx, int offsy, int winx, int winy) { /* 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; } /* tessellation code - draw stroke as series of connected quads with connection * edges rotated to minimize shrinking artifacts, and rounded endcaps */ { bGPDspoint *pt1, *pt2; float pm[2]; int i; glShadeModel(GL_FLAT); glBegin(GL_QUADS); for (i = 0, pt1 = points, pt2 = points + 1; i < (totpoints - 1); i++, pt1++, pt2++) { float s0[2], s1[2]; /* segment 'center' points */ 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 points */ gp_calc_2d_stroke_xy(pt1, sflag, offsx, offsy, winx, winy, s0); gp_calc_2d_stroke_xy(pt2, sflag, offsx, offsy, winx, winy, s1); /* 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]; glVertex2fv(t0); glVertex2fv(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]; /* draw this line twice (first to finish off start cap, then for stroke) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(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]; /* draw this line twice (once for end of current segment, and once for start of next) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(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]; /* draw this line twice (once for end of stroke, and once for endcap)*/ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(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]; glVertex2fv(t1); glVertex2fv(t0); } /* store stroke's 'natural' normal for next stroke to use */ copy_v2_v2(pm, m2); } glEnd(); } /* draw debug points of curve on top? (original stroke points) */ if (debug) { bGPDspoint *pt; int i; glPointSize((float)(thickness_s + 2)); glBegin(GL_POINTS); for (i = 0, pt = points; i < totpoints && pt; i++, pt++) { float co[2]; gp_calc_2d_stroke_xy(pt, sflag, offsx, offsy, winx, winy, co); glVertex2fv(co); } glEnd(); } }
/* draw a given stroke in 2d */ static void gp_draw_stroke (bGPDspoint *points, int totpoints, short thickness_s, short dflag, short sflag, short debug, int offsx, int offsy, int winx, int winy) { /* otherwise thickness is twice that of the 3D view */ float thickness= (float)thickness_s * 0.5f; /* if thickness is less than GP_DRAWTHICKNESS_SPECIAL, 'smooth' opengl lines look better * - 'smooth' opengl lines are also required if Image Editor 'image-based' stroke */ if ( (thickness < GP_DRAWTHICKNESS_SPECIAL) || ((dflag & GP_DRAWDATA_IEDITHACK) && (dflag & GP_DRAWDATA_ONLYV2D)) ) { bGPDspoint *pt; int i; glBegin(GL_LINE_STRIP); for (i=0, pt=points; i < totpoints && pt; i++, pt++) { if (sflag & GP_STROKE_2DSPACE) { glVertex2f(pt->x, pt->y); } else if (sflag & GP_STROKE_2DIMAGE) { const float x= (pt->x * winx) + offsx; const float y= (pt->y * winy) + offsy; glVertex2f(x, y); } else { const float x= (pt->x / 100 * winx) + offsx; const float y= (pt->y / 100 * winy) + offsy; glVertex2f(x, y); } } glEnd(); } /* tessellation code - draw stroke as series of connected quads with connection * edges rotated to minimise shrinking artifacts, and rounded endcaps */ else { bGPDspoint *pt1, *pt2; float pm[2]; int i; glShadeModel(GL_FLAT); glBegin(GL_QUADS); for (i=0, pt1=points, pt2=points+1; i < (totpoints-1); i++, pt1++, pt2++) { float s0[2], s1[2]; /* segment 'center' points */ 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 points */ if (sflag & GP_STROKE_2DSPACE) { s0[0]= pt1->x; s0[1]= pt1->y; s1[0]= pt2->x; s1[1]= pt2->y; } else if (sflag & GP_STROKE_2DIMAGE) { s0[0]= (pt1->x * winx) + offsx; s0[1]= (pt1->y * winy) + offsy; s1[0]= (pt2->x * winx) + offsx; s1[1]= (pt2->y * winy) + offsy; } else { s0[0]= (pt1->x / 100 * winx) + offsx; s0[1]= (pt1->y / 100 * winy) + offsy; s1[0]= (pt2->x / 100 * winx) + offsx; s1[1]= (pt2->y / 100 * winy) + offsy; } /* 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); /* 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]; glVertex2fv(t0); glVertex2fv(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]; /* draw this line twice (first to finish off start cap, then for stroke) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(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]; /* draw this line twice (once for end of current segment, and once for start of next) */ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(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); /* 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]; /* draw this line twice (once for end of stroke, and once for endcap)*/ glVertex2fv(t1); glVertex2fv(t0); glVertex2fv(t0); glVertex2fv(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]; glVertex2fv(t1); glVertex2fv(t0); } /* store stroke's 'natural' normal for next stroke to use */ copy_v2_v2(pm, m2); } glEnd(); } /* draw debug points of curve on top? (original stroke points) */ if (debug) { bGPDspoint *pt; int i; glBegin(GL_POINTS); for (i=0, pt=points; i < totpoints && pt; i++, pt++) { if (sflag & GP_STROKE_2DSPACE) { glVertex2fv(&pt->x); } else if (sflag & GP_STROKE_2DIMAGE) { const float x= (float)((pt->x * winx) + offsx); const float y= (float)((pt->y * winy) + offsy); glVertex2f(x, y); } else { const float x= (float)(pt->x / 100 * winx) + offsx; const float y= (float)(pt->y / 100 * winy) + offsy; glVertex2f(x, y); } } glEnd(); } }
/* main call for drawing a single NLA-strip */ static void nla_draw_strip(SpaceNla *snla, AnimData *adt, NlaTrack *nlt, NlaStrip *strip, View2D *v2d, float yminc, float ymaxc) { const bool non_solo = ((adt && (adt->flag & ADT_NLA_SOLO_TRACK)) && (nlt->flag & NLATRACK_SOLO) == 0); const bool muted = ((nlt->flag & NLATRACK_MUTED) || (strip->flag & NLASTRIP_FLAG_MUTED)); float color[4] = {1.0f, 1.0f, 1.0f, 1.0f}; uint shdr_pos; /* get color of strip */ nla_strip_get_color_inside(adt, strip, color); shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* draw extrapolation info first (as backdrop) * - but this should only be drawn if track has some contribution */ if ((strip->extendmode != NLASTRIP_EXTEND_NOTHING) && (non_solo == 0)) { /* enable transparency... */ GPU_blend_set_func_separate( GPU_SRC_ALPHA, GPU_ONE_MINUS_SRC_ALPHA, GPU_ONE, GPU_ONE_MINUS_SRC_ALPHA); GPU_blend(true); switch (strip->extendmode) { /* since this does both sides, * only do the 'before' side, and leave the rest to the next case */ case NLASTRIP_EXTEND_HOLD: /* only need to draw here if there's no strip before since * it only applies in such a situation */ if (strip->prev == NULL) { /* set the drawing color to the color of the strip, but with very faint alpha */ immUniformColor3fvAlpha(color, 0.15f); /* draw the rect to the edge of the screen */ immRectf(shdr_pos, v2d->cur.xmin, yminc, strip->start, ymaxc); } ATTR_FALLTHROUGH; /* this only draws after the strip */ case NLASTRIP_EXTEND_HOLD_FORWARD: /* only need to try and draw if the next strip doesn't occur immediately after */ if ((strip->next == NULL) || (IS_EQF(strip->next->start, strip->end) == 0)) { /* set the drawing color to the color of the strip, but this time less faint */ immUniformColor3fvAlpha(color, 0.3f); /* draw the rect to the next strip or the edge of the screen */ float x2 = strip->next ? strip->next->start : v2d->cur.xmax; immRectf(shdr_pos, strip->end, yminc, x2, ymaxc); } break; } GPU_blend(false); } /* draw 'inside' of strip itself */ if (non_solo == 0) { immUnbindProgram(); /* strip is in normal track */ UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */ UI_draw_roundbox_shade_x(true, strip->start, yminc, strip->end, ymaxc, 0.0, 0.5, 0.1, color); /* restore current vertex format & program (roundbox trashes it) */ shdr_pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); } else { /* strip is in disabled track - make less visible */ immUniformColor3fvAlpha(color, 0.1f); GPU_blend(true); immRectf(shdr_pos, strip->start, yminc, strip->end, ymaxc); GPU_blend(false); } /* draw strip's control 'curves' * - only if user hasn't hidden them... */ if ((snla->flag & SNLA_NOSTRIPCURVES) == 0) { nla_draw_strip_curves(strip, yminc, ymaxc, shdr_pos); } immUnbindProgram(); /* draw markings indicating locations of local markers * (useful for lining up different actions) */ if ((snla->flag & SNLA_NOLOCALMARKERS) == 0) { nla_strip_draw_markers(strip, yminc, ymaxc); } /* draw strip outline * - color used here is to indicate active vs non-active */ if (strip->flag & NLASTRIP_FLAG_ACTIVE) { /* strip should appear 'sunken', so draw a light border around it */ color[0] = 0.9f; /* FIXME: hardcoded temp-hack colors */ color[1] = 1.0f; color[2] = 0.9f; } else { /* strip should appear to stand out, so draw a dark border around it */ color[0] = color[1] = color[2] = 0.0f; /* FIXME: or 1.0f ?? */ } /* draw outline * - dashed-line shader is loaded after this block */ if (muted) { /* muted - draw dotted, squarish outline (for simplicity) */ shdr_pos = nla_draw_use_dashed_outlines(color, muted); imm_draw_box_wire_2d(shdr_pos, strip->start, yminc, strip->end, ymaxc); } else { /* non-muted - draw solid, rounded outline */ UI_draw_roundbox_shade_x(false, strip->start, yminc, strip->end, ymaxc, 0.0, 0.0, 0.1, color); /* restore current vertex format & program (roundbox trashes it) */ shdr_pos = nla_draw_use_dashed_outlines(color, muted); } /* if action-clip strip, draw lines delimiting repeats too (in the same color as outline) */ if ((strip->type == NLASTRIP_TYPE_CLIP) && strip->repeat > 1.0f) { float repeatLen = (strip->actend - strip->actstart) * strip->scale; /* only draw lines for whole-numbered repeats, starting from the first full-repeat * up to the last full repeat (but not if it lies on the end of the strip) */ immBeginAtMost(GPU_PRIM_LINES, 2 * floorf(strip->repeat)); for (int i = 1; i < strip->repeat; i++) { float repeatPos = strip->start + (repeatLen * i); /* don't draw if line would end up on or after the end of the strip */ if (repeatPos < strip->end) { immVertex2f(shdr_pos, repeatPos, yminc + 4); immVertex2f(shdr_pos, repeatPos, ymaxc - 4); } } immEnd(); } /* or if meta-strip, draw lines delimiting extents of sub-strips * (in same color as outline, if more than 1 exists) */ else if ((strip->type == NLASTRIP_TYPE_META) && (strip->strips.first != strip->strips.last)) { const float y = (ymaxc - yminc) * 0.5f + yminc; /* up to 2 lines per strip */ immBeginAtMost(GPU_PRIM_LINES, 4 * BLI_listbase_count(&strip->strips)); /* only draw first-level of child-strips, but don't draw any lines on the endpoints */ for (NlaStrip *cs = strip->strips.first; cs; cs = cs->next) { /* draw start-line if not same as end of previous (and only if not the first strip) * - on upper half of strip */ if ((cs->prev) && IS_EQF(cs->prev->end, cs->start) == 0) { immVertex2f(shdr_pos, cs->start, y); immVertex2f(shdr_pos, cs->start, ymaxc); } /* draw end-line if not the last strip * - on lower half of strip */ if (cs->next) { immVertex2f(shdr_pos, cs->end, yminc); immVertex2f(shdr_pos, cs->end, y); } } immEnd(); } immUnbindProgram(); }
/* helper call for drawing influence/time control curves for a given NLA-strip */ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, unsigned int pos) { const float yheight = ymaxc - yminc; immUniformColor3f(0.7f, 0.7f, 0.7f); /* draw with AA'd line */ GPU_line_smooth(true); GPU_blend(true); /* influence -------------------------- */ if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { FCurve *fcu = list_find_fcurve(&strip->fcurves, "influence", 0); float cfra; /* plot the curve (over the strip's main region) */ if (fcu) { immBegin(GPU_PRIM_LINE_STRIP, abs((int)(strip->end - strip->start) + 1)); /* sample at 1 frame intervals, and draw * - min y-val is yminc, max is y-maxc, so clamp in those regions */ for (cfra = strip->start; cfra <= strip->end; cfra += 1.0f) { float y = evaluate_fcurve(fcu, cfra); /* assume this to be in 0-1 range */ CLAMP(y, 0.0f, 1.0f); immVertex2f(pos, cfra, ((y * yheight) + yminc)); } immEnd(); } } else { /* use blend in/out values only if both aren't zero */ if ((IS_EQF(strip->blendin, 0.0f) && IS_EQF(strip->blendout, 0.0f)) == 0) { immBeginAtMost(GPU_PRIM_LINE_STRIP, 4); /* start of strip - if no blendin, start straight at 1, * otherwise from 0 to 1 over blendin frames */ if (IS_EQF(strip->blendin, 0.0f) == 0) { immVertex2f(pos, strip->start, yminc); immVertex2f(pos, strip->start + strip->blendin, ymaxc); } else { immVertex2f(pos, strip->start, ymaxc); } /* end of strip */ if (IS_EQF(strip->blendout, 0.0f) == 0) { immVertex2f(pos, strip->end - strip->blendout, ymaxc); immVertex2f(pos, strip->end, yminc); } else { immVertex2f(pos, strip->end, ymaxc); } immEnd(); } } /* turn off AA'd lines */ GPU_line_smooth(false); GPU_blend(false); }
/* helper for apply() - perform sliding for some value */ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, float *val) { float cframe = (float)pso->cframe; float sVal, eVal; float w1, w2; /* get keyframe values for endpoint poses to blend with */ /* previous/start */ sVal = evaluate_fcurve(fcu, (float)pso->prevFrame); /* next/end */ eVal = evaluate_fcurve(fcu, (float)pso->nextFrame); /* if both values are equal, don't do anything */ if (IS_EQF(sVal, eVal)) { (*val) = sVal; return; } /* calculate the relative weights of the endpoints */ if (pso->mode == POSESLIDE_BREAKDOWN) { /* get weights from the percentage control */ w1 = pso->percentage; /* this must come second */ w2 = 1.0f - w1; /* this must come first */ } else { /* - these weights are derived from the relative distance of these * poses from the current frame * - they then get normalized so that they only sum up to 1 */ float wtot; w1 = cframe - (float)pso->prevFrame; w2 = (float)pso->nextFrame - cframe; wtot = w1 + w2; w1 = (w1 / wtot); w2 = (w2 / wtot); } /* depending on the mode, calculate the new value * - in all of these, the start+end values are multiplied by w2 and w1 (respectively), * since multiplication in another order would decrease the value the current frame is closer to */ switch (pso->mode) { case POSESLIDE_PUSH: /* make the current pose more pronounced */ { /* perform a weighted average here, favoring the middle pose * - numerator should be larger than denominator to 'expand' the result * - perform this weighting a number of times given by the percentage... */ int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */ while (iters-- > 0) { (*val) = (-((sVal * w2) + (eVal * w1)) + ((*val) * 6.0f) ) / 5.0f; } break; } case POSESLIDE_RELAX: /* make the current pose more like its surrounding ones */ { /* perform a weighted average here, favoring the middle pose * - numerator should be smaller than denominator to 'relax' the result * - perform this weighting a number of times given by the percentage... */ int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */ while (iters-- > 0) { (*val) = ( ((sVal * w2) + (eVal * w1)) + ((*val) * 5.0f) ) / 6.0f; } break; } case POSESLIDE_BREAKDOWN: /* make the current pose slide around between the endpoints */ { /* perform simple linear interpolation - coefficient for start must come from pso->percentage... */ /* TODO: make this use some kind of spline interpolation instead? */ (*val) = ((sVal * w2) + (eVal * w1)); break; } } }