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