Blender V4.3
evaluation.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Developers
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
5#include "ANIM_evaluation.hh"
6
7#include "RNA_access.hh"
8
9#include "BKE_animsys.h"
10#include "BKE_fcurve.hh"
11
12#include "BLI_map.hh"
13
15
16namespace blender::animrig {
17
18using namespace internal;
19
24EvaluationResult blend_layer_results(const EvaluationResult &last_result,
25 const EvaluationResult &current_result,
26 const Layer &current_layer);
27
34void apply_evaluation_result(const EvaluationResult &evaluation_result,
35 PointerRNA &animated_id_ptr,
36 bool flush_to_original);
37
39 Action &action,
40 const slot_handle_t slot_handle,
41 const AnimationEvalContext &anim_eval_context)
42{
43 EvaluationResult last_result;
44
45 /* Evaluate each layer in order. */
46 for (Layer *layer : action.layers()) {
47 if (layer->influence <= 0.0f) {
48 /* Don't bother evaluating layers without influence. */
49 continue;
50 }
51
52 auto layer_result = evaluate_layer(
53 animated_id_ptr, action, *layer, slot_handle, anim_eval_context);
54 if (!layer_result) {
55 continue;
56 }
57
58 if (!last_result) {
59 /* Simple case: no results so far, so just use this layer as-is. There is
60 * nothing to blend/combine with, so ignore the influence and combination
61 * options. */
62 last_result = layer_result;
63 continue;
64 }
65
66 /* Complex case: blend this layer's result into the previous layer's result. */
67 last_result = blend_layer_results(last_result, layer_result, *layer);
68 }
69
70 return last_result;
71}
72
74 Action &action,
75 const slot_handle_t slot_handle,
76 const AnimationEvalContext &anim_eval_context,
77 const bool flush_to_original)
78{
79 EvaluationResult evaluation_result = evaluate_action(
80 animated_id_ptr, action, slot_handle, anim_eval_context);
81 if (!evaluation_result) {
82 return;
83 }
84
85 apply_evaluation_result(evaluation_result, animated_id_ptr, flush_to_original);
86}
87
88/* Copy of the same-named function in anim_sys.cc, with the check on action groups removed. */
89static bool is_fcurve_evaluatable(const FCurve *fcu)
90{
91 if (fcu->flag & (FCURVE_MUTED | FCURVE_DISABLED)) {
92 return false;
93 }
94 if (BKE_fcurve_is_empty(fcu)) {
95 return false;
96 }
97 return true;
98}
99
100/* Copy of the same-named function in anim_sys.cc, but with the special handling for NLA strips
101 * removed. */
103{
104 *ptr_orig = *ptr;
105 /* Original note from anim_sys.cc:
106 * -----------
107 * NOTE: nlastrip_evaluate_controls() creates PointerRNA with ID of nullptr. Technically, this is
108 * not a valid pointer, but there are exceptions in various places of this file which handles
109 * such pointers.
110 * We do special trickery here as well, to quickly go from evaluated to original NlaStrip.
111 * -----------
112 * And this is all not ported to the new layered animation system. */
113 BLI_assert_msg(ptr->owner_id, "NLA support was not ported to the layered animation system");
114 ptr_orig->owner_id = ptr_orig->owner_id->orig_id;
115 ptr_orig->data = ptr_orig->owner_id;
116}
117
118/* Copy of the same-named function in anim_sys.cc. */
120 const char *rna_path,
121 const int array_index,
122 const float value)
123{
124 PointerRNA ptr_orig;
126
127 PathResolvedRNA orig_anim_rna;
128 /* TODO(sergey): Should be possible to cache resolved path in dependency graph somehow. */
129 if (BKE_animsys_rna_path_resolve(&ptr_orig, rna_path, array_index, &orig_anim_rna)) {
130 BKE_animsys_write_to_rna_path(&orig_anim_rna, value);
131 }
132}
133
135 StripKeyframeData &strip_data,
136 const slot_handle_t slot_handle,
137 const AnimationEvalContext &offset_eval_context)
138{
139 ChannelBag *channelbag_for_slot = strip_data.channelbag_for_slot(slot_handle);
140 if (!channelbag_for_slot) {
141 return {};
142 }
143
144 EvaluationResult evaluation_result;
145 for (FCurve *fcu : channelbag_for_slot->fcurves()) {
146 /* Blatant copy of animsys_evaluate_fcurves(). */
147
148 if (!is_fcurve_evaluatable(fcu)) {
149 continue;
150 }
151
152 PathResolvedRNA anim_rna;
154 &animated_id_ptr, fcu->rna_path, fcu->array_index, &anim_rna))
155 {
156 printf("Cannot resolve RNA path %s[%d] on ID %s\n",
157 fcu->rna_path,
158 fcu->array_index,
159 animated_id_ptr.owner_id->name);
160 continue;
161 }
162
163 const float curval = calculate_fcurve(&anim_rna, fcu, &offset_eval_context);
164 evaluation_result.store(fcu->rna_path, fcu->array_index, curval, anim_rna);
165 }
166
167 return evaluation_result;
168}
169
170void apply_evaluation_result(const EvaluationResult &evaluation_result,
171 PointerRNA &animated_id_ptr,
172 const bool flush_to_original)
173{
174 for (auto channel_result : evaluation_result.items()) {
175 const PropIdentifier &prop_ident = channel_result.key;
176 const AnimatedProperty &anim_prop = channel_result.value;
177 const float animated_value = anim_prop.value;
178 PathResolvedRNA anim_rna = anim_prop.prop_rna;
179
180 BKE_animsys_write_to_rna_path(&anim_rna, animated_value);
181
182 if (flush_to_original) {
183 /* Convert the StringRef to a `const char *`, as the rest of the RNA path handling code in
184 * BKE still uses `char *` instead of `StringRef`. */
185 animsys_write_orig_anim_rna(&animated_id_ptr,
186 StringRefNull(prop_ident.rna_path).c_str(),
187 prop_ident.array_index,
188 animated_value);
189 }
190 }
191}
192
194 Action &owning_action,
195 Strip &strip,
196 const slot_handle_t slot_handle,
197 const AnimationEvalContext &anim_eval_context)
198{
199 AnimationEvalContext offset_eval_context = anim_eval_context;
200 /* Positive offset means the entire strip is pushed "to the right", so
201 * evaluation needs to happen further "to the left". */
202 offset_eval_context.eval_time -= strip.frame_offset;
203
204 switch (strip.type()) {
205 case Strip::Type::Keyframe: {
206 StripKeyframeData &strip_data = strip.data<StripKeyframeData>(owning_action);
207 return evaluate_keyframe_data(animated_id_ptr, strip_data, slot_handle, offset_eval_context);
208 }
209 }
210
211 return {};
212}
213
215 const EvaluationResult &current_result,
216 const Layer &current_layer)
217{
218 /* TODO?: store the layer results sequentially, so that we can step through
219 * them in parallel, instead of iterating over one and doing map lookups on
220 * the other. */
221
222 EvaluationResult blend = last_result;
223
224 for (auto channel_result : current_result.items()) {
225 const PropIdentifier &prop_ident = channel_result.key;
226 AnimatedProperty *last_prop = blend.lookup_ptr(prop_ident);
227 const AnimatedProperty &anim_prop = channel_result.value;
228
229 if (!last_prop) {
230 /* Nothing to blend with, so just take (influence * value). */
231 blend.store(prop_ident.rna_path,
232 prop_ident.array_index,
233 anim_prop.value * current_layer.influence,
234 anim_prop.prop_rna);
235 continue;
236 }
237
238 /* TODO: move this to a separate function. And write more smartness for rotations. */
239 switch (current_layer.mix_mode()) {
241 last_prop->value = anim_prop.value * current_layer.influence;
242 break;
244 last_prop->value = math::interpolate(
245 current_layer.influence, last_prop->value, anim_prop.value);
246 break;
248 last_prop->value += anim_prop.value * current_layer.influence;
249 break;
251 last_prop->value -= anim_prop.value * current_layer.influence;
252 break;
254 last_prop->value *= anim_prop.value * current_layer.influence;
255 break;
256 };
257 }
258
259 return blend;
260}
261
262namespace internal {
263
265 Action &owning_action,
266 Layer &layer,
267 const slot_handle_t slot_handle,
268 const AnimationEvalContext &anim_eval_context)
269{
270 /* TODO: implement cross-blending between overlapping strips. For now, this is not supported.
271 * Instead, the first strong result is taken (see below), and if that is not available, the last
272 * weak result will be used.
273 *
274 * Weak result: obtained from evaluating the final frame of the strip.
275 * Strong result: any result that is not a weak result. */
276 EvaluationResult last_weak_result;
277
278 for (Strip *strip : layer.strips()) {
279 if (!strip->contains_frame(anim_eval_context.eval_time)) {
280 continue;
281 }
282
283 const EvaluationResult strip_result = evaluate_strip(
284 animated_id_ptr, owning_action, *strip, slot_handle, anim_eval_context);
285 if (!strip_result) {
286 continue;
287 }
288
289 const bool is_weak_result = strip->is_last_frame(anim_eval_context.eval_time);
290 if (is_weak_result) {
291 /* Keep going until a strong result is found. */
292 last_weak_result = strip_result;
293 continue;
294 }
295
296 /* Found a strong result, just return it. */
297 return strip_result;
298 }
299
300 return last_weak_result;
301}
302
303} // namespace internal
304
305} // namespace blender::animrig
Layered Action evaluation.
bool BKE_animsys_rna_path_resolve(struct PointerRNA *ptr, const char *rna_path, int array_index, struct PathResolvedRNA *r_result)
Definition anim_sys.cc:350
bool BKE_animsys_write_to_rna_path(struct PathResolvedRNA *anim_rna, float value)
Definition anim_sys.cc:460
bool BKE_fcurve_is_empty(const FCurve *fcu)
float calculate_fcurve(PathResolvedRNA *anim_rna, FCurve *fcu, const AnimationEvalContext *anim_eval_context)
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
@ FCURVE_DISABLED
@ FCURVE_MUTED
constexpr const char * c_str() const
blender::Span< const Layer * > layers() const
blender::Span< const FCurve * > fcurves() const
void store(const StringRefNull rna_path, const int array_index, const float value, const PathResolvedRNA &prop_rna)
EvaluationMap::ItemIterator items() const
MixMode mix_mode() const
const ChannelBag * channelbag_for_slot(const Slot &slot) const
const T & data(const Action &owning_action) const
#define printf
EvaluationResult evaluate_layer(PointerRNA &animated_id_ptr, Action &owning_action, Layer &layer, const slot_handle_t slot_handle, const AnimationEvalContext &anim_eval_context)
static void animsys_write_orig_anim_rna(PointerRNA *ptr, const char *rna_path, const int array_index, const float value)
void evaluate_and_apply_action(PointerRNA &animated_id_ptr, Action &action, slot_handle_t slot_handle, const AnimationEvalContext &anim_eval_context, bool flush_to_original)
Definition evaluation.cc:73
static bool is_fcurve_evaluatable(const FCurve *fcu)
Definition evaluation.cc:89
EvaluationResult evaluate_action(PointerRNA &animated_id_ptr, Action &action, slot_handle_t slot_handle, const AnimationEvalContext &anim_eval_context)
Definition evaluation.cc:38
static void animsys_construct_orig_pointer_rna(const PointerRNA *ptr, PointerRNA *ptr_orig)
EvaluationResult blend_layer_results(const EvaluationResult &last_result, const EvaluationResult &current_result, const Layer &current_layer)
void apply_evaluation_result(const EvaluationResult &evaluation_result, PointerRNA &animated_id_ptr, bool flush_to_original)
static EvaluationResult evaluate_keyframe_data(PointerRNA &animated_id_ptr, StripKeyframeData &strip_data, const slot_handle_t slot_handle, const AnimationEvalContext &offset_eval_context)
decltype(::ActionSlot::handle) slot_handle_t
static EvaluationResult evaluate_strip(PointerRNA &animated_id_ptr, Action &owning_action, Strip &strip, const slot_handle_t slot_handle, const AnimationEvalContext &anim_eval_context)
T interpolate(const T &a, const T &b, const FactorT &t)
struct ID * orig_id
Definition DNA_ID.h:466
char name[66]
Definition DNA_ID.h:425
ID * owner_id
Definition RNA_types.hh:40
void * data
Definition RNA_types.hh:42
PointerRNA * ptr
Definition wm_files.cc:4126