Panda3D
cLerpNodePathInterval.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file cLerpNodePathInterval.cxx
10  * @author drose
11  * @date 2002-08-27
12  */
13 
14 #include "cLerpNodePathInterval.h"
15 #include "lerp_helpers.h"
16 #include "transformState.h"
17 #include "renderState.h"
18 #include "colorAttrib.h"
19 #include "colorScaleAttrib.h"
20 #include "texMatrixAttrib.h"
21 #include "dcast.h"
22 #include "config_interval.h"
23 
24 TypeHandle CLerpNodePathInterval::_type_handle;
25 
26 /**
27  * Constructs a lerp interval that will lerp some properties on the indicated
28  * node, possibly relative to the indicated other node (if other is nonempty).
29  *
30  * You must call set_end_pos(), etc. for the various properties you wish to
31  * lerp before the first call to priv_initialize(). If you want to set a
32  * starting value for any of the properties, you may call set_start_pos(),
33  * etc.; otherwise, the starting value is taken from the actual node's value
34  * at the time the lerp is performed.
35  *
36  * The starting values may be explicitly specified or omitted. The value of
37  * bake_in_start determines the behavior if the starting values are omitted.
38  * If bake_in_start is true, the values are obtained the first time the lerp
39  * runs, and thenceforth are stored within the interval. If bake_in_start is
40  * false, the starting value is computed each frame, based on assuming the
41  * current value represents the value set from the last time the interval was
42  * run. This "smart" behavior allows code to manipulate the object event
43  * while it is being lerped, and the lerp continues to apply in a sensible
44  * way.
45  *
46  * If fluid is true, the prev_transform is not adjusted by the lerp;
47  * otherwise, it is reset.
48  */
50 CLerpNodePathInterval(const std::string &name, double duration,
51  CLerpInterval::BlendType blend_type,
52  bool bake_in_start, bool fluid,
53  const NodePath &node, const NodePath &other) :
54  CLerpInterval(name, duration, blend_type),
55  _node(node),
56  _other(other),
57  _flags(0),
58  _texture_stage(TextureStage::get_default()),
59  _override(0),
60  _slerp(nullptr)
61 {
62  if (bake_in_start) {
63  _flags |= F_bake_in_start;
64  }
65  if (fluid) {
66  _flags |= F_fluid;
67  }
68  _prev_d = 0.0;
69 }
70 
71 /**
72  * This replaces the first call to priv_step(), and indicates that the
73  * interval has just begun. This may be overridden by derived classes that
74  * need to do some explicit initialization on the first call.
75  */
77 priv_initialize(double t) {
78  check_stopped(get_class_type(), "priv_initialize");
79  recompute();
80  _prev_d = 0.0;
81  _state = S_started;
82  priv_step(t);
83 }
84 
85 /**
86  * This is called in lieu of priv_initialize() .. priv_step() ..
87  * priv_finalize(), when everything is to happen within one frame. The
88  * interval should initialize itself, then leave itself in the final state.
89  */
91 priv_instant() {
92  check_stopped(get_class_type(), "priv_instant");
93  recompute();
94  _prev_d = 0.0;
95  _state = S_started;
97  _state = S_final;
98 }
99 
100 /**
101  * Advances the time on the interval. The time may either increase (the
102  * normal case) or decrease (e.g. if the interval is being played by a
103  * slider).
104  */
106 priv_step(double t) {
107  check_started(get_class_type(), "priv_step");
108  _state = S_started;
109  double d = compute_delta(t);
110 
111  // Save this in case we want to restore it later.
112  CPT(TransformState) prev_transform = _node.get_prev_transform();
113 
114  if ((_flags & (F_end_pos | F_end_hpr | F_end_quat | F_end_scale | F_end_shear)) != 0) {
115  // We have some transform lerp.
116  CPT(TransformState) transform;
117 
118  if (_other.is_empty()) {
119  // If there is no other node, it's a local transform lerp.
120  transform = _node.get_transform();
121  } else {
122  // If there *is* another node, we get the transform relative to that
123  // node.
124  transform = _node.get_transform(_other);
125  }
126 
127  LPoint3 pos;
128  LVecBase3 hpr;
129  LQuaternion quat;
130  LVecBase3 scale;
131  LVecBase3 shear;
132 
133  if ((_flags & F_end_pos) != 0) {
134  if ((_flags & F_start_pos) != 0) {
135  lerp_value(pos, d, _start_pos, _end_pos);
136 
137  } else if ((_flags & F_bake_in_start) != 0) {
138  // Get the current starting pos, and bake it in.
139  set_start_pos(transform->get_pos());
140  lerp_value(pos, d, _start_pos, _end_pos);
141 
142  } else {
143  // "smart" lerp from the current pos to the new pos.
144  pos = transform->get_pos();
145  lerp_value_from_prev(pos, d, _prev_d, pos, _end_pos);
146  }
147  }
148  if ((_flags & F_end_hpr) != 0) {
149  if ((_flags & F_start_hpr) != 0) {
150  lerp_value(hpr, d, _start_hpr, _end_hpr);
151 
152  } else if ((_flags & F_start_quat) != 0) {
153  _start_hpr = _start_quat.get_hpr();
154  _flags |= F_start_hpr;
155  lerp_value(hpr, d, _start_hpr, _end_hpr);
156 
157  } else if ((_flags & F_bake_in_start) != 0) {
158  set_start_hpr(transform->get_hpr());
159  lerp_value(hpr, d, _start_hpr, _end_hpr);
160 
161  } else {
162  hpr = transform->get_hpr();
163  lerp_value_from_prev(hpr, d, _prev_d, hpr, _end_hpr);
164  }
165  }
166  if ((_flags & F_end_quat) != 0) {
167  if ((_flags & F_slerp_setup) == 0) {
168  if ((_flags & F_start_quat) != 0) {
169  setup_slerp();
170 
171  } else if ((_flags & F_start_hpr) != 0) {
172  _start_quat.set_hpr(_start_hpr);
173  _flags |= F_start_quat;
174  setup_slerp();
175 
176  } else if ((_flags & F_bake_in_start) != 0) {
177  set_start_quat(transform->get_norm_quat());
178  setup_slerp();
179 
180  } else {
181  if (_prev_d == 1.0) {
182  _start_quat = _end_quat;
183  } else {
184  LQuaternion prev_value = transform->get_norm_quat();
185  _start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d);
186  }
187  setup_slerp();
188 
189  // In this case, clear the slerp_setup flag because we need to re-
190  // setup the slerp each time.
191  _flags &= ~F_slerp_setup;
192  }
193  }
194  nassertv(_slerp != nullptr);
195  (this->*_slerp)(quat, d);
196  }
197  if ((_flags & F_end_scale) != 0) {
198  if ((_flags & F_start_scale) != 0) {
199  lerp_value(scale, d, _start_scale, _end_scale);
200 
201  } else if ((_flags & F_bake_in_start) != 0) {
202  set_start_scale(transform->get_scale());
203  lerp_value(scale, d, _start_scale, _end_scale);
204 
205  } else {
206  scale = transform->get_scale();
207  lerp_value_from_prev(scale, d, _prev_d, scale, _end_scale);
208  }
209  }
210  if ((_flags & F_end_shear) != 0) {
211  if ((_flags & F_start_shear) != 0) {
212  lerp_value(shear, d, _start_shear, _end_shear);
213 
214  } else if ((_flags & F_bake_in_start) != 0) {
215  set_start_shear(transform->get_shear());
216  lerp_value(shear, d, _start_shear, _end_shear);
217 
218  } else {
219  shear = transform->get_shear();
220  lerp_value_from_prev(shear, d, _prev_d, shear, _end_shear);
221  }
222  }
223 
224  // Now apply the modifications back to the transform. We want to be a
225  // little careful here, because we don't want to assume the transform has
226  // hprscale components if they're not needed. And in any case, we only
227  // want to apply the components that we computed, above.
228  unsigned int transform_flags = _flags & (F_end_pos | F_end_hpr | F_end_quat | F_end_scale);
229  switch (transform_flags) {
230  case 0:
231  break;
232 
233  case F_end_pos:
234  if (_other.is_empty()) {
235  _node.set_pos(pos);
236  } else {
237  _node.set_pos(_other, pos);
238  }
239  break;
240 
241  case F_end_hpr:
242  if (_other.is_empty()) {
243  _node.set_hpr(hpr);
244  } else {
245  _node.set_hpr(_other, hpr);
246  }
247  break;
248 
249  case F_end_quat:
250  if (_other.is_empty()) {
251  _node.set_quat(quat);
252  } else {
253  _node.set_quat(_other, quat);
254  }
255  break;
256 
257  case F_end_scale:
258  if (_other.is_empty()) {
259  _node.set_scale(scale);
260  } else {
261  _node.set_scale(_other, scale);
262  }
263  break;
264 
265  case F_end_hpr | F_end_scale:
266  if (_other.is_empty()) {
267  _node.set_hpr_scale(hpr, scale);
268  } else {
269  _node.set_hpr_scale(hpr, scale);
270  }
271  break;
272 
273  case F_end_quat | F_end_scale:
274  if (_other.is_empty()) {
275  _node.set_quat_scale(quat, scale);
276  } else {
277  _node.set_quat_scale(quat, scale);
278  }
279  break;
280 
281  case F_end_pos | F_end_hpr:
282  if (_other.is_empty()) {
283  _node.set_pos_hpr(pos, hpr);
284  } else {
285  _node.set_pos_hpr(_other, pos, hpr);
286  }
287  break;
288 
289  case F_end_pos | F_end_quat:
290  if (_other.is_empty()) {
291  _node.set_pos_quat(pos, quat);
292  } else {
293  _node.set_pos_quat(_other, pos, quat);
294  }
295  break;
296 
297  case F_end_pos | F_end_scale:
298  if (transform->quat_given()) {
299  if (_other.is_empty()) {
300  _node.set_pos_quat_scale(pos, transform->get_quat(), scale);
301  } else {
302  _node.set_pos_quat_scale(_other, pos, transform->get_quat(), scale);
303  }
304  } else {
305  if (_other.is_empty()) {
306  _node.set_pos_hpr_scale(pos, transform->get_hpr(), scale);
307  } else {
308  _node.set_pos_hpr_scale(_other, pos, transform->get_hpr(), scale);
309  }
310  }
311  break;
312 
313  case F_end_pos | F_end_hpr | F_end_scale:
314  if ((_flags & F_end_shear) != 0) {
315  // Even better: we have all four components.
316  if (_other.is_empty()) {
317  _node.set_pos_hpr_scale_shear(pos, hpr, scale, shear);
318  } else {
319  _node.set_pos_hpr_scale_shear(_other, pos, hpr, scale, shear);
320  }
321  } else {
322  // We have only the primary three components.
323  if (_other.is_empty()) {
324  _node.set_pos_hpr_scale(pos, hpr, scale);
325  } else {
326  _node.set_pos_hpr_scale(_other, pos, hpr, scale);
327  }
328  }
329  break;
330 
331  case F_end_pos | F_end_quat | F_end_scale:
332  if ((_flags & F_end_shear) != 0) {
333  // Even better: we have all four components.
334  if (_other.is_empty()) {
335  _node.set_pos_quat_scale_shear(pos, quat, scale, shear);
336  } else {
337  _node.set_pos_quat_scale_shear(_other, pos, quat, scale, shear);
338  }
339  } else {
340  // We have only the primary three components.
341  if (_other.is_empty()) {
342  _node.set_pos_quat_scale(pos, quat, scale);
343  } else {
344  _node.set_pos_quat_scale(_other, pos, quat, scale);
345  }
346  }
347  break;
348 
349  default:
350  // Some unhandled combination. We should handle this.
351  interval_cat.error()
352  << "Internal error in CLerpNodePathInterval::priv_step().\n";
353  }
354  if ((_flags & F_end_shear) != 0) {
355  // Also apply changes to shear.
356  if (transform_flags == (F_end_pos | F_end_hpr | F_end_scale) ||
357  transform_flags == (F_end_pos | F_end_quat | F_end_scale)) {
358  // Actually, we already handled this case above.
359 
360  } else {
361  if (_other.is_empty()) {
362  _node.set_shear(shear);
363  } else {
364  _node.set_shear(_other, shear);
365  }
366  }
367  }
368  }
369 
370  if ((_flags & F_fluid) != 0) {
371  // If we have the fluid flag set, we shouldn't mess with the prev
372  // transform. Therefore, restore it to what it was before we started
373  // messing with it.
374  _node.set_prev_transform(prev_transform);
375  }
376 
377  if ((_flags & (F_end_color | F_end_color_scale | F_end_tex_offset | F_end_tex_rotate | F_end_tex_scale)) != 0) {
378  // We have some render state lerp.
379  CPT(RenderState) state;
380 
381  if (_other.is_empty()) {
382  // If there is no other node, it's a local state lerp. This is most
383  // common.
384  state = _node.get_state();
385  } else {
386  // If there *is* another node, we get the state relative to that node.
387  // This is weird, but you could lerp color (for instance) relative to
388  // some other node's color.
389  state = _node.get_state(_other);
390  }
391 
392  // Unlike in the transform case above, we can go ahead and modify the
393  // state immediately with each attribute change, since these attributes
394  // don't interrelate.
395 
396  if ((_flags & F_end_color) != 0) {
397  LColor color;
398 
399  if ((_flags & F_start_color) != 0) {
400  lerp_value(color, d, _start_color, _end_color);
401 
402  } else {
403  // Get the previous color.
404  color.set(1.0f, 1.0f, 1.0f, 1.0f);
405  const RenderAttrib *attrib =
406  state->get_attrib(ColorAttrib::get_class_type());
407  if (attrib != nullptr) {
408  const ColorAttrib *ca = DCAST(ColorAttrib, attrib);
409  if (ca->get_color_type() == ColorAttrib::T_flat) {
410  color = ca->get_color();
411  }
412  }
413 
414  lerp_value_from_prev(color, d, _prev_d, color, _end_color);
415  }
416 
417  state = state->add_attrib(ColorAttrib::make_flat(color), _override);
418  }
419 
420  if ((_flags & F_end_color_scale) != 0) {
421  LVecBase4 color_scale;
422 
423  if ((_flags & F_start_color_scale) != 0) {
424  lerp_value(color_scale, d, _start_color_scale, _end_color_scale);
425 
426  } else {
427  // Get the previous color scale.
428  color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
429  const RenderAttrib *attrib =
430  state->get_attrib(ColorScaleAttrib::get_class_type());
431  if (attrib != nullptr) {
432  const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
433  color_scale = csa->get_scale();
434  }
435 
436  lerp_value_from_prev(color_scale, d, _prev_d, color_scale, _end_color_scale);
437  }
438 
439  state = state->add_attrib(ColorScaleAttrib::make(color_scale), _override);
440  }
441 
442  if ((_flags & (F_end_tex_offset | F_end_tex_rotate | F_end_tex_scale)) != 0) {
443  // We have a UV lerp.
444  CPT(TransformState) transform = TransformState::make_identity();
445 
446  const RenderAttrib *attrib =
447  state->get_attrib(TexMatrixAttrib::get_class_type());
448  CPT(TexMatrixAttrib) tma;
449  if (attrib != nullptr) {
450  tma = DCAST(TexMatrixAttrib, attrib);
451  transform = tma->get_transform(_texture_stage);
452  } else {
453  tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
454  }
455 
456  if ((_flags & F_end_tex_offset) != 0) {
457  LVecBase2 tex_offset;
458 
459  if ((_flags & F_start_tex_offset) != 0) {
460  lerp_value(tex_offset, d, _start_tex_offset, _end_tex_offset);
461  } else {
462  tex_offset = transform->get_pos2d();
463  lerp_value_from_prev(tex_offset, d, _prev_d, tex_offset,
464  _end_tex_offset);
465  }
466 
467  transform = transform->set_pos2d(tex_offset);
468  }
469 
470  if ((_flags & F_end_tex_rotate) != 0) {
471  PN_stdfloat tex_rotate;
472 
473  if ((_flags & F_start_tex_rotate) != 0) {
474  lerp_value(tex_rotate, d, _start_tex_rotate, _end_tex_rotate);
475  } else {
476  tex_rotate = transform->get_rotate2d();
477  lerp_value_from_prev(tex_rotate, d, _prev_d, tex_rotate,
478  _end_tex_rotate);
479  }
480 
481  transform = transform->set_rotate2d(tex_rotate);
482  }
483 
484  if ((_flags & F_end_tex_scale) != 0) {
485  LVecBase2 tex_scale;
486 
487  if ((_flags & F_start_tex_scale) != 0) {
488  lerp_value(tex_scale, d, _start_tex_scale, _end_tex_scale);
489  } else {
490  tex_scale = transform->get_scale2d();
491  lerp_value_from_prev(tex_scale, d, _prev_d, tex_scale,
492  _end_tex_scale);
493  }
494 
495  transform = transform->set_scale2d(tex_scale);
496  }
497 
498  // Apply the modified transform back to the state.
499  state = state->set_attrib(tma->add_stage(_texture_stage, transform, _override));
500  }
501 
502 
503  // Now apply the new state back to the node.
504  if (_other.is_empty()) {
505  _node.set_state(state);
506  } else {
507  _node.set_state(_other, state);
508  }
509  } _prev_d = d;
510  _curr_t = t;
511 }
512 
513 /**
514  * Similar to priv_initialize(), but this is called when the interval is being
515  * played backwards; it indicates that the interval should start at the
516  * finishing state and undo any intervening intervals.
517  */
519 priv_reverse_initialize(double t) {
520  check_stopped(get_class_type(), "priv_reverse_initialize");
521  recompute();
522  _state = S_started;
523  _prev_d = 1.0;
524  priv_step(t);
525 }
526 
527 /**
528  * This is called in lieu of priv_reverse_initialize() .. priv_step() ..
529  * priv_reverse_finalize(), when everything is to happen within one frame.
530  * The interval should initialize itself, then leave itself in the initial
531  * state.
532  */
535  check_stopped(get_class_type(), "priv_reverse_initialize");
536  recompute();
537  _state = S_started;
538  _prev_d = 1.0;
539  priv_step(0.0);
540  _state = S_initial;
541 }
542 
543 /**
544  *
545  */
546 void CLerpNodePathInterval::
547 output(std::ostream &out) const {
548  out << get_name() << ":";
549 
550  if ((_flags & F_end_pos) != 0) {
551  out << " pos";
552  if ((_flags & F_start_pos) != 0) {
553  out << " from " << _start_pos;
554  }
555  out << " to " << _end_pos;
556  }
557 
558  if ((_flags & F_end_hpr) != 0) {
559  out << " hpr";
560  if ((_flags & F_start_hpr) != 0) {
561  out << " from " << _start_hpr;
562  }
563  out << " to " << _end_hpr;
564  }
565 
566  if ((_flags & F_end_quat) != 0) {
567  out << " quat";
568  if ((_flags & F_start_quat) != 0) {
569  out << " from " << _start_quat;
570  }
571  out << " to " << _end_quat;
572  }
573 
574  if ((_flags & F_end_scale) != 0) {
575  out << " scale";
576  if ((_flags & F_start_scale) != 0) {
577  out << " from " << _start_scale;
578  }
579  out << " to " << _end_scale;
580  }
581 
582  if ((_flags & F_end_shear) != 0) {
583  out << " shear";
584  if ((_flags & F_start_shear) != 0) {
585  out << " from " << _start_shear;
586  }
587  out << " to " << _end_shear;
588  }
589 
590  if ((_flags & F_end_color) != 0) {
591  out << " color";
592  if ((_flags & F_start_color) != 0) {
593  out << " from " << _start_color;
594  }
595  out << " to " << _end_color;
596  }
597 
598  if ((_flags & F_end_color_scale) != 0) {
599  out << " color_scale";
600  if ((_flags & F_start_color_scale) != 0) {
601  out << " from " << _start_color_scale;
602  }
603  out << " to " << _end_color_scale;
604  }
605 
606  out << " dur " << get_duration();
607 }
608 
609 /**
610  * Sets up a spherical lerp from _start_quat to _end_quat. This precomputes
611  * some important values (like the angle between the quaternions) and sets up
612  * the _slerp method pointer.
613  */
614 void CLerpNodePathInterval::
615 setup_slerp() {
616  if (_start_quat.dot(_end_quat) < 0.0f) {
617  // Make sure both quaternions are on the same side.
618  _start_quat = -_start_quat;
619  }
620 
621  _slerp_angle = _start_quat.angle_rad(_end_quat);
622 
623  if (_slerp_angle < 0.1f) {
624  // If the angle is small, use sin(angle)angle as the denominator, to
625  // provide better behavior with small divisors. This is Don Hatch's
626  // suggestion from http:www.hadron.org~hatchrightway.php .
627  _slerp_denom = csin_over_x(_slerp_angle);
628  _slerp = &CLerpNodePathInterval::slerp_angle_0;
629 
630  } else if (_slerp_angle > 3.14) {
631  // If the angle is close to 180 degrees, the lerp is ambiguous. which
632  // plane should we lerp through? Better pick an intermediate point to
633  // resolve the ambiguity up front.
634 
635  // We pick it by choosing a linear point between the quats and normalizing
636  // it out; this will give an arbitrary point when the angle is exactly
637  // 180, but will behave sanely as the angle approaches 180.
638  _slerp_c = (_start_quat + _end_quat);
639  _slerp_c.normalize();
640  _slerp_angle = _end_quat.angle_rad(_slerp_c);
641  _slerp_denom = csin(_slerp_angle);
642 
643  _slerp = &CLerpNodePathInterval::slerp_angle_180;
644 
645  } else {
646  // Otherwise, use the original Shoemake equation for spherical lerp.
647  _slerp_denom = csin(_slerp_angle);
648  _slerp = &CLerpNodePathInterval::slerp_basic;
649  }
650 
651  nassertv(_slerp_denom != 0.0f);
652  _flags |= F_slerp_setup;
653 }
654 
655 /**
656  * Implements Ken Shoemake's spherical lerp equation. This is appropriate
657  * when the angle between the quaternions is not near one extreme or the
658  * other.
659  */
660 void CLerpNodePathInterval::
661 slerp_basic(LQuaternion &result, PN_stdfloat t) const {
662  nassertv(_slerp_denom != 0.0f);
663  PN_stdfloat ti = 1.0f - t;
664  PN_stdfloat ta = t * _slerp_angle;
665  PN_stdfloat tia = ti * _slerp_angle;
666 
667  if (interval_cat.is_spam()) {
668  interval_cat.spam()
669  << "slerp_basic, (t = " << t << "), angle = " << _slerp_angle << "\n"
670  << "_start_quat = " << _start_quat << ", _end_quat = "
671  << _end_quat << ", denom = " << _slerp_denom << "\n";
672  }
673 
674  result = (csin(tia) * _start_quat + csin(ta) * _end_quat) / _slerp_denom;
675  nassertv(!result.is_nan());
676 }
677 
678 /**
679  * Implements Don Hatch's modified spherical lerp equation, appropriate for
680  * when the angle between the quaternions approaches zero.
681  */
682 void CLerpNodePathInterval::
683 slerp_angle_0(LQuaternion &result, PN_stdfloat t) const {
684  nassertv(_slerp_denom != 0.0f);
685  PN_stdfloat ti = 1.0f - t;
686  PN_stdfloat ta = t * _slerp_angle;
687  PN_stdfloat tia = ti * _slerp_angle;
688 
689  if (interval_cat.is_spam()) {
690  interval_cat.spam()
691  << "slerp_angle_0, (t = " << t << "), angle = " << _slerp_angle
692  << "\n_start_quat = " << _start_quat << ", _end_quat = "
693  << _end_quat << ", denom = " << _slerp_denom << "\n";
694  }
695 
696  result = (csin_over_x(tia) * ti * _start_quat + csin_over_x(ta) * t * _end_quat) / _slerp_denom;
697  nassertv(!result.is_nan());
698 }
699 
700 
701 /**
702  * Implements a two-part slerp, to an intermediate point and out again,
703  * appropriate for when the angle between the quaternions approaches 180
704  * degrees.
705  */
706 void CLerpNodePathInterval::
707 slerp_angle_180(LQuaternion &result, PN_stdfloat t) const {
708  nassertv(_slerp_denom != 0.0f);
709  if (t < 0.5) {
710  // The first half of the lerp: _start_quat to _slerp_c.
711 
712  t *= 2.0f;
713 
714  PN_stdfloat ti = 1.0f - t;
715  PN_stdfloat ta = t * _slerp_angle;
716  PN_stdfloat tia = ti * _slerp_angle;
717 
718  if (interval_cat.is_spam()) {
719  interval_cat.spam()
720  << "slerp_angle_180, first half (t = " << t << "), angle = "
721  << _slerp_angle << "\n_start_quat = " << _start_quat
722  << ", _slerp_c = " << _slerp_c << ", denom = "
723  << _slerp_denom << "\n";
724  }
725 
726  result = (csin(tia) * _start_quat + csin(ta) * _slerp_c) / _slerp_denom;
727 
728  } else {
729  // The second half of the lerp: _slerp_c to _end_quat.
730  t = t * 2.0f - 1.0f;
731 
732  PN_stdfloat ti = 1.0f - t;
733  PN_stdfloat ta = t * _slerp_angle;
734  PN_stdfloat tia = ti * _slerp_angle;
735 
736  if (interval_cat.is_spam()) {
737  interval_cat.spam()
738  << "slerp_angle_180, second half (t = " << t << "), angle = "
739  << _slerp_angle << "\n_slerp_c = " << _slerp_c
740  << ", _end_quat = " << _end_quat << ", denom = "
741  << _slerp_denom << "\n";
742  }
743 
744  result = (csin(tia) * _slerp_c + csin(ta) * _end_quat) / _slerp_denom;
745  }
746 
747  nassertv(!result.is_nan());
748 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_name
Returns the interval's name.
Definition: cInterval.h:124
get_duration
Returns the duration of the interval in seconds.
Definition: cInterval.h:127
The base class for a family of intervals that linearly interpolate one or more numeric values over ti...
Definition: cLerpInterval.h:24
void set_start_scale(const LVecBase3 &scale)
Indicates the initial scale of the lerped node.
void set_start_hpr(const LVecBase3 &hpr)
Indicates the initial rotation of the lerped node.
virtual void priv_instant()
This is called in lieu of priv_initialize() .
virtual void priv_initialize(double t)
This replaces the first call to priv_step(), and indicates that the interval has just begun.
virtual void priv_reverse_initialize(double t)
Similar to priv_initialize(), but this is called when the interval is being played backwards; it indi...
CLerpNodePathInterval(const std::string &name, double duration, BlendType blend_type, bool bake_in_start, bool fluid, const NodePath &node, const NodePath &other)
Constructs a lerp interval that will lerp some properties on the indicated node, possibly relative to...
void set_start_quat(const LQuaternion &quat)
Indicates the initial rotation of the lerped node.
virtual void priv_step(double t)
Advances the time on the interval.
virtual void priv_reverse_instant()
This is called in lieu of priv_reverse_initialize() .
void set_start_pos(const LVecBase3 &pos)
Indicates the initial position of the lerped node.
void set_start_shear(const LVecBase3 &shear)
Indicates the initial shear of the lerped node.
Indicates what color should be applied to renderable geometry.
Definition: colorAttrib.h:27
get_color
If the type is T_flat or T_off, this returns the color that will be applied to geometry.
Definition: colorAttrib.h:47
get_color_type
Returns the type of color specified by this ColorAttrib.
Definition: colorAttrib.h:46
Applies a scale to colors in the scene graph and on vertices.
get_scale
Returns the scale to be applied to colors.
NodePath is the fundamental system for disambiguating instances, and also provides a higher-level int...
Definition: nodePath.h:159
void set_hpr_scale(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r, PN_stdfloat sx, PN_stdfloat sy, PN_stdfloat sz)
Sets the rotation and scale components of the transform, leaving translation untouched.
Definition: nodePath.I:737
void set_quat(const LQuaternion &quat)
Sets the rotation component of the transform, leaving translation and scale untouched.
Definition: nodePath.cxx:1122
void set_pos_quat_scale(const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale)
Replaces the translation, rotation, and scale components, implicitly setting shear to 0.
Definition: nodePath.cxx:1321
LVecBase3 get_hpr() const
Retrieves the rotation component of the transform.
Definition: nodePath.cxx:1110
void set_pos_quat_scale_shear(const LVecBase3 &pos, const LQuaternion &quat, const LVecBase3 &scale, const LVecBase3 &shear)
Completely replaces the transform with new translation, rotation, scale, and shear components.
Definition: nodePath.cxx:1347
const TransformState * get_prev_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the transform that has been set as this node's "previous" position.
Definition: nodePath.cxx:883
void set_pos_hpr_scale(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat h, PN_stdfloat p, PN_stdfloat r, PN_stdfloat sx, PN_stdfloat sy, PN_stdfloat sz)
Completely replaces the transform with new translation, rotation, and scale components.
Definition: nodePath.I:746
bool is_empty() const
Returns true if the NodePath contains no nodes.
Definition: nodePath.I:188
void set_pos_quat(const LVecBase3 &pos, const LQuaternion &quat)
Sets the translation and rotation component of the transform, leaving scale untouched.
Definition: nodePath.cxx:1268
LQuaternion get_quat() const
Retrieves the rotation component of the transform.
Definition: nodePath.cxx:1132
void set_state(const RenderState *state, Thread *current_thread=Thread::get_current_thread())
Changes the complete state object on this node.
Definition: nodePath.I:427
void set_pos_hpr_scale_shear(const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale, const LVecBase3 &shear)
Completely replaces the transform with new translation, rotation, scale, and shear components.
Definition: nodePath.cxx:1334
const RenderState * get_state(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete state object set on this node.
Definition: nodePath.cxx:723
void set_shear(PN_stdfloat shxy, PN_stdfloat shxz, PN_stdfloat shyz)
Sets the shear component of the transform, leaving translation, rotation, and scale untouched.
Definition: nodePath.I:704
void set_pos_hpr(PN_stdfloat x, PN_stdfloat y, PN_stdfloat z, PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Sets the translation and rotation component of the transform, leaving scale untouched.
Definition: nodePath.I:728
const TransformState * get_transform(Thread *current_thread=Thread::get_current_thread()) const
Returns the complete transform object set on this node.
Definition: nodePath.cxx:795
void set_scale(PN_stdfloat scale)
Sets the scale component of the transform, leaving translation and rotation untouched.
Definition: nodePath.I:675
void set_quat_scale(const LQuaternion &quat, const LVecBase3 &scale)
Sets the rotation and scale components of the transform, leaving translation untouched.
Definition: nodePath.cxx:1295
void set_hpr(PN_stdfloat h, PN_stdfloat p, PN_stdfloat r)
Sets the rotation component of the transform, leaving translation and scale untouched.
Definition: nodePath.I:651
This is the base class for a number of render attributes (other than transform) that may be set on sc...
Definition: renderAttrib.h:51
This represents a unique collection of RenderAttrib objects that correspond to a particular renderabl...
Definition: renderState.h:47
Applies a transform matrix to UV's before they are rendered.
Defines the properties of a named stage of the multitexture pipeline.
Definition: textureStage.h:35
Indicates a coordinate-system transform on vertices.
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
float csin_over_x(float v)
Computes sin(x) / x, well-behaved as x approaches 0.
Definition: cmath.I:77
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void lerp_value(NumericType &current_value, double d, const NumericType &starting_value, const NumericType &ending_value)
Applies the linear lerp computation for a single parameter.
Definition: lerp_helpers.h:27
void lerp_value_from_prev(NumericType &current_value, double d, double prev_d, const NumericType &prev_value, const NumericType &ending_value)
Applies the linear lerp computation for a single parameter, when the starting value is implicit.
Definition: lerp_helpers.h:43
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.