Skip to content

Commit

Permalink
animated: add time_offset parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
ubitux committed Jan 29, 2024
1 parent 1fc02b5 commit e0d21f1
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 5 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ Versioning](https://semver.org/spec/v2.0.0.html) for `libnopegl`.
- `forward_transforms` parameter to the `Texture` and `RenderToTexture` nodes
to enable forwarding of the camera/model transformations
- `Selector` filter to select colors according to their lightness, chroma or hue
- `AnimKeyFrame*` nodes can now be reused at a different time using the
`Animated*.time_offset` parameter

### Fixed
- Moving the split position in `ngl-diff`
Expand Down
56 changes: 56 additions & 0 deletions libnopegl/nodes.specs
Original file line number Diff line number Diff line change
Expand Up @@ -1051,6 +1051,13 @@
"choices": "colorspace",
"flags": [],
"desc": "color space defining how to interpret `value`"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand All @@ -1070,6 +1077,13 @@
"node_types": ["Path", "SmoothPath"],
"flags": ["nonull"],
"desc": "path to follow"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand All @@ -1082,6 +1096,13 @@
"node_types": ["AnimKeyFrameFloat"],
"flags": [],
"desc": "time key frames to interpolate from"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand All @@ -1094,6 +1115,13 @@
"node_types": ["AnimKeyFrameFloat"],
"flags": [],
"desc": "float key frames to interpolate from"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand All @@ -1106,6 +1134,13 @@
"node_types": ["AnimKeyFrameVec2"],
"flags": [],
"desc": "vec2 key frames to interpolate from"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand All @@ -1118,6 +1153,13 @@
"node_types": ["AnimKeyFrameVec3"],
"flags": [],
"desc": "vec3 key frames to interpolate from"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand All @@ -1130,6 +1172,13 @@
"node_types": ["AnimKeyFrameVec4"],
"flags": [],
"desc": "vec4 key frames to interpolate from"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand All @@ -1149,6 +1198,13 @@
"default": 0,
"flags": [],
"desc": "exposed as a 4x4 rotation matrix in the program"
},
{
"name": "time_offset",
"type": "f64",
"default": 0.000000,
"flags": [],
"desc": "apply a time offset before evaluating the animation"
}
]
},
Expand Down
2 changes: 2 additions & 0 deletions libnopegl/src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,8 @@ struct variable_opts {
int as_mat4; /* UniformQuat and AnimatedQuat only */
int space; /* UniformColor and AnimatedColor only */
};

double time_offset;
};

struct variable_info {
Expand Down
23 changes: 20 additions & 3 deletions libnopegl/src/node_animated.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,34 +36,44 @@ static const struct node_param animatedtime_params[] = {
{"keyframes", NGLI_PARAM_TYPE_NODELIST, OFFSET(animkf), .flags=NGLI_PARAM_FLAG_DOT_DISPLAY_PACKED,
.node_types=(const uint32_t[]){NGL_NODE_ANIMKEYFRAMEFLOAT, NGLI_NODE_NONE},
.desc=NGLI_DOCSTRING("time key frames to interpolate from")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

static const struct node_param animatedfloat_params[] = {
{"keyframes", NGLI_PARAM_TYPE_NODELIST, OFFSET(animkf), .flags=NGLI_PARAM_FLAG_DOT_DISPLAY_PACKED,
.node_types=(const uint32_t[]){NGL_NODE_ANIMKEYFRAMEFLOAT, NGLI_NODE_NONE},
.desc=NGLI_DOCSTRING("float key frames to interpolate from")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

static const struct node_param animatedvec2_params[] = {
{"keyframes", NGLI_PARAM_TYPE_NODELIST, OFFSET(animkf), .flags=NGLI_PARAM_FLAG_DOT_DISPLAY_PACKED,
.node_types=(const uint32_t[]){NGL_NODE_ANIMKEYFRAMEVEC2, NGLI_NODE_NONE},
.desc=NGLI_DOCSTRING("vec2 key frames to interpolate from")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

static const struct node_param animatedvec3_params[] = {
{"keyframes", NGLI_PARAM_TYPE_NODELIST, OFFSET(animkf), .flags=NGLI_PARAM_FLAG_DOT_DISPLAY_PACKED,
.node_types=(const uint32_t[]){NGL_NODE_ANIMKEYFRAMEVEC3, NGLI_NODE_NONE},
.desc=NGLI_DOCSTRING("vec3 key frames to interpolate from")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

static const struct node_param animatedvec4_params[] = {
{"keyframes", NGLI_PARAM_TYPE_NODELIST, OFFSET(animkf), .flags=NGLI_PARAM_FLAG_DOT_DISPLAY_PACKED,
.node_types=(const uint32_t[]){NGL_NODE_ANIMKEYFRAMEVEC4, NGLI_NODE_NONE},
.desc=NGLI_DOCSTRING("vec4 key frames to interpolate from")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

Expand All @@ -73,6 +83,8 @@ static const struct node_param animatedquat_params[] = {
.desc=NGLI_DOCSTRING("quaternion key frames to interpolate from")},
{"as_mat4", NGLI_PARAM_TYPE_BOOL, OFFSET(as_mat4), {.i32=0},
.desc=NGLI_DOCSTRING("exposed as a 4x4 rotation matrix in the program")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

Expand All @@ -84,6 +96,8 @@ static const struct node_param animatedpath_params[] = {
.node_types=(const uint32_t[]){NGL_NODE_PATH, NGL_NODE_SMOOTHPATH, NGLI_NODE_NONE},
.flags=NGLI_PARAM_FLAG_NON_NULL,
.desc=NGLI_DOCSTRING("path to follow")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

Expand All @@ -94,6 +108,8 @@ static const struct node_param animatedcolor_params[] = {
{"space", NGLI_PARAM_TYPE_SELECT, OFFSET(space), {.i32=NGLI_COLORCONV_SPACE_SRGB},
.choices=&ngli_colorconv_colorspace_choices,
.desc=NGLI_DOCSTRING("color space defining how to interpret `value`")},
{"time_offset", NGLI_PARAM_TYPE_F64, OFFSET(time_offset),
.desc=NGLI_DOCSTRING("apply a time offset before evaluating the animation")},
{NULL}
};

Expand Down Expand Up @@ -318,7 +334,7 @@ int ngl_anim_evaluate(struct ngl_node *node, void *dst, double t)
}
}

return ngli_animation_evaluate(&s->anim_eval, dst, t);
return ngli_animation_evaluate(&s->anim_eval, dst, t - o->time_offset);
}

static int animation_init(struct ngl_node *node)
Expand Down Expand Up @@ -404,7 +420,8 @@ static int animatedpath_init(struct ngl_node *node)
static int animation_update(struct ngl_node *node, double t)
{
struct animated_priv *s = node->priv_data;
return ngli_animation_evaluate(&s->anim, s->var.data, t);
const struct variable_opts *o = node->opts;
return ngli_animation_evaluate(&s->anim, s->var.data, t - o->time_offset);
}

#define animatedtime_update animation_update
Expand All @@ -419,7 +436,7 @@ static int animatedquat_update(struct ngl_node *node, double t)
{
struct animated_priv *s = node->priv_data;
const struct variable_opts *o = node->opts;
int ret = ngli_animation_evaluate(&s->anim, s->vector, t);
int ret = ngli_animation_evaluate(&s->anim, s->vector, t - o->time_offset);
if (ret < 0)
return ret;
if (o->as_mat4)
Expand Down
6 changes: 4 additions & 2 deletions libnopegl/src/node_velocity.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ int ngli_velocity_evaluate(struct ngl_node *node, void *dst, double t)
}
}

return ngli_animation_derivate(&s->anim_eval, dst, t);
return ngli_animation_derivate(&s->anim_eval, dst, t - anim->time_offset);
}

static int velocity_init(struct ngl_node *node)
Expand All @@ -184,7 +184,9 @@ static int velocity_init(struct ngl_node *node)
static int velocity_update(struct ngl_node *node, double t)
{
struct velocity_priv *s = node->priv_data;
return ngli_animation_derivate(&s->anim, s->var.data, t);
const struct velocity_opts *o = node->opts;
const struct variable_opts *anim = o->anim_node->opts;
return ngli_animation_derivate(&s->anim, s->var.data, t - anim->time_offset);
}

#define DEFINE_VELOCITY_CLASS(class_id, class_name, type, dtype, count) \
Expand Down
1 change: 1 addition & 0 deletions tests/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,7 @@ foreach backend : backends
'rotate_quat_animated',
'path',
'smoothpath',
'shared_anim',
]

tests_userlive = [
Expand Down
15 changes: 15 additions & 0 deletions tests/refs/transform_shared_anim.ref
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
7710F590A080A0805550F510A080A080 7710F590A080A0805550F510A080A080 7710F590A080A0805550F510A080A080 00000000000000000000000000000000
04D526D43780A2825554F580A080A080 04D526D43780A2825554F580A080A080 04D526D43780A2825554F580A080A080 00000000000000000000000000000000
017509B51CA0A0A05555F5B0A080A080 017509B51CA0A0A05555F5B0A080A080 017509B51CA0A0A05555F5B0A080A080 00000000000000000000000000000000
013509B51CA0A0A05555F5B0A080A080 013509B51CA0A0A05555F5B0A080A080 013509B51CA0A0A05555F5B0A080A080 00000000000000000000000000000000
033509B51CA0A0A05555F5B0A080A080 033509B51CA0A0A05555F5B0A080A080 033509B51CA0A0A05555F5B0A080A080 00000000000000000000000000000000
033509B51CA0A0A05555F5B0A080A080 033509B51CA0A0A05555F5B0A080A080 033509B51CA0A0A05555F5B0A080A080 00000000000000000000000000000000
033509B508E002A0045526D522802282 033509B508E002A0045526D522802282 033509B508E002A0045526D522802282 00000000000000000000000000000000
033D08BD08E00228011509F509A008A0 033D08BD08E00228011509F509A008A0 033D08BD08E00228011509F509A008A0 00000000000000000000000000000000
033509B508A008A0015509B508A008A0 033509B508A008A0015509B508A008A0 033509B508A008A0015509B508A008A0 00000000000000000000000000000000
05D42ED62283228001550BB508A008A0 05D42ED62283228001550BB508A008A0 05D42ED62283228001550BB508A008A0 00000000000000000000000000000000
7710F590A085A0A01555A1B508A008A0 7710F590A085A0A01555A1B508A008A0 7710F590A085A0A01555A1B508A008A0 00000000000000000000000000000000
7710F590A085A0A01555A3B508A008A0 7710F590A085A0A01555A3B508A008A0 7710F590A085A0A01555A3B508A008A0 00000000000000000000000000000000
7710F590A085A0A81555A33D08E002A8 7710F590A085A0A81555A33D08E002A8 7710F590A085A0A81555A33D08E002A8 00000000000000000000000000000000
7710F590A085A0A01555A1B508A008A0 7710F590A085A0A01555A1B508A008A0 7710F590A085A0A01555A1B508A008A0 00000000000000000000000000000000
7710F590A0D4A0801554A3D62E822282 7710F590A0D4A0801554A3D62E822282 7710F590A0D4A0801554A3D62E822282 00000000000000000000000000000000
24 changes: 24 additions & 0 deletions tests/transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -353,3 +353,27 @@ def transform_smoothpath(cfg: ngl.SceneCfg):
]

return ngl.Translate(shape, vector=ngl.AnimatedPath(anim_kf, path))


@test_fingerprint(keyframes=15, tolerance=1)
@ngl.scene()
def transform_shared_anim(cfg: ngl.SceneCfg):
cfg.aspect_ratio = (1, 1)
cfg.duration = 6

# Duplicate the same shape at different positions
shape = ngl.RenderColor(geometry=ngl.Circle(radius=1 / 3, npoints=5))
shape0 = ngl.Translate(shape, vector=(-0.5, 0.5, 0))
shape1 = ngl.Translate(shape, vector=(-0.5, -0.5, 0))

# Same animation, reused at different times
anim_d = cfg.duration * 2 / 3
anim_kf = [
ngl.AnimKeyFrameVec3(0, (0, 0, 0)),
ngl.AnimKeyFrameVec3(anim_d / 2, (1, 0, 0), "exp_out"),
ngl.AnimKeyFrameVec3(anim_d, (0, 0, 0), "back_in"),
]
anim0 = ngl.Translate(shape0, vector=ngl.AnimatedVec3(anim_kf))
anim1 = ngl.Translate(shape1, vector=ngl.AnimatedVec3(anim_kf, time_offset=cfg.duration * 1 / 3))

return ngl.Group(children=[anim0, anim1])

0 comments on commit e0d21f1

Please sign in to comment.