Coverage Report

Created: 2026-02-14 07:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/scenegraph/mpeg4_animators.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2000-2023
6
 *          All rights reserved
7
 *
8
 *  This file is part of GPAC / Scene Graph sub-project
9
 *
10
 *  GPAC is free software; you can redistribute it and/or modify
11
 *  it under the terms of the GNU Lesser General Public License as published by
12
 *  the Free Software Foundation; either version 2, or (at your option)
13
 *  any later version.
14
 *
15
 *  GPAC is distributed in the hope that it will be useful,
16
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
 *  GNU Lesser General Public License for more details.
19
 *
20
 *  You should have received a copy of the GNU Lesser General Public
21
 *  License along with this library; see the file COPYING.  If not, write to
22
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23
 *
24
 */
25
26
27
#include <gpac/internal/scenegraph_dev.h>
28
/*MPEG4 tags (for internal nodes)*/
29
#include <gpac/nodes_mpeg4.h>
30
31
32
#ifndef GPAC_DISABLE_VRML
33
34
static Fixed Interpolate(Fixed keyValue1, Fixed keyValue2, Fixed fraction)
35
0
{
36
0
  return gf_mulfix(keyValue2 - keyValue1, fraction) + keyValue1;
37
0
}
38
39
static Fixed GetInterpolateFraction(Fixed key1, Fixed key2, Fixed fraction)
40
0
{
41
0
  Fixed keyDiff = key2 - key1;
42
0
  gf_assert((fraction >= key1) && (fraction <= key2));
43
0
  if (ABS(keyDiff) < FIX_EPSILON) return 0;
44
0
  return gf_divfix(fraction - key1, keyDiff);
45
0
}
46
47
enum
48
{
49
  ANIM_LINE,
50
  ANIM_QUADRATIC,
51
  ANIM_CUBIC,
52
  ANIM_NURBS,
53
  /*NOT SUPPORTED*/
54
  ANIM_SPLINE
55
};
56
57
enum
58
{
59
  ANIM_DEFAULT,
60
  ANIM_DISCRETE,
61
  ANIM_LINEAR,
62
  /*NOT SUPPORTED ON SPLINES*/
63
  ANIM_PACED,
64
  ANIM_VELOCITY
65
};
66
67
68
/* Bisection algorithm to find u=a*t^3+b*t^2+c*t+d */
69
Fixed do_bisection(Fixed t, SFVec2f a, SFVec2f b, SFVec2f c, SFVec2f d)
70
0
{
71
0
  Fixed left, right, usearch, tsearch, limit;
72
0
  left = 0;
73
0
  right = FIX_ONE;
74
0
  limit = FIX_ONE/1000;
75
76
0
  do {
77
0
    usearch = (left+right)/2;
78
0
    tsearch = gf_mulfix(usearch, c.x + gf_mulfix(usearch, b.x + gf_mulfix(usearch, a.x))) + d.x;
79
0
    if (t < tsearch + limit) right = usearch;
80
0
    else left = usearch;
81
0
  } while ((t > tsearch + limit) || (t<tsearch - limit));
82
0
  return gf_mulfix(usearch, c.y + gf_mulfix(usearch, b.y + gf_mulfix(usearch , a.y))) + d.y;
83
0
}
84
85
86
87
typedef struct
88
{
89
  Fixed *knots, *weights, *n, *left, *right;
90
  u32 nknots, nweights, npoints;
91
  u32 p;
92
  u32 type;
93
  Bool valid;
94
} anim_nurbs;
95
96
static Fixed cubic_knots[] = {0,0,0,0,FIX_ONE,FIX_ONE,FIX_ONE,FIX_ONE};
97
static Fixed quadratic_knots[] = {0,0,0,FIX_ONE,FIX_ONE,FIX_ONE};
98
99
static void anurbs_reset(anim_nurbs *nurbs)
100
0
{
101
0
  if (nurbs->n) gf_free(nurbs->n);
102
0
  if (nurbs->left) gf_free(nurbs->left);
103
0
  if (nurbs->right) gf_free(nurbs->right);
104
0
  nurbs->n = nurbs->left = nurbs->right = NULL;
105
0
}
106
107
static void anurbs_init(anim_nurbs *nurbs, u32 type, u32 nCtrl, u32 nKnots, Fixed *knots, u32 nWeight, Fixed *weights)
108
0
{
109
0
  memset(nurbs, 0, sizeof(anim_nurbs));
110
0
  nurbs->type = type;
111
0
  switch (type) {
112
0
  case ANIM_CUBIC:
113
0
    nurbs->npoints = 4;
114
0
    nurbs->nknots = 8;
115
0
    nurbs->knots = cubic_knots;
116
0
    break;
117
0
  case ANIM_QUADRATIC:
118
0
    nurbs->npoints = 3;
119
0
    nurbs->nknots = 6;
120
0
    nurbs->knots = quadratic_knots;
121
0
    break;
122
0
  default:
123
0
    nurbs->npoints = nCtrl;
124
0
    nurbs->knots = knots;
125
0
    nurbs->nknots = nKnots;
126
0
    nurbs->weights = weights;
127
0
    nurbs->nweights = nWeight;
128
0
    break;
129
0
  }
130
0
  nurbs->p = nurbs->nknots - nurbs->npoints - 1;
131
0
  if ((nurbs->p<=0) || (nurbs->p >= nurbs->nknots -1)
132
0
          || ((nurbs->nweights>0) && (nurbs->npoints != nurbs->nweights)) ) {
133
0
    nurbs->valid = 0;
134
0
  } else {
135
0
    nurbs->valid = 1;
136
0
  }
137
0
}
138
139
static void anurbs_basis(anim_nurbs *nurbs, s32 span, Fixed t)
140
0
{
141
0
  u32 i, j;
142
0
  Fixed saved, temp;
143
0
  if (!nurbs->n) {
144
0
    nurbs->n = (Fixed*)gf_malloc(sizeof(Fixed) * (nurbs->p+1));
145
0
    nurbs->left = (Fixed*)gf_malloc(sizeof(Fixed) * (nurbs->p+1));
146
0
    nurbs->right = (Fixed*)gf_malloc(sizeof(Fixed) * (nurbs->p+1));
147
0
  }
148
0
  nurbs->n[0] = FIX_ONE;
149
150
0
  for(i=1; i<=nurbs->p; i++) {
151
0
    nurbs->left[i] = t - nurbs->knots[span+1-i];
152
0
    nurbs->right[i] = nurbs->knots[span+i]-t;
153
0
    saved = 0;
154
155
0
    for(j=0; j<i; j++) {
156
0
      temp = gf_divfix(nurbs->n[j], nurbs->right[j+1] + nurbs->left[i-j]);
157
0
      nurbs->n[j] = saved + gf_mulfix(nurbs->right[j+1], temp);
158
0
      saved = gf_mulfix(nurbs->left[i-j], temp);
159
0
    }
160
0
    nurbs->n[i]=saved;
161
0
  }
162
0
}
163
164
static s32 anurbs_find_span(anim_nurbs *nurbs, Fixed u)
165
0
{
166
#if 0
167
  s32 span;
168
  if (u == nurbs->knots[nurbs->npoints]) return nurbs->npoints - 1;
169
  for (span = (s32) nurbs->p; span < (s32) nurbs->nknots - (s32) nurbs->p; span++) {
170
    if (u<nurbs->knots[span]) break;
171
  }
172
  span--;
173
  return span;
174
#else
175
0
  s32 low, high, mid;
176
0
  if (u == nurbs->knots[nurbs->npoints]) return nurbs->npoints - 1;
177
0
  low = nurbs->p;
178
0
  high = nurbs->npoints;
179
0
  mid = (low + high)/2;
180
181
0
  while (u < nurbs->knots[mid] || u >= nurbs->knots[mid+1]) {
182
0
    if (u < nurbs->knots[mid]) high = mid;
183
0
    else low = mid;
184
0
    mid = (low + high)/2;
185
0
  }
186
0
  return (mid);
187
188
0
#endif
189
0
}
190
191
static SFVec3f anurbs_get_vec3f(anim_nurbs *nurbs, s32 span, SFVec3f *pts)
192
0
{
193
0
  SFVec3f res, tmp;
194
0
  Fixed w, wi;
195
0
  u32 i;
196
0
  tmp.x = tmp.y = tmp.z = 0;
197
0
  res = tmp;
198
0
  w=0;
199
0
  for(i=0; i<=nurbs->p; i++) {
200
0
    tmp = pts[span - nurbs->p + i];
201
0
    if (nurbs->nweights>0) {
202
0
      wi = nurbs->weights[span - nurbs->p + i];
203
0
      tmp = gf_vec_scale(tmp, wi);
204
0
      w += gf_mulfix(nurbs->n[i], wi);
205
0
    }
206
0
    res.x += gf_mulfix(nurbs->n[i], tmp.x);
207
0
    res.y += gf_mulfix(nurbs->n[i], tmp.y);
208
0
    res.z += gf_mulfix(nurbs->n[i], tmp.z);
209
0
  }
210
0
  if (nurbs->nweights>0) {
211
0
    if (w) {
212
0
      w = gf_invfix(w);
213
0
      res = gf_vec_scale(res, w);
214
0
    }
215
0
  }
216
0
  return res;
217
0
}
218
219
static SFVec2f anurbs_get_vec2f(anim_nurbs *nurbs, s32 span, SFVec2f *pts)
220
0
{
221
0
  SFVec2f res, tmp;
222
0
  Fixed w, wi;
223
0
  u32 i;
224
0
  tmp.x = tmp.y = 0;
225
0
  res = tmp;
226
0
  w=0;
227
0
  for(i=0; i<=nurbs->p; i++) {
228
0
    tmp = pts[span - nurbs->p + i];
229
0
    if (nurbs->nweights>0) {
230
0
      wi = nurbs->weights[span - nurbs->p + i];
231
0
      tmp.x = gf_mulfix(tmp.x, wi);
232
0
      tmp.y = gf_mulfix(tmp.y, wi);
233
0
      w += gf_mulfix(nurbs->n[i], wi);
234
0
    }
235
0
    res.x += gf_mulfix(nurbs->n[i], tmp.x);
236
0
    res.y += gf_mulfix(nurbs->n[i], tmp.y);
237
0
  }
238
0
  if (nurbs->nweights>0) {
239
0
    if (w) {
240
0
      w = gf_invfix(w);
241
0
      res.x = gf_mulfix(res.x, w);
242
0
      res.y = gf_mulfix(res.y, w);
243
0
    }
244
0
  }
245
0
  return res;
246
0
}
247
248
static Fixed anurbs_get_float(anim_nurbs *nurbs, s32 span, Fixed *vals)
249
0
{
250
0
  Fixed res;
251
0
  Fixed w, wi;
252
0
  u32 i;
253
0
  res = 0;
254
0
  w=0;
255
0
  for(i=0; i<=nurbs->p; i++) {
256
0
    Fixed tmp = vals[span - nurbs->p + i];
257
0
    if (nurbs->nweights>0) {
258
0
      wi = nurbs->weights[span - nurbs->p + i];
259
0
      tmp = gf_mulfix(tmp, wi);
260
0
      w += gf_mulfix(nurbs->n[i], wi);
261
0
    }
262
0
    res += gf_mulfix(nurbs->n[i], tmp);
263
0
  }
264
0
  if (nurbs->nweights>0) res = gf_divfix(res, w);
265
0
  return res;
266
0
}
267
268
typedef struct
269
{
270
  Bool is_dirty;
271
  u32 anim_type;
272
  /*for paced anim*/
273
  Fixed length;
274
  /*for spline anim*/
275
  SFVec2f a, b, c, d;
276
  /*nurbs path*/
277
  anim_nurbs anurbs;
278
} AnimatorStack;
279
280
static void Anim_Destroy(GF_Node *node, void *rs, Bool is_destroy)
281
0
{
282
0
  if (is_destroy) {
283
0
    AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node);
284
0
    anurbs_reset(&stack->anurbs);
285
0
    gf_free(stack);
286
0
  }
287
0
}
288
289
static void Animator_Update(AnimatorStack *stack, u32 keyValueType, u32 nCtrl, MFVec2f *keySpline, u32 nWeight, Fixed *weights)
290
0
{
291
0
  if (stack->anim_type==ANIM_SPLINE) {
292
0
    stack->a.x = (keySpline->vals[0].x - keySpline->vals[1].x)*3 + FIX_ONE;
293
0
    stack->a.y = (keySpline->vals[0].y - keySpline->vals[1].y)*3 + FIX_ONE;
294
0
    stack->b.x = (keySpline->vals[1].x - 2*keySpline->vals[0].x)*3;
295
0
    stack->b.y = (keySpline->vals[1].y - 2*keySpline->vals[0].y)*3;
296
0
    stack->c.x = keySpline->vals[0].x*3;
297
0
    stack->c.y = keySpline->vals[0].y*3;
298
0
    stack->d.x = stack->d.y = 0;
299
0
  }
300
0
  anurbs_reset(&stack->anurbs);
301
0
  switch (keyValueType) {
302
0
  case ANIM_CUBIC:
303
0
    anurbs_init(&stack->anurbs, ANIM_CUBIC, 0, 0, NULL, 0, NULL);
304
0
    break;
305
0
  case ANIM_QUADRATIC:
306
0
    anurbs_init(&stack->anurbs, ANIM_QUADRATIC, 0, 0, NULL, 0, NULL);
307
0
    break;
308
0
  case ANIM_NURBS:
309
0
    anurbs_init(&stack->anurbs, ANIM_NURBS, nCtrl, keySpline->count, (Fixed *) &keySpline->vals[0].x, nWeight, weights);
310
0
    break;
311
0
  }
312
0
}
313
314
315
static Bool anim_check_frac(Fixed frac, SFVec2f *fromTo)
316
0
{
317
0
  if (frac<0) return 0;
318
0
  if (frac>FIX_ONE) return 0;
319
0
  if (fromTo->x > fromTo->y) return 0;
320
  /*not active*/
321
0
  if (frac<fromTo->x) return 0;
322
0
  if (frac>fromTo->y) return 0;
323
0
  return 1;
324
0
}
325
326
static void PA_Update(M_PositionAnimator *pa, AnimatorStack *stack)
327
0
{
328
0
  u32 i;
329
0
  GF_Vec d;
330
0
  stack->is_dirty = 0;
331
0
  stack->anim_type = pa->keyType;
332
  /*if empty key and default anim switch to linear*/
333
0
  if (!pa->key.count && !stack->anim_type) stack->anim_type = ANIM_LINEAR;
334
335
0
  if (stack->anim_type == ANIM_PACED) {
336
0
    stack->length = 0;
337
0
    for (i=0; i<pa->keyValue.count-1; i++) {
338
0
      d.x = pa->keyValue.vals[i+1].x - pa->keyValue.vals[i].x;
339
0
      d.y = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y;
340
0
      d.z = pa->keyValue.vals[i+1].z - pa->keyValue.vals[i].z;
341
0
      stack->length += gf_vec_len(d);
342
0
    }
343
0
  }
344
0
  Animator_Update(stack, pa->keyValueType, pa->keyValue.count, &pa->keySpline, pa->weight.count, pa->weight.vals);
345
0
}
346
static void PA_SetFraction(GF_Node *node, GF_Route *route)
347
0
{
348
0
  Fixed frac;
349
0
  u32 nbKeys, nbVals, i;
350
0
  GF_Vec d;
351
0
  Fixed len, dlen, dist;
352
0
  M_PositionAnimator *pa = (M_PositionAnimator *)node;
353
0
  AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node);
354
355
0
  frac = pa->set_fraction;
356
0
  if (!anim_check_frac(frac, &pa->fromTo)) return;
357
358
0
  if (stack->is_dirty) PA_Update(pa, stack);
359
360
0
  nbKeys = pa->key.count;
361
0
  nbVals = pa->keyValue.count;
362
363
0
  switch (pa->keyValueType) {
364
  /*linear interpolate*/
365
0
  case ANIM_LINE:
366
    /*compute frac and segment start index*/
367
0
    switch (stack->anim_type) {
368
0
    case ANIM_DEFAULT:
369
0
      if (nbKeys != nbVals) return;
370
0
      if (frac<pa->key.vals[0]) {
371
0
        i=0;
372
0
        frac = 0;
373
0
      }
374
0
      else if (frac>pa->key.vals[nbKeys-1]) {
375
0
        i=nbVals-2;
376
0
        frac = FIX_ONE;
377
0
      }
378
0
      else {
379
0
        for (i=0; i<nbKeys-1; i++) {
380
0
          if ((frac>=pa->key.vals[i]) && (frac<pa->key.vals[i+1])) break;
381
0
        }
382
0
        frac = GetInterpolateFraction(pa->key.vals[i], pa->key.vals[i+1], frac);
383
0
      }
384
0
      break;
385
0
    case ANIM_DISCRETE:
386
0
      i = FIX2INT(gf_floor(frac*nbVals));
387
0
      frac = 0;
388
0
      break;
389
0
    case ANIM_LINEAR:
390
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
391
0
      frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1);
392
0
      break;
393
0
    case ANIM_PACED:
394
      /*at cst speed, this is the length done*/
395
0
      dist = gf_mulfix(frac, stack->length);
396
      /*then figure out in which seg we are*/
397
0
      len = 0;
398
0
      dlen = 0;
399
0
      for (i=0; i<nbVals-1; i++) {
400
0
        d.x = pa->keyValue.vals[i+1].x - pa->keyValue.vals[i].x;
401
0
        d.y = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y;
402
0
        d.z = pa->keyValue.vals[i+1].z - pa->keyValue.vals[i].z;
403
0
        dlen = gf_vec_len(d);
404
0
        if (len+dlen>dist) break;
405
0
        len += dlen;
406
0
      }
407
      /*that's our fraction inside the seg*/
408
0
      frac = gf_divfix(dist-len, dlen);
409
0
      break;
410
0
    case ANIM_SPLINE:
411
0
      frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d);
412
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
413
0
      frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1);
414
0
      break;
415
0
    default:
416
0
      return;
417
0
    }
418
    /*interpolate*/
419
0
    pa->value_changed.x = Interpolate(pa->keyValue.vals[i].x, pa->keyValue.vals[i+1].x, frac);
420
0
    pa->value_changed.y = Interpolate(pa->keyValue.vals[i].y, pa->keyValue.vals[i+1].y, frac);
421
0
    pa->value_changed.z = Interpolate(pa->keyValue.vals[i].z, pa->keyValue.vals[i+1].z, frac);
422
0
    break;
423
  /*bezier interpolate*/
424
0
  case ANIM_QUADRATIC:
425
0
  case ANIM_CUBIC:
426
0
  case ANIM_NURBS:
427
0
    if (!stack->anurbs.valid) return;
428
    /*compute frac*/
429
0
    switch (stack->anim_type) {
430
0
    case ANIM_DISCRETE:
431
0
      i = FIX2INT(gf_floor(frac*nbVals));
432
0
      frac = INT2FIX(i) / nbVals;
433
0
      break;
434
0
    case ANIM_LINEAR:
435
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
436
0
      frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1);
437
0
      break;
438
0
    case ANIM_VELOCITY:
439
0
      frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d);
440
0
      break;
441
    /*nothing to do for this one here*/
442
0
    case ANIM_DEFAULT:
443
    /*not supported - use frac as is*/
444
0
    case ANIM_PACED:
445
0
    default:
446
0
      break;
447
0
    }
448
    /*evaluate*/
449
0
    i = anurbs_find_span(&stack->anurbs, frac);
450
0
    anurbs_basis(&stack->anurbs, i, frac);
451
0
    pa->value_changed = anurbs_get_vec3f(&stack->anurbs, i, pa->keyValue.vals);
452
0
    break;
453
  /*not supported*/
454
0
  case ANIM_SPLINE:
455
0
  default:
456
0
    return;
457
0
  }
458
459
0
  pa->value_changed.x += pa->offset.x;
460
0
  pa->value_changed.y += pa->offset.y;
461
0
  pa->value_changed.z += pa->offset.z;
462
0
  gf_node_event_out(node, 12/*"value_changed"*/);
463
0
}
464
465
void PA_Modified(GF_Node *node, GF_FieldInfo *field)
466
0
{
467
0
  AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node);
468
0
  M_PositionAnimator *pa = (M_PositionAnimator *)node;
469
470
0
  if ( /*all fields impacting cached path len / nurbs*/
471
0
      (field->far_ptr == &pa->keyValue)
472
0
      || (field->far_ptr == &pa->keyValueType)
473
0
      || (field->far_ptr == &pa->key)
474
0
      || (field->far_ptr == &pa->keyType)
475
0
      || (field->far_ptr == &pa->keySpline)
476
0
      || (field->far_ptr == &pa->weight)
477
0
  )
478
0
    stack->is_dirty = 1;
479
0
}
480
void PA_Init(GF_Node *n)
481
0
{
482
0
  M_PositionAnimator *sa = (M_PositionAnimator*)n;
483
0
  AnimatorStack *stack;
484
0
  GF_SAFEALLOC(stack, AnimatorStack);
485
0
  if (!stack) {
486
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to allocate position animator stack\n"));
487
0
    return;
488
0
  }
489
0
  stack->is_dirty = 1;
490
0
  gf_node_set_private(n, stack);
491
0
  gf_node_set_callback_function(n, Anim_Destroy);
492
0
  sa->on_set_fraction = PA_SetFraction;
493
0
}
494
495
static void PA2D_Update(M_PositionAnimator2D *pa, AnimatorStack *stack)
496
0
{
497
0
  u32 i;
498
0
  Fixed dx, dy;
499
0
  stack->is_dirty = 0;
500
0
  stack->anim_type = pa->keyType;
501
  /*if empty key and default anim switch to linear*/
502
0
  if (!pa->key.count && !stack->anim_type) stack->anim_type = ANIM_LINEAR;
503
504
0
  if (stack->anim_type == ANIM_PACED) {
505
0
    stack->length = 0;
506
0
    for (i=0; i<pa->keyValue.count-1; i++) {
507
0
      dx = pa->keyValue.vals[i+1].x - pa->keyValue.vals[i].x;
508
0
      dy = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y;
509
0
      stack->length += gf_sqrt(gf_mulfix(dx, dx) + gf_mulfix(dy, dy));
510
0
    }
511
0
  }
512
0
  Animator_Update(stack, pa->keyValueType, pa->keyValue.count, &pa->keySpline, pa->weight.count, pa->weight.vals);
513
0
}
514
static void PA2D_SetFraction(GF_Node *node, GF_Route *route)
515
0
{
516
0
  Fixed frac;
517
0
  u32 nbKeys, nbVals, i;
518
0
  Fixed dx, dy;
519
0
  Fixed len, dlen, dist;
520
0
  M_PositionAnimator2D *pa = (M_PositionAnimator2D *)node;
521
0
  AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node);
522
523
0
  frac = pa->set_fraction;
524
0
  if (!anim_check_frac(frac, &pa->fromTo)) return;
525
526
0
  if (stack->is_dirty) PA2D_Update(pa, stack);
527
528
0
  nbKeys = pa->key.count;
529
0
  nbVals = pa->keyValue.count;
530
531
0
  switch (pa->keyValueType) {
532
  /*linear interpolate*/
533
0
  case ANIM_LINE:
534
    /*compute frac and segment start index*/
535
0
    switch (stack->anim_type) {
536
0
    case ANIM_DEFAULT:
537
0
      if (nbKeys != nbVals) return;
538
0
      if (frac<=pa->key.vals[0]) {
539
0
        i=0;
540
0
        frac = 0;
541
0
      }
542
0
      else if (frac>=pa->key.vals[nbKeys-1]) {
543
0
        i=nbVals-2;
544
0
        frac=FIX_ONE;
545
0
      }
546
0
      else {
547
0
        for (i=0; i<nbKeys-1; i++) {
548
0
          if ((frac>=pa->key.vals[i]) && (frac<pa->key.vals[i+1])) break;
549
0
        }
550
0
        frac = GetInterpolateFraction(pa->key.vals[i], pa->key.vals[i+1], frac);
551
0
      }
552
0
      break;
553
0
    case ANIM_DISCRETE:
554
0
      i = FIX2INT(gf_floor(frac*nbVals));
555
0
      frac = 0;
556
0
      break;
557
0
    case ANIM_LINEAR:
558
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
559
0
      frac = (frac - INT2FIX(i)/ (nbVals-1) ) * (nbVals-1);
560
0
      break;
561
0
    case ANIM_PACED:
562
      /*at cst speed, this is the length done*/
563
0
      dist = gf_mulfix(frac, stack->length);
564
      /*then figure out in which seg we are*/
565
0
      len = 0;
566
0
      dlen = 0;
567
0
      for (i=0; i<nbVals-1; i++) {
568
0
        dx = pa->keyValue.vals[i+1].x - pa->keyValue.vals[i].x;
569
0
        dy = pa->keyValue.vals[i+1].y - pa->keyValue.vals[i].y;
570
0
        dlen = gf_sqrt(gf_mulfix(dx,dx) + gf_mulfix(dy,dy));
571
0
        if (len+dlen>dist) break;
572
0
        len += dlen;
573
0
      }
574
      /*that's our fraction inside the seg*/
575
0
      frac = gf_divfix(dist-len, dlen);
576
0
      break;
577
0
    case ANIM_SPLINE:
578
0
      frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d);
579
0
      i = FIX2INT(gf_floor(frac * (nbVals-1)));
580
0
      frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1);
581
0
      break;
582
0
    default:
583
0
      return;
584
0
    }
585
    /*interpolate*/
586
0
    pa->value_changed.x = Interpolate(pa->keyValue.vals[i].x, pa->keyValue.vals[i+1].x, frac);
587
0
    pa->value_changed.y = Interpolate(pa->keyValue.vals[i].y, pa->keyValue.vals[i+1].y, frac);
588
0
    break;
589
  /*bezier interpolate*/
590
0
  case ANIM_QUADRATIC:
591
0
  case ANIM_CUBIC:
592
0
  case ANIM_NURBS:
593
0
    if (!stack->anurbs.valid) return;
594
    /*compute frac*/
595
0
    switch (stack->anim_type) {
596
0
    case ANIM_DISCRETE:
597
0
      i = FIX2INT(gf_floor(frac*nbVals));
598
0
      frac = INT2FIX(i) / nbVals;
599
0
      break;
600
0
    case ANIM_LINEAR:
601
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
602
0
      frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1);
603
0
      break;
604
0
    case ANIM_VELOCITY:
605
0
      frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d);
606
0
      break;
607
    /*nothing to do for this one here*/
608
0
    case ANIM_DEFAULT:
609
    /*not supported - use frac as is*/
610
0
    case ANIM_PACED:
611
0
    default:
612
0
      break;
613
0
    }
614
    /*evaluate*/
615
0
    i = anurbs_find_span(&stack->anurbs, frac);
616
0
    anurbs_basis(&stack->anurbs, i, frac);
617
0
    pa->value_changed = anurbs_get_vec2f(&stack->anurbs, i, pa->keyValue.vals);
618
0
    break;
619
  /*not supported*/
620
0
  case ANIM_SPLINE:
621
0
  default:
622
0
    return;
623
0
  }
624
625
0
  pa->value_changed.x += pa->offset.x;
626
0
  pa->value_changed.y += pa->offset.y;
627
0
  gf_node_event_out(node, 12/*"value_changed"*/);
628
0
}
629
630
void PA2D_Modified(GF_Node *node, GF_FieldInfo *field)
631
0
{
632
0
  AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node);
633
0
  M_PositionAnimator2D *pa = (M_PositionAnimator2D *)node;
634
635
0
  if ( /*all fields impacting cached path len / nurbs*/
636
0
      (field->far_ptr == &pa->keyValue)
637
0
      || (field->far_ptr == &pa->keyValueType)
638
0
      || (field->far_ptr == &pa->key)
639
0
      || (field->far_ptr == &pa->keyType)
640
0
      || (field->far_ptr == &pa->keySpline)
641
0
      || (field->far_ptr == &pa->weight)
642
0
  )
643
0
    stack->is_dirty = 1;
644
0
}
645
void PA2D_Init(GF_Node *n)
646
0
{
647
0
  M_PositionAnimator2D *sa = (M_PositionAnimator2D *)n;
648
0
  AnimatorStack *stack;
649
0
  GF_SAFEALLOC(stack, AnimatorStack);
650
0
  if (!stack) {
651
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to allocate position animator 2D stack\n"));
652
0
    return;
653
0
  }
654
0
  stack->is_dirty = 1;
655
0
  gf_node_set_private(n, stack);
656
0
  gf_node_set_callback_function(n, Anim_Destroy);
657
0
  sa->on_set_fraction = PA2D_SetFraction;
658
0
}
659
660
static void SA_Update(M_ScalarAnimator *sa, AnimatorStack *stack)
661
0
{
662
0
  u32 i;
663
0
  Fixed len;
664
0
  stack->is_dirty = 0;
665
0
  stack->anim_type = sa->keyType;
666
  /*if empty key and default anim switch to linear*/
667
0
  if (!sa->key.count && !stack->anim_type) stack->anim_type = ANIM_LINEAR;
668
669
0
  if (stack->anim_type == ANIM_PACED) {
670
0
    stack->length = 0;
671
0
    for (i=0; i<sa->keyValue.count-1; i++) {
672
0
      len = sa->keyValue.vals[i+1] - sa->keyValue.vals[i];
673
0
      stack->length += ABS(len);
674
0
    }
675
0
  }
676
0
  Animator_Update(stack, sa->keyValueType, sa->keyValue.count, &sa->keySpline, sa->weight.count, sa->weight.vals);
677
0
}
678
679
void SA_SetFraction(GF_Node *node, GF_Route *route)
680
0
{
681
0
  Fixed frac;
682
0
  u32 nbKeys, nbVals, i;
683
0
  Fixed len, dlen, dist;
684
0
  M_ScalarAnimator *sa = (M_ScalarAnimator *)node;
685
0
  AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node);
686
687
0
  frac = sa->set_fraction;
688
0
  if (!anim_check_frac(frac, &sa->fromTo)) return;
689
690
0
  if (stack->is_dirty) SA_Update(sa, stack);
691
692
0
  nbKeys = sa->key.count;
693
0
  nbVals = sa->keyValue.count;
694
695
0
  i = 0;
696
0
  switch (sa->keyValueType) {
697
  /*linear interpolate*/
698
0
  case ANIM_LINE:
699
    /*compute frac & segment start index*/
700
0
    switch (stack->anim_type) {
701
0
    case ANIM_DEFAULT:
702
0
      if (nbKeys != nbVals) return;
703
0
      if (frac<sa->key.vals[0]) {
704
0
        i=0;
705
0
        frac = 0;
706
0
      }
707
0
      else if (frac>sa->key.vals[nbKeys-1]) {
708
0
        i=nbVals-2;
709
0
        frac=FIX_ONE;
710
0
      }
711
0
      else {
712
0
        for (i=0; i<nbKeys-1; i++) {
713
0
          if ((frac>=sa->key.vals[i]) && (frac<sa->key.vals[i+1])) break;
714
0
        }
715
0
        frac = GetInterpolateFraction(sa->key.vals[i], sa->key.vals[i+1], frac);
716
0
      }
717
0
      break;
718
0
    case ANIM_DISCRETE:
719
0
      i = FIX2INT(gf_floor(frac*nbVals));
720
0
      frac = 0;
721
0
      break;
722
0
    case ANIM_LINEAR:
723
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
724
0
      frac = (frac - INT2FIX(i)/ (nbVals-1) ) * (nbVals-1);
725
0
      break;
726
0
    case ANIM_PACED:
727
      /*at cst speed, this is the length done*/
728
0
      dist = gf_mulfix(frac, stack->length);
729
      /*then figure out in which seg we are*/
730
0
      len = 0;
731
0
      dlen = 0;
732
0
      for (i=0; i<nbVals-1; i++) {
733
0
        dlen = sa->keyValue.vals[i+1] - sa->keyValue.vals[i];
734
0
        if (dlen<0) dlen *= -1;
735
0
        if (len+dlen>dist) break;
736
0
        len += dlen;
737
0
      }
738
      /*that's our fraction inside the seg*/
739
0
      frac = gf_divfix(dist-len, dlen);
740
0
      break;
741
0
    case ANIM_SPLINE:
742
0
      frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d);
743
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
744
0
      frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1);
745
0
      break;
746
0
    }
747
    /*interpolate*/
748
0
    sa->value_changed = Interpolate(sa->keyValue.vals[i], sa->keyValue.vals[i+1], frac);
749
0
    break;
750
  /*bezier interpolate*/
751
0
  case ANIM_QUADRATIC:
752
0
  case ANIM_CUBIC:
753
0
  case ANIM_NURBS:
754
0
    if (!stack->anurbs.valid) return;
755
    /*compute frac*/
756
0
    switch (stack->anim_type) {
757
0
    case ANIM_DISCRETE:
758
0
      i = FIX2INT(gf_floor(frac*nbVals));
759
0
      frac = INT2FIX(i) / nbVals;
760
0
      break;
761
0
    case ANIM_LINEAR:
762
0
      i = FIX2INT(gf_floor(frac*(nbVals-1)));
763
0
      frac = (frac - INT2FIX(i) / (nbVals-1) ) * (nbVals-1);
764
0
      break;
765
0
    case ANIM_VELOCITY:
766
0
      frac = do_bisection(frac, stack->a, stack->b, stack->c, stack->d);
767
0
      break;
768
    /*nothing to do for this one here*/
769
0
    case ANIM_DEFAULT:
770
    /*not supported - use frac as is*/
771
0
    case ANIM_PACED:
772
0
    default:
773
0
      break;
774
0
    }
775
    /*evaluate nurbs*/
776
0
    i = anurbs_find_span(&stack->anurbs, frac);
777
0
    anurbs_basis(&stack->anurbs, i, frac);
778
0
    sa->value_changed = anurbs_get_float(&stack->anurbs, i, sa->keyValue.vals);
779
0
    break;
780
  /*not supported*/
781
0
  case ANIM_SPLINE:
782
0
  default:
783
0
    return;
784
0
  }
785
786
0
  sa->value_changed += sa->offset;
787
0
  gf_node_event_out(node, 10/*"value_changed"*/);
788
0
}
789
790
void SA_Modified(GF_Node *node, GF_FieldInfo *field)
791
0
{
792
0
  AnimatorStack *stack = (AnimatorStack *)gf_node_get_private(node);
793
0
  M_ScalarAnimator *sa = (M_ScalarAnimator *)node;
794
795
0
  if ( /*all fields impacting cached path len / nurbs*/
796
0
      (field->far_ptr == &sa->keyValue)
797
0
      || (field->far_ptr == &sa->keyValueType)
798
0
      || (field->far_ptr == &sa->key)
799
0
      || (field->far_ptr == &sa->keyType)
800
0
      || (field->far_ptr == &sa->keySpline)
801
0
      || (field->far_ptr == &sa->weight)
802
0
  )
803
0
    stack->is_dirty = 1;
804
0
}
805
806
void SA_Init(GF_Node *n)
807
0
{
808
0
  M_ScalarAnimator *sa = (M_ScalarAnimator *)n;
809
0
  AnimatorStack *stack;
810
0
  GF_SAFEALLOC(stack, AnimatorStack);
811
0
  if (!stack) {
812
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_SCENE, ("[VRML] Failed to allocate scalar animator stack\n"));
813
0
    return;
814
0
  }
815
0
  stack->is_dirty = 1;
816
0
  gf_node_set_private(n, stack);
817
0
  gf_node_set_callback_function(n, Anim_Destroy);
818
0
  sa->on_set_fraction = SA_SetFraction;
819
0
}
820
821
822
#endif  /*GPAC_DISABLE_VRML*/