Refactor: draw fcurve bezt function #108748

Merged
Christoph Lendenfeld merged 4 commits from ChrisLend/blender:graph_editor_draw_refactor into main 2023-06-15 18:20:22 +02:00
1 changed files with 115 additions and 99 deletions

View File

@ -819,7 +819,65 @@ static bool fcurve_can_use_simple_bezt_drawing(FCurve *fcu)
return true;
}
/* helper func - draw one repeat of an F-Curve (using Bezier curve approximations) */
static int calculate_bezt_draw_resolution(BezTriple *bezt,
BezTriple *prevbezt,
const int max_bez_resolution,
const bool is_driver)
{
if (is_driver) {
return max_bez_resolution;
}
const int resolution = (int)(5.0f * len_v2v2(bezt->vec[1], prevbezt->vec[1]));
/* NOTE: higher values will crash */
/* TODO: view scale should factor into this someday too... */
return min_ii(resolution, max_bez_resolution);
}
/** Draw a segment from \param prevbezt to \param bezt at the given \param resolution.

This could return min_ii(resolution, max_bez_resolution), making it possible for resolution to be const.

This could `return min_ii(resolution, max_bez_resolution)`, making it possible for `resolution` to be `const`.
* immBeginAtMost is expected to be called with enough space for this function to run.
*/
static void draw_bezt(BezTriple *bezt, BezTriple *prevbezt, int resolution, uint pos)
{
float prev_key[2], prev_handle[2], bez_handle[2], bez_key[2];
float data[120];
if (resolution < 2) {
prev_key[0] = prevbezt->vec[1][0];
prev_key[1] = prevbezt->vec[1][1];
immVertex2fv(pos, prev_key);
return;
}
prev_key[0] = prevbezt->vec[1][0];
prev_key[1] = prevbezt->vec[1][1];
prev_handle[0] = prevbezt->vec[2][0];
prev_handle[1] = prevbezt->vec[2][1];
bez_handle[0] = bezt->vec[0][0];
bez_handle[1] = bezt->vec[0][1];
bez_key[0] = bezt->vec[1][0];
bez_key[1] = bezt->vec[1][1];
BKE_fcurve_correct_bezpart(prev_key, prev_handle, bez_handle, bez_key);
BKE_curve_forward_diff_bezier(
prev_key[0], prev_handle[0], bez_handle[0], bez_key[0], data, resolution, sizeof(float[3]));
BKE_curve_forward_diff_bezier(prev_key[1],
prev_handle[1],
bez_handle[1],
bez_key[1],
data + 1,
resolution,
sizeof(float[3]));
for (float *fp = data; resolution; resolution--, fp += 3) {
immVertex2fv(pos, fp);
}
}
/* Helper function - draw one repeat of an F-Curve (using Bezier curve approximations). */
static void draw_fcurve_curve_bezts(
bAnimContext *ac, ID *id, FCurve *fcu, View2D *v2d, uint pos, const bool draw_extrapolation)
{
@ -827,178 +885,136 @@ static void draw_fcurve_curve_bezts(
return;
}
BezTriple *prevbezt = fcu->bezt;
BezTriple *bezt = prevbezt + 1;
float v1[2], v2[2], v3[2], v4[2];
float *fp, data[120];
float fac = 0.0f;
int b = fcu->totvert - 1;
int resol;
float unit_scale, offset;
short mapping_flag = ANIM_get_normalization_flags(ac);
/* apply unit mapping */
/* Apply unit mapping. */
GPU_matrix_push();
unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
float offset;
short mapping_flag = ANIM_get_normalization_flags(ac);
const float unit_scale = ANIM_unit_mapping_get_factor(ac->scene, id, fcu, mapping_flag, &offset);
GPU_matrix_scale_2f(1.0f, unit_scale);
GPU_matrix_translate_2f(0.0f, offset);
/* For now, this assumes the worst case scenario, where all the keyframes have
* bezier interpolation, and are drawn at full res.
* This is tricky to optimize, but maybe can be improved at some point... */
immBeginAtMost(GPU_PRIM_LINE_STRIP, (b * 32 + 3));
int b = fcu->totvert - 1;
const int max_bezt_resolution = 32;
immBeginAtMost(GPU_PRIM_LINE_STRIP, (b * max_bezt_resolution + 3));
/* extrapolate to left? */
BezTriple *prevbezt = fcu->bezt;
BezTriple *bezt = prevbezt + 1;
float vertex_position[2];
float fac = 0.0f;
/* Extrapolate to the left? */
if (draw_extrapolation && prevbezt->vec[1][0] > v2d->cur.xmin) {
/* left-side of view comes before first keyframe, so need to extend as not cyclic */
v1[0] = v2d->cur.xmin;
vertex_position[0] = v2d->cur.xmin;
/* y-value depends on the interpolation */
if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (prevbezt->ipo == BEZT_IPO_CONST) ||
(prevbezt->ipo == BEZT_IPO_LIN && fcu->totvert == 1))
{
/* just extend across the first keyframe's value */
v1[1] = prevbezt->vec[1][1];
vertex_position[1] = prevbezt->vec[1][1];
}
else if (prevbezt->ipo == BEZT_IPO_LIN) {
/* extrapolate linear doesn't use the handle, use the next points center instead */
fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - vertex_position[0]);
if (fac) {
fac = 1.0f / fac;
}
v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]);
vertex_position[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]);
}
else {
/* based on angle of handle 1 (relative to keyframe) */
fac = (prevbezt->vec[0][0] - prevbezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
fac = (prevbezt->vec[0][0] - prevbezt->vec[1][0]) /
(prevbezt->vec[1][0] - vertex_position[0]);
if (fac) {
fac = 1.0f / fac;
}
v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[0][1] - prevbezt->vec[1][1]);
vertex_position[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[0][1] - prevbezt->vec[1][1]);
}
immVertex2fv(pos, v1);
immVertex2fv(pos, vertex_position);
}
/* if only one keyframe, add it now */
/* If only one keyframe, add it now. */
if (fcu->totvert == 1) {
v1[0] = prevbezt->vec[1][0];
v1[1] = prevbezt->vec[1][1];
immVertex2fv(pos, v1);
vertex_position[0] = prevbezt->vec[1][0];
vertex_position[1] = prevbezt->vec[1][1];
immVertex2fv(pos, vertex_position);
}
/* draw curve between first and last keyframe (if there are enough to do so) */
/* Draw curve between first and last keyframe (if there are enough to do so). */
/* TODO: optimize this to not have to calc stuff out of view too? */
while (b--) {
if (prevbezt->ipo == BEZT_IPO_CONST) {
/* Constant-Interpolation: draw segment between previous keyframe and next,
* but holding same value */
v1[0] = prevbezt->vec[1][0];
v1[1] = prevbezt->vec[1][1];
immVertex2fv(pos, v1);
vertex_position[0] = prevbezt->vec[1][0];
vertex_position[1] = prevbezt->vec[1][1];
immVertex2fv(pos, vertex_position);
v1[0] = bezt->vec[1][0];
v1[1] = prevbezt->vec[1][1];
immVertex2fv(pos, v1);
vertex_position[0] = bezt->vec[1][0];
vertex_position[1] = prevbezt->vec[1][1];
immVertex2fv(pos, vertex_position);
}
else if (prevbezt->ipo == BEZT_IPO_LIN) {
/* Linear interpolation: just add one point (which should add a new line segment) */
v1[0] = prevbezt->vec[1][0];
v1[1] = prevbezt->vec[1][1];
immVertex2fv(pos, v1);
vertex_position[0] = prevbezt->vec[1][0];
vertex_position[1] = prevbezt->vec[1][1];
immVertex2fv(pos, vertex_position);
}
else if (prevbezt->ipo == BEZT_IPO_BEZ) {
/* Bezier-Interpolation: draw curve as series of segments between keyframes
* - resol determines number of points to sample in between keyframes
*/
/* resol depends on distance between points
* (not just horizontal) OR is a fixed high res */
/* TODO: view scale should factor into this someday too... */
if (fcu->driver) {
resol = 32;
}
else {
resol = (int)(5.0f * len_v2v2(bezt->vec[1], prevbezt->vec[1]));
}
if (resol < 2) {
/* only draw one */
v1[0] = prevbezt->vec[1][0];
v1[1] = prevbezt->vec[1][1];
immVertex2fv(pos, v1);
}
else {
/* clamp resolution to max of 32 */
/* NOTE: higher values will crash */
if (resol > 32) {
resol = 32;
}
v1[0] = prevbezt->vec[1][0];
v1[1] = prevbezt->vec[1][1];
v2[0] = prevbezt->vec[2][0];
v2[1] = prevbezt->vec[2][1];
v3[0] = bezt->vec[0][0];
v3[1] = bezt->vec[0][1];
v4[0] = bezt->vec[1][0];
v4[1] = bezt->vec[1][1];
BKE_fcurve_correct_bezpart(v1, v2, v3, v4);
BKE_curve_forward_diff_bezier(v1[0], v2[0], v3[0], v4[0], data, resol, sizeof(float[3]));
BKE_curve_forward_diff_bezier(
v1[1], v2[1], v3[1], v4[1], data + 1, resol, sizeof(float[3]));
for (fp = data; resol; resol--, fp += 3) {
immVertex2fv(pos, fp);
}
}
int resolution = calculate_bezt_draw_resolution(
bezt, prevbezt, max_bezt_resolution, fcu->driver != NULL);
draw_bezt(bezt, prevbezt, resolution, pos);
}
/* get next pointers */
/* Get next pointers. */
prevbezt = bezt;
bezt++;
/* last point? */
/* Last point? */
if (b == 0) {
v1[0] = prevbezt->vec[1][0];
v1[1] = prevbezt->vec[1][1];
immVertex2fv(pos, v1);
vertex_position[0] = prevbezt->vec[1][0];
vertex_position[1] = prevbezt->vec[1][1];
immVertex2fv(pos, vertex_position);
}
}
/* extrapolate to right? (see code for left-extrapolation above too) */
/* Extrapolate to the right? (see code for left-extrapolation above too) */
if (draw_extrapolation && prevbezt->vec[1][0] < v2d->cur.xmax) {
v1[0] = v2d->cur.xmax;
vertex_position[0] = v2d->cur.xmax;
/* y-value depends on the interpolation */
/* y-value depends on the interpolation. */
if ((fcu->extend == FCURVE_EXTRAPOLATE_CONSTANT) || (fcu->flag & FCURVE_INT_VALUES) ||
(prevbezt->ipo == BEZT_IPO_CONST) || (prevbezt->ipo == BEZT_IPO_LIN && fcu->totvert == 1))
{
/* based on last keyframe's value */
v1[1] = prevbezt->vec[1][1];
vertex_position[1] = prevbezt->vec[1][1];
}
else if (prevbezt->ipo == BEZT_IPO_LIN) {
/* extrapolate linear doesn't use the handle, use the previous points center instead */
/* Extrapolate linear doesn't use the handle, use the previous points center instead. */
bezt = prevbezt - 1;
fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
fac = (prevbezt->vec[1][0] - bezt->vec[1][0]) / (prevbezt->vec[1][0] - vertex_position[0]);
if (fac) {
fac = 1.0f / fac;
}
v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]);
vertex_position[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[1][1] - bezt->vec[1][1]);
}
else {
/* based on angle of handle 1 (relative to keyframe) */
fac = (prevbezt->vec[2][0] - prevbezt->vec[1][0]) / (prevbezt->vec[1][0] - v1[0]);
/* Based on angle of handle 1 (relative to keyframe). */
fac = (prevbezt->vec[2][0] - prevbezt->vec[1][0]) /
(prevbezt->vec[1][0] - vertex_position[0]);
if (fac) {
fac = 1.0f / fac;
}
v1[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[2][1] - prevbezt->vec[1][1]);
vertex_position[1] = prevbezt->vec[1][1] - fac * (prevbezt->vec[2][1] - prevbezt->vec[1][1]);
}
immVertex2fv(pos, v1);
immVertex2fv(pos, vertex_position);
}
immEnd();