Blender V4.3
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
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 = {"bke.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 = (float *)MEM_mallocN(sizeof(float) * 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 while (true) {
188 cur_idx = cur_base + cur_step / 2;
189 left_len = accum_len_arr[cur_idx];
190 right_len = accum_len_arr[cur_idx + 1];
191
192 if (left_len <= goal_len && right_len > goal_len) {
193 *r_idx = cur_idx + 1;
194 *r_frac = (goal_len - left_len) / (right_len - left_len);
195 return true;
196 }
197 if (cur_idx == 0) {
198 /* We ended up at the first segment. The point must be in here. */
199 *r_idx = 0;
200 *r_frac = goal_len / accum_len_arr[0];
201 return true;
202 }
203
204 if (UNLIKELY(cur_step == 0)) {
205 /* This should never happen unless there is something horribly wrong. */
206 CLOG_ERROR(&LOG, "Couldn't find any valid point on the animation path!");
207 BLI_assert_msg(0, "Couldn't find any valid point on the animation path!");
208 return false;
209 }
210
211 if (left_len < goal_len) {
212 /* Go to the right. */
213 cur_base = cur_idx + 1;
214 cur_step--;
215 } /* Else, go to the left. */
216
217 cur_step /= 2;
218 }
219}
220
222 float ctime,
223 float r_vec[4],
224 float r_dir[3],
225 float r_quat[4],
226 float *r_radius,
227 float *r_weight)
228{
229 if (ob == nullptr || ob->type != OB_CURVES_LEGACY) {
230 return false;
231 }
232 Curve *cu = static_cast<Curve *>(ob->data);
233 if (ob->runtime->curve_cache == nullptr) {
234 CLOG_WARN(&LOG, "No curve cache!");
235 return false;
236 }
237 if (ob->runtime->curve_cache->anim_path_accum_length == nullptr) {
238 CLOG_WARN(&LOG, "No anim path!");
239 return false;
240 }
241 /* We only use the first curve. */
242 BevList *bl = static_cast<BevList *>(ob->runtime->curve_cache->bev.first);
243 if (bl == nullptr || !bl->nr) {
244 CLOG_WARN(&LOG, "No bev list data!");
245 return false;
246 }
247
248 /* Test for cyclic curve. */
249 const bool is_cyclic = bl->poly >= 0;
250
251 if (is_cyclic) {
252 /* Wrap the time into a 0.0 - 1.0 range. */
253 if (ctime < 0.0f || ctime > 1.0f) {
254 ctime -= floorf(ctime);
255 }
256 }
257
258 /* The curve points for this ctime value. */
259 const BevPoint *p0, *p1, *p2, *p3;
260
261 float frac;
262 const int seg_size = get_bevlist_seg_array_size(bl);
263 const float *accum_len_arr = ob->runtime->curve_cache->anim_path_accum_length;
264 const float goal_len = ctime * accum_len_arr[seg_size - 1];
265
266 /* Are we simply trying to get the start/end point? */
267 if (ctime <= 0.0f || ctime >= 1.0f) {
268 const float clamp_time = clamp_f(ctime, 0.0f, 1.0f);
269 const int idx = clamp_time * (seg_size - 1);
270 get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
271
272 if (idx == 0) {
273 frac = goal_len / accum_len_arr[0];
274 }
275 else {
276 frac = (goal_len - accum_len_arr[idx - 1]) / (accum_len_arr[idx] - accum_len_arr[idx - 1]);
277 }
278 }
279 else {
280 /* Do binary search to get the correct segment. */
281 int idx;
282 const bool found_idx = binary_search_anim_path(accum_len_arr, seg_size, goal_len, &idx, &frac);
283
284 if (UNLIKELY(!found_idx)) {
285 return false;
286 }
287 get_curve_points_from_idx(idx, bl, is_cyclic, &p0, &p1, &p2, &p3);
288 }
289
290 /* NOTE: commented out for follow constraint
291 *
292 * If it's ever be uncommented watch out for BKE_curve_deform_coords()
293 * which used to temporary set CU_FOLLOW flag for the curve and no
294 * longer does it (because of threading issues of such a thing.
295 */
296 // if (cu->flag & CU_FOLLOW) {
297
298 float w[4];
299
301
302 if (r_dir) {
303 interp_v3_v3v3v3v3(r_dir, p0->vec, p1->vec, p2->vec, p3->vec, w);
304
305 /* Make compatible with #vec_to_quat. */
306 negate_v3(r_dir);
307 }
308 //}
309
310 const ListBase *nurbs = BKE_curve_editNurbs_get(cu);
311 if (!nurbs) {
312 nurbs = &cu->nurb;
313 }
314 const Nurb *nu = static_cast<const Nurb *>(nurbs->first);
315
316 /* Make sure that first and last frame are included in the vectors here. */
317 if (ELEM(nu->type, CU_POLY, CU_BEZIER, CU_NURBS)) {
319 }
320 else if (p2 == p3) {
322 }
323 else {
325 }
326
327 if (r_vec) {
328 /* X, Y, Z axis. */
329 r_vec[0] = w[0] * p0->vec[0] + w[1] * p1->vec[0] + w[2] * p2->vec[0] + w[3] * p3->vec[0];
330 r_vec[1] = w[0] * p0->vec[1] + w[1] * p1->vec[1] + w[2] * p2->vec[1] + w[3] * p3->vec[1];
331 r_vec[2] = w[0] * p0->vec[2] + w[1] * p1->vec[2] + w[2] * p2->vec[2] + w[3] * p3->vec[2];
332 }
333
334 /* Clamp weights to 0-1 as we don't want to extrapolate other values than position. */
335 clamp_v4(w, 0.0f, 1.0f);
336
337 if (r_vec) {
338 /* Tilt, should not be needed since we have quat still used. */
339 r_vec[3] = w[0] * p0->tilt + w[1] * p1->tilt + w[2] * p2->tilt + w[3] * p3->tilt;
340 }
341
342 if (r_quat) {
343 float totfac, q1[4], q2[4];
344
345 totfac = w[0] + w[3];
346 if (totfac > FLT_EPSILON) {
347 interp_qt_qtqt(q1, p0->quat, p3->quat, w[3] / totfac);
348 }
349 else {
350 copy_qt_qt(q1, p1->quat);
351 }
352
353 totfac = w[1] + w[2];
354 if (totfac > FLT_EPSILON) {
355 interp_qt_qtqt(q2, p1->quat, p2->quat, w[2] / totfac);
356 }
357 else {
358 copy_qt_qt(q2, p3->quat);
359 }
360
361 totfac = w[0] + w[1] + w[2] + w[3];
362 if (totfac > FLT_EPSILON) {
363 interp_qt_qtqt(r_quat, q1, q2, (w[1] + w[2]) / totfac);
364 }
365 else {
366 copy_qt_qt(r_quat, q2);
367 }
368 }
369
370 if (r_radius) {
371 *r_radius = w[0] * p0->radius + w[1] * p1->radius + w[2] * p2->radius + w[3] * p3->radius;
372 }
373
374 if (r_weight) {
375 *r_weight = w[0] * p0->weight + w[1] * p1->weight + w[2] * p2->weight + w[3] * p3->weight;
376 }
377
378 return true;
379}
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:398
void key_curve_tangent_weights(float t, float data[4], int type)
Definition key.cc:380
void key_curve_position_weights(float t, float data[4], int type)
Definition key.cc:339
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
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:182
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
@ 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:221
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
static CLG_LogRef LOG
Definition anim_path.cc:27
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(float x, ccl_private int *ix)
#define floorf(x)
static bool is_cyclic(const Nurb *nu)
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
BevPoint * bevpoints
float quat[4]
float vec[3]
ListBase bev
Definition BKE_curve.hh:38
const float * anim_path_accum_length
Definition BKE_curve.hh:46
ListBase nurb
void * first
short type
ObjectRuntimeHandle * runtime