Blender V5.0
anim_path.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "MEM_guardedalloc.h"
10
11#include <cfloat>
12
13#include "DNA_curve_types.h"
14#include "DNA_key_types.h"
15#include "DNA_object_types.h"
16
17#include "BLI_math_rotation.h"
18#include "BLI_math_vector.h"
19
20#include "BKE_anim_path.h"
21#include "BKE_curve.hh"
22#include "BKE_key.hh"
23#include "BKE_object_types.hh"
24
25#include "CLG_log.h"
26
27static CLG_LogRef LOG = {"anim"};
28
29/* ******************************************************************** */
30/* Curve Paths - for curve deforms and/or curve following */
31
33{
34 if (bl->poly >= 0) {
35 /* Cyclic curve. */
36 return bl->nr;
37 }
38
39 return bl->nr - 1;
40}
41
43{
44 BLI_assert(curve_cache != nullptr);
45
46 BevList *bl = static_cast<BevList *>(curve_cache->bev.first);
47
48 BLI_assert(bl != nullptr && bl->nr > 1);
49
51}
52
53float BKE_anim_path_get_length(const CurveCache *curve_cache)
54{
55 const int seg_size = BKE_anim_path_get_array_size(curve_cache);
56 return curve_cache->anim_path_accum_length[seg_size - 1];
57}
58
60{
61 if (ob == nullptr || ob->type != OB_CURVES_LEGACY) {
62 return;
63 }
64 if (ob->runtime->curve_cache == nullptr) {
65 CLOG_WARN(&LOG, "No curve cache!");
66 return;
67 }
68
69 /* Free old data. */
70 MEM_SAFE_FREE(ob->runtime->curve_cache->anim_path_accum_length);
71
72 /* We only use the first curve. */
73 BevList *bl = static_cast<BevList *>(ob->runtime->curve_cache->bev.first);
74 /* There are no points. */
75 if (bl == nullptr) {
76 return;
77 }
78 /* There is only a single point (with no segments). */
79 if (bl->nr == 0) {
80 return;
81 }
82
83 /* There should typically be at least two points in the curve
84 * however a single point may be present here when all points are co-located.
85 * In this case either calculate a single length (for a cyclic) curve or nothing.
86 * While not useful, it's harmless too. */
87 const int seg_size = get_bevlist_seg_array_size(bl);
88 float *len_data = MEM_malloc_arrayN<float>(size_t(seg_size), "calcpathdist");
89 ob->runtime->curve_cache->anim_path_accum_length = len_data;
90
91 BevPoint *bp_arr = bl->bevpoints;
92 float prev_len = 0.0f;
93 for (int i = 0; i < bl->nr - 1; i++) {
94 prev_len += len_v3v3(bp_arr[i].vec, bp_arr[i + 1].vec);
95 len_data[i] = prev_len;
96 }
97
98 if (bl->poly >= 0) {
99 /* Cyclic curve. */
100 len_data[seg_size - 1] = prev_len + len_v3v3(bp_arr[0].vec, bp_arr[bl->nr - 1].vec);
101 }
102}
103
104static void get_curve_points_from_idx(const int idx,
105 const BevList *bl,
106 const bool is_cyclic,
107 BevPoint const **r_p0,
108 BevPoint const **r_p1,
109 BevPoint const **r_p2,
110 BevPoint const **r_p3)
111{
112 BLI_assert(idx >= 0);
113 BLI_assert(idx < bl->nr - 1 || (is_cyclic && idx < bl->nr));
114 BLI_assert(bl->nr > 1);
115
116 const BevPoint *bp_arr = bl->bevpoints;
117
118 /* First segment. */
119 if (idx == 0) {
120 *r_p1 = &bp_arr[0];
121 if (is_cyclic) {
122 *r_p0 = &bp_arr[bl->nr - 1];
123 }
124 else {
125 *r_p0 = *r_p1;
126 }
127
128 *r_p2 = &bp_arr[1];
129
130 if (bl->nr > 2) {
131 *r_p3 = &bp_arr[2];
132 }
133 else {
134 *r_p3 = *r_p2;
135 }
136 return;
137 }
138
139 /* Last segment (or next to last in a cyclic curve). */
140 if (idx == bl->nr - 2) {
141 /* The case when the bl->nr == 2 falls in to the "first segment" check above.
142 * So here we can assume that bl->nr > 2.
143 */
144 *r_p0 = &bp_arr[idx - 1];
145 *r_p1 = &bp_arr[idx];
146 *r_p2 = &bp_arr[idx + 1];
147
148 if (is_cyclic) {
149 *r_p3 = &bp_arr[0];
150 }
151 else {
152 *r_p3 = *r_p2;
153 }
154 return;
155 }
156
157 if (idx == bl->nr - 1) {
158 /* Last segment in a cyclic curve. This should only trigger if the curve is cyclic
159 * as it gets an extra segment between the end and the start point. */
160 *r_p0 = &bp_arr[idx - 1];
161 *r_p1 = &bp_arr[idx];
162 *r_p2 = &bp_arr[0];
163 *r_p3 = &bp_arr[1];
164 return;
165 }
166
167 /* To get here the curve has to have four curve points or more and idx can't
168 * be the first or the last segment.
169 * So we can assume that we can get four points without any special checks.
170 */
171 *r_p0 = &bp_arr[idx - 1];
172 *r_p1 = &bp_arr[idx];
173 *r_p2 = &bp_arr[idx + 1];
174 *r_p3 = &bp_arr[idx + 2];
175}
176
177static bool binary_search_anim_path(const float *accum_len_arr,
178 const int seg_size,
179 const float goal_len,
180 int *r_idx,
181 float *r_frac)
182{
183 float left_len, right_len;
184 int cur_idx = 0, cur_base = 0;
185 int cur_step = seg_size - 1;
186
187 /* Special case, for a single segment accessing the `right_len`
188 * would be an invalid index, see: #132976. */
189 if (UNLIKELY(seg_size == 1)) {
190 *r_idx = 0;
191 *r_frac = goal_len / accum_len_arr[0];
192 return true;
193 }
194
195 while (true) {
196 BLI_assert(cur_idx + 1 < seg_size);
197 cur_idx = cur_base + cur_step / 2;
198 left_len = accum_len_arr[cur_idx];
199 right_len = accum_len_arr[cur_idx + 1];
200
201 if (left_len <= goal_len && right_len > goal_len) {
202 *r_idx = cur_idx + 1;
203 *r_frac = (goal_len - left_len) / (right_len - left_len);
204 return true;
205 }
206 if (cur_idx == 0) {
207 /* We ended up at the first segment. The point must be in here. */
208 *r_idx = 0;
209 *r_frac = goal_len / accum_len_arr[0];
210 return true;
211 }
212
213 if (UNLIKELY(cur_step == 0)) {
214 /* This should never happen unless there is something horribly wrong. */
215 CLOG_ERROR(&LOG, "Couldn't find any valid point on the animation path!");
216 BLI_assert_msg(0, "Couldn't find any valid point on the animation path!");
217 return false;
218 }
219
220 if (left_len < goal_len) {
221 /* Go to the right. */
222 cur_base = cur_idx + 1;
223 cur_step--;
224 } /* Else, go to the left. */
225
226 cur_step /= 2;
227 }
228}
229
231 float ctime,
232 float r_vec[4],
233 float r_dir[3],
234 float r_quat[4],
235 float *r_radius,
236 float *r_weight)
237{
238 if (ob == nullptr || ob->type != OB_CURVES_LEGACY) {
239 return false;
240 }
241 Curve *cu = static_cast<Curve *>(ob->data);
242 if (ob->runtime->curve_cache == nullptr) {
243 CLOG_WARN(&LOG, "No curve cache!");
244 return false;
245 }
246 if (ob->runtime->curve_cache->anim_path_accum_length == nullptr) {
247 CLOG_WARN(&LOG, "No anim path!");
248 return false;
249 }
250 /* We only use the first curve. */
251 BevList *bl = static_cast<BevList *>(ob->runtime->curve_cache->bev.first);
252 if (bl == nullptr || !bl->nr) {
253 CLOG_WARN(&LOG, "No bev list data!");
254 return false;
255 }
256
257 /* Test for cyclic curve. */
258 const bool is_cyclic = bl->poly >= 0;
259
260 if (is_cyclic) {
261 /* Wrap the time into a 0.0 - 1.0 range. */
262 if (ctime < 0.0f || ctime > 1.0f) {
263 ctime -= floorf(ctime);
264 }
265 }
266
267 /* The curve points for this ctime value. */
268 const BevPoint *p0, *p1, *p2, *p3;
269
270 float frac;
271 const int seg_size = get_bevlist_seg_array_size(bl);
272 const float *accum_len_arr = ob->runtime->curve_cache->anim_path_accum_length;
273 const float goal_len = ctime * accum_len_arr[seg_size - 1];
274
275 /* Are we simply trying to get the start/end point? */
276 if (ctime <= 0.0f || ctime >= 1.0f) {
277 const float clamp_time = clamp_f(ctime, 0.0f, 1.0f);
278 const int idx = clamp_time * (seg_size - 1);
279 get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
280
281 if (idx == 0) {
282 frac = goal_len / accum_len_arr[0];
283 }
284 else {
285 frac = (goal_len - accum_len_arr[idx - 1]) / (accum_len_arr[idx] - accum_len_arr[idx - 1]);
286 }
287 }
288 else {
289 /* Do binary search to get the correct segment. */
290 int idx;
291 const bool found_idx = binary_search_anim_path(accum_len_arr, seg_size, goal_len, &idx, &frac);
292
293 if (UNLIKELY(!found_idx)) {
294 return false;
295 }
296 get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
297 }
298
299 /* NOTE: commented out for follow constraint
300 *
301 * If it's ever be uncommented watch out for BKE_curve_deform_coords()
302 * which used to temporary set CU_FOLLOW flag for the curve and no
303 * longer does it (because of threading issues of such a thing.
304 */
305 // if (cu->flag & CU_FOLLOW) {
306
307 float w[4];
308
310
311 if (r_dir) {
312 interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
313
314 /* Make compatible with #vec_to_quat. */
315 negate_v3(r_dir);
316 }
317 //}
318
319 const ListBase *nurbs = BKE_curve_editNurbs_get(cu);
320 if (!nurbs) {
321 nurbs = &cu->nurb;
322 }
323 const Nurb *nu = static_cast<const Nurb *>(nurbs->first);
324
325 /* Make sure that first and last frame are included in the vectors here. */
326 if (ELEM(nu->type, CU_POLY, CU_BEZIER, CU_NURBS)) {
328 }
329 else if (p2 == p3) {
331 }
332 else {
334 }
335
336 if (r_vec) {
337 /* X, Y, Z axis. */
338 r_vec[0] = w[0] * p0->vec[0] + w[1] * p1->vec[0] + w[2] * p2->vec[0] + w[3] * p3->vec[0];
339 r_vec[1] = w[0] * p0->vec[1] + w[1] * p1->vec[1] + w[2] * p2->vec[1] + w[3] * p3->vec[1];
340 r_vec[2] = w[0] * p0->vec[2] + w[1] * p1->vec[2] + w[2] * p2->vec[2] + w[3] * p3->vec[2];
341 }
342
343 /* Clamp weights to 0-1 as we don't want to extrapolate other values than position. */
344 clamp_v4(w, 0.0f, 1.0f);
345
346 if (r_vec) {
347 /* Tilt, should not be needed since we have quat still used. */
348 r_vec[3] = w[0] * p0->tilt + w[1] * p1->tilt + w[2] * p2->tilt + w[3] * p3->tilt;
349 }
350
351 if (r_quat) {
352 float totfac, q1[4], q2[4];
353
354 totfac = w[0] + w[3];
355 if (totfac > FLT_EPSILON) {
356 interp_qt_qtqt(q1, p0->quat, p3->quat, w[3] / totfac);
357 }
358 else {
359 copy_qt_qt(q1, p1->quat);
360 }
361
362 totfac = w[1] + w[2];
363 if (totfac > FLT_EPSILON) {
364 interp_qt_qtqt(q2, p1->quat, p2->quat, w[2] / totfac);
365 }
366 else {
367 copy_qt_qt(q2, p3->quat);
368 }
369
370 totfac = w[0] + w[1] + w[2] + w[3];
371 if (totfac > FLT_EPSILON) {
372 interp_qt_qtqt(r_quat, q1, q2, (w[1] + w[2]) / totfac);
373 }
374 else {
375 copy_qt_qt(r_quat, q2);
376 }
377 }
378
379 if (r_radius) {
380 *r_radius = w[0] * p0->radius + w[1] * p1->radius + w[2] * p2->radius + w[3] * p3->radius;
381 }
382
383 if (r_weight) {
384 *r_weight = w[0] * p0->weight + w[1] * p1->weight + w[2] * p2->weight + w[3] * p3->weight;
385 }
386
387 return true;
388}
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:419
void key_curve_position_weights(float t, float data[4], KeyInterpolationType type)
Definition key.cc:308
void key_curve_tangent_weights(float t, float data[4], KeyInterpolationType type)
Definition key.cc:351
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE float clamp_f(float value, float min, float max)
void interp_qt_qtqt(float q[4], const float a[4], const float b[4], float t)
void copy_qt_qt(float q[4], const float a[4])
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void clamp_v4(float vec[4], float min, float max)
MINLINE void negate_v3(float r[3])
void interp_v3_v3v3v3v3(float p[3], const float v1[3], const float v2[3], const float v3[3], const float v4[3], const float w[4])
#define UNLIKELY(x)
#define ELEM(...)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:188
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:189
@ CU_BEZIER
@ CU_POLY
@ CU_NURBS
@ KEY_LINEAR
@ KEY_CARDINAL
@ KEY_BSPLINE
Object is a sort of wrapper for general info.
@ OB_CURVES_LEGACY
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
int BKE_anim_path_get_array_size(const CurveCache *curve_cache)
Definition anim_path.cc:42
bool BKE_where_on_path(const Object *ob, float ctime, float r_vec[4], float r_dir[3], float r_quat[4], float *r_radius, float *r_weight)
Definition anim_path.cc:230
static bool binary_search_anim_path(const float *accum_len_arr, const int seg_size, const float goal_len, int *r_idx, float *r_frac)
Definition anim_path.cc:177
static void get_curve_points_from_idx(const int idx, const BevList *bl, const bool is_cyclic, BevPoint const **r_p0, BevPoint const **r_p1, BevPoint const **r_p2, BevPoint const **r_p3)
Definition anim_path.cc:104
float BKE_anim_path_get_length(const CurveCache *curve_cache)
Definition anim_path.cc:53
void BKE_anim_path_calc_data(Object *ob)
Definition anim_path.cc:59
static int get_bevlist_seg_array_size(const BevList *bl)
Definition anim_path.cc:32
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
CCL_NAMESPACE_BEGIN ccl_device_inline float frac(const float x, ccl_private int *ix)
static bool is_cyclic(const Nurb *nu)
#define LOG(level)
Definition log.h:97
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
#define floorf
BevPoint * bevpoints
float quat[4]
float vec[3]
ListBase bev
Definition BKE_curve.hh:41
const float * anim_path_accum_length
Definition BKE_curve.hh:49
ListBase nurb
void * first
short type
ObjectRuntimeHandle * runtime
i
Definition text_draw.cc:230