/* helper func - just draw the F-Curve by sampling the visible region (for drawing curves with modifiers) */ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, View2DGrid *grid) { ChannelDriver *driver; float samplefreq, ctime; float stime, etime; float unitFac; float dx, dy; short mapping_flag = ANIM_get_normalization_flags(ac); /* when opening a blend file on a different sized screen or while dragging the toolbar this can happen * best just bail out in this case */ UI_view2d_grid_size(grid, &dx, &dy); if (dx <= 0.0f) return; /* disable any drivers temporarily */ driver = fcu->driver; fcu->driver = NULL; /* compute unit correction factor */ unitFac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag); /* Note about sampling frequency: * Ideally, this is chosen such that we have 1-2 pixels = 1 segment * which means that our curves can be as smooth as possible. However, * this does mean that curves may not be fully accurate (i.e. if they have * sudden spikes which happen at the sampling point, we may have problems). * Also, this may introduce lower performance on less densely detailed curves,' * though it is impossible to predict this from the modifiers! * * If the automatically determined sampling frequency is likely to cause an infinite * loop (i.e. too close to 0), then clamp it to a determined "safe" value. The value * chosen here is just the coarsest value which still looks reasonable... */ /* grid->dx represents the number of 'frames' between gridlines, but we divide by U.v2d_min_gridsize to get pixels-steps */ /* TODO: perhaps we should have 1.0 frames as upper limit so that curves don't get too distorted? */ samplefreq = dx / (U.v2d_min_gridsize * U.pixelsize); if (samplefreq < 0.00001f) samplefreq = 0.00001f; /* the start/end times are simply the horizontal extents of the 'cur' rect */ stime = v2d->cur.xmin; etime = v2d->cur.xmax + samplefreq; /* + samplefreq here so that last item gets included... */ /* at each sampling interval, add a new vertex * - apply the unit correction factor to the calculated values so that * the displayed values appear correctly in the viewport */ glBegin(GL_LINE_STRIP); for (ctime = stime; ctime <= etime; ctime += samplefreq) glVertex2f(ctime, evaluate_fcurve(fcu, ctime) * unitFac); glEnd(); /* restore driver */ fcu->driver = driver; }
/* Evaluates the curves between each selected keyframe on each frame, and keys the value */ void sample_fcurve (FCurve *fcu) { BezTriple *bezt, *start=NULL, *end=NULL; tempFrameValCache *value_cache, *fp; int sfra, range; int i, n, nIndex; /* find selected keyframes... once pair has been found, add keyframes */ for (i=0, bezt=fcu->bezt; i < fcu->totvert; i++, bezt++) { /* check if selected, and which end this is */ if (BEZSELECTED(bezt)) { if (start) { /* set end */ end= bezt; /* cache values then add keyframes using these values, as adding * keyframes while sampling will affect the outcome... * - only start sampling+adding from index=1, so that we don't overwrite original keyframe */ range= (int)( ceil(end->vec[1][0] - start->vec[1][0]) ); sfra= (int)( floor(start->vec[1][0]) ); if (range) { value_cache= MEM_callocN(sizeof(tempFrameValCache)*range, "IcuFrameValCache"); /* sample values */ for (n=1, fp=value_cache; n<range && fp; n++, fp++) { fp->frame= (float)(sfra + n); fp->val= evaluate_fcurve(fcu, fp->frame); } /* add keyframes with these, tagging as 'breakdowns' */ for (n=1, fp=value_cache; n<range && fp; n++, fp++) { nIndex= insert_vert_fcurve(fcu, fp->frame, fp->val, 1); BEZKEYTYPE(fcu->bezt + nIndex)= BEZT_KEYTYPE_BREAKDOWN; } /* free temp cache */ MEM_freeN(value_cache); /* as we added keyframes, we need to compensate so that bezt is at the right place */ bezt = fcu->bezt + i + range - 1; i += (range - 1); } /* bezt was selected, so it now marks the start of a whole new chain to search */ start= bezt; end= NULL; } else { /* just set start keyframe */ start= bezt; end= NULL; } } } /* recalculate channel's handles? */ calchandles_fcurve(fcu); }
/* 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); }
/* Create new libmv Tracks structure from blender's tracks list. */ static struct libmv_Tracks *libmv_tracks_new(MovieClip *clip, ListBase *tracksbase, int width, int height) { int tracknr = 0; MovieTrackingTrack *track; struct libmv_Tracks *tracks = libmv_tracksNew(); track = tracksbase->first; while (track) { FCurve *weight_fcurve; int a = 0; weight_fcurve = id_data_find_fcurve(&clip->id, track, &RNA_MovieTrackingTrack, "weight", 0, NULL); for (a = 0; a < track->markersnr; a++) { MovieTrackingMarker *marker = &track->markers[a]; if ((marker->flag & MARKER_DISABLED) == 0) { float weight = track->weight; if (weight_fcurve) { int scene_framenr = BKE_movieclip_remap_clip_to_scene_frame(clip, marker->framenr); weight = evaluate_fcurve(weight_fcurve, scene_framenr); } libmv_tracksInsert(tracks, marker->framenr, tracknr, (marker->pos[0] + track->offset[0]) * width, (marker->pos[1] + track->offset[1]) * height, weight); } } track = track->next; tracknr++; } return tracks; }
/* helper func - just draw the F-Curve by sampling the visible region (for drawing curves with modifiers) */ static void draw_fcurve_curve(bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, View2DGrid *grid) { SpaceIpo *sipo = (SpaceIpo *)ac->sl; ChannelDriver *driver; float samplefreq; float stime, etime; float unitFac, offset; float dx, dy; short mapping_flag = ANIM_get_normalization_flags(ac); int i, n; /* when opening a blend file on a different sized screen or while dragging the toolbar this can happen * best just bail out in this case */ UI_view2d_grid_size(grid, &dx, &dy); if (dx <= 0.0f) return; /* disable any drivers temporarily */ driver = fcu->driver; fcu->driver = NULL; /* compute unit correction factor */ unitFac = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset); /* Note about sampling frequency: * Ideally, this is chosen such that we have 1-2 pixels = 1 segment * which means that our curves can be as smooth as possible. However, * this does mean that curves may not be fully accurate (i.e. if they have * sudden spikes which happen at the sampling point, we may have problems). * Also, this may introduce lower performance on less densely detailed curves, * though it is impossible to predict this from the modifiers! * * If the automatically determined sampling frequency is likely to cause an infinite * loop (i.e. too close to 0), then clamp it to a determined "safe" value. The value * chosen here is just the coarsest value which still looks reasonable... */ /* grid->dx represents the number of 'frames' between gridlines, but we divide by U.v2d_min_gridsize to get pixels-steps */ /* TODO: perhaps we should have 1.0 frames as upper limit so that curves don't get too distorted? */ samplefreq = dx / (U.v2d_min_gridsize * U.pixelsize); if (sipo->flag & SIPO_BEAUTYDRAW_OFF) { /* Low Precision = coarse lower-bound clamping * * Although the "Beauty Draw" flag was originally for AA'd * line drawing, the sampling rate here has a much greater * impact on performance (e.g. for T40372)! * * This one still amounts to 10 sample-frames for each 1-frame interval * which should be quite a decent approximation in many situations. */ if (samplefreq < 0.1f) samplefreq = 0.1f; } else { /* "Higher Precision" but slower - especially on larger windows (e.g. T40372) */ if (samplefreq < 0.00001f) samplefreq = 0.00001f; } /* the start/end times are simply the horizontal extents of the 'cur' rect */ stime = v2d->cur.xmin; etime = v2d->cur.xmax + samplefreq; /* + samplefreq here so that last item gets included... */ /* at each sampling interval, add a new vertex * - apply the unit correction factor to the calculated values so that * the displayed values appear correctly in the viewport */ glBegin(GL_LINE_STRIP); n = (etime - stime) / samplefreq + 0.5f; for (i = 0; i <= n; i++) { float ctime = stime + i * samplefreq; glVertex2f(ctime, (evaluate_fcurve(fcu, ctime) + offset) * unitFac); } glEnd(); /* restore driver */ fcu->driver = driver; }
/* helper for apply() - perform sliding for quaternion rotations (using quat blending) */ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) { FCurve *fcu_w = NULL, *fcu_x = NULL, *fcu_y = NULL, *fcu_z = NULL; bPoseChannel *pchan = pfl->pchan; LinkData *ld = NULL; char *path = NULL; float cframe; /* get the path to use - this should be quaternion rotations only (needs care) */ path = BLI_sprintfN("%s.%s", pfl->pchan_path, "rotation_quaternion"); /* get the current frame number */ cframe = (float)pso->cframe; /* using this path, find each matching F-Curve for the variables we're interested in */ while ( (ld = poseAnim_mapping_getNextFCurve(&pfl->fcurves, ld, path)) ) { FCurve *fcu = (FCurve *)ld->data; /* assign this F-Curve to one of the relevant pointers... */ switch (fcu->array_index) { case 3: /* z */ fcu_z = fcu; break; case 2: /* y */ fcu_y = fcu; break; case 1: /* x */ fcu_x = fcu; break; case 0: /* w */ fcu_w = fcu; break; } } /* only if all channels exist, proceed */ if (fcu_w && fcu_x && fcu_y && fcu_z) { float quat_prev[4], quat_next[4]; /* get 2 quats */ quat_prev[0] = evaluate_fcurve(fcu_w, pso->prevFrame); quat_prev[1] = evaluate_fcurve(fcu_x, pso->prevFrame); quat_prev[2] = evaluate_fcurve(fcu_y, pso->prevFrame); quat_prev[3] = evaluate_fcurve(fcu_z, pso->prevFrame); quat_next[0] = evaluate_fcurve(fcu_w, pso->nextFrame); quat_next[1] = evaluate_fcurve(fcu_x, pso->nextFrame); quat_next[2] = evaluate_fcurve(fcu_y, pso->nextFrame); quat_next[3] = evaluate_fcurve(fcu_z, pso->nextFrame); /* perform blending */ if (pso->mode == POSESLIDE_BREAKDOWN) { /* just perform the interpol between quat_prev and quat_next using pso->percentage as a guide */ interp_qt_qtqt(pchan->quat, quat_prev, quat_next, pso->percentage); } else if (pso->mode == POSESLIDE_PUSH) { float quat_diff[4], quat_orig[4]; /* calculate the delta transform from the previous to the current */ /* TODO: investigate ways to favour one transform more? */ sub_qt_qtqt(quat_diff, pchan->quat, quat_prev); /* make a copy of the original rotation */ copy_qt_qt(quat_orig, pchan->quat); /* increase the original by the delta transform, by an amount determined by percentage */ add_qt_qtqt(pchan->quat, quat_orig, quat_diff, pso->percentage); } else { float quat_interp[4], quat_orig[4]; int iters = (int)ceil(10.0f * pso->percentage); /* TODO: maybe a sensitivity ctrl on top of this is needed */ /* perform this blending several times until a satisfactory result is reached */ while (iters-- > 0) { /* calculate the interpolation between the endpoints */ interp_qt_qtqt(quat_interp, quat_prev, quat_next, (cframe - pso->prevFrame) / (pso->nextFrame - pso->prevFrame)); /* make a copy of the original rotation */ copy_qt_qt(quat_orig, pchan->quat); /* tricky interpolations - blending between original and new */ interp_qt_qtqt(pchan->quat, quat_orig, quat_interp, 1.0f / 6.0f); } } } /* free the path now */ MEM_freeN(path); }
/* 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); /* 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; } } }
/* 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); }