Panda3D
 All Classes Functions Variables Enumerations
cLerpNodePathInterval.cxx
00001 // Filename: cLerpNodePathInterval.cxx
00002 // Created by:  drose (27Aug02)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 #include "cLerpNodePathInterval.h"
00016 #include "lerp_helpers.h"
00017 #include "transformState.h"
00018 #include "renderState.h"
00019 #include "colorAttrib.h"
00020 #include "colorScaleAttrib.h"
00021 #include "texMatrixAttrib.h"
00022 #include "dcast.h"
00023 #include "config_interval.h"
00024 
00025 TypeHandle CLerpNodePathInterval::_type_handle;
00026 
00027 ////////////////////////////////////////////////////////////////////
00028 //     Function: CLerpNodePathInterval::Constructor
00029 //       Access: Published
00030 //  Description: Constructs a lerp interval that will lerp some
00031 //               properties on the indicated node, possibly relative
00032 //               to the indicated other node (if other is nonempty).
00033 //
00034 //               You must call set_end_pos(), etc. for the various
00035 //               properties you wish to lerp before the first call to
00036 //               priv_initialize().  If you want to set a starting value
00037 //               for any of the properties, you may call
00038 //               set_start_pos(), etc.; otherwise, the starting value
00039 //               is taken from the actual node's value at the time the
00040 //               lerp is performed.
00041 //
00042 //               The starting values may be explicitly specified or
00043 //               omitted.  The value of bake_in_start determines the
00044 //               behavior if the starting values are omitted.  If
00045 //               bake_in_start is true, the values are obtained the
00046 //               first time the lerp runs, and thenceforth are stored
00047 //               within the interval.  If bake_in_start is false, the
00048 //               starting value is computed each frame, based on
00049 //               assuming the current value represents the value set
00050 //               from the last time the interval was run.  This
00051 //               "smart" behavior allows code to manipulate the object
00052 //               event while it is being lerped, and the lerp
00053 //               continues to apply in a sensible way.
00054 //
00055 //               If fluid is true, the prev_transform is not adjusted
00056 //               by the lerp; otherwise, it is reset.
00057 ////////////////////////////////////////////////////////////////////
00058 CLerpNodePathInterval::
00059 CLerpNodePathInterval(const string &name, double duration, 
00060                       CLerpInterval::BlendType blend_type,
00061                       bool bake_in_start, bool fluid,
00062                       const NodePath &node, const NodePath &other) :
00063   CLerpInterval(name, duration, blend_type),
00064   _node(node),
00065   _other(other),
00066   _flags(0),
00067   _texture_stage(TextureStage::get_default()),
00068   _override(0),
00069   _slerp(NULL)
00070 {
00071   if (bake_in_start) {
00072     _flags |= F_bake_in_start;
00073   }
00074   if (fluid) {
00075     _flags |= F_fluid;
00076   }
00077   _prev_d = 0.0;
00078 }
00079 
00080 ////////////////////////////////////////////////////////////////////
00081 //     Function: CLerpNodePathInterval::initialize
00082 //       Access: Published, Virtual
00083 //  Description: This replaces the first call to priv_step(), and indicates
00084 //               that the interval has just begun.  This may be
00085 //               overridden by derived classes that need to do some
00086 //               explicit initialization on the first call.
00087 ////////////////////////////////////////////////////////////////////
00088 void CLerpNodePathInterval::
00089 priv_initialize(double t) {
00090   check_stopped(get_class_type(), "priv_initialize");
00091   recompute();
00092   _prev_d = 0.0;
00093   _state = S_started;
00094   priv_step(t);
00095 }
00096 
00097 ////////////////////////////////////////////////////////////////////
00098 //     Function: CLerpNodePathInterval::instant
00099 //       Access: Published, Virtual
00100 //  Description: This is called in lieu of priv_initialize() .. priv_step()
00101 //               .. priv_finalize(), when everything is to happen within
00102 //               one frame.  The interval should initialize itself,
00103 //               then leave itself in the final state.
00104 ////////////////////////////////////////////////////////////////////
00105 void CLerpNodePathInterval::
00106 priv_instant() {
00107   check_stopped(get_class_type(), "priv_instant");
00108   recompute();
00109   _prev_d = 0.0;
00110   _state = S_started;
00111   priv_step(get_duration());
00112   _state = S_final;
00113 }
00114 
00115 ////////////////////////////////////////////////////////////////////
00116 //     Function: CLerpNodePathInterval::step
00117 //       Access: Published, Virtual
00118 //  Description: Advances the time on the interval.  The time may
00119 //               either increase (the normal case) or decrease
00120 //               (e.g. if the interval is being played by a slider).
00121 ////////////////////////////////////////////////////////////////////
00122 void CLerpNodePathInterval::
00123 priv_step(double t) {
00124   check_started(get_class_type(), "priv_step");
00125   _state = S_started;
00126   double d = compute_delta(t);
00127 
00128   // Save this in case we want to restore it later.
00129   CPT(TransformState) prev_transform = _node.get_prev_transform();
00130 
00131   if ((_flags & (F_end_pos | F_end_hpr | F_end_quat | F_end_scale | F_end_shear)) != 0) {
00132     // We have some transform lerp.
00133     CPT(TransformState) transform;
00134 
00135     if (_other.is_empty()) {
00136       // If there is no other node, it's a local transform lerp.
00137       transform = _node.get_transform();
00138     } else {
00139       // If there *is* another node, we get the transform relative to
00140       // that node.
00141       transform = _node.get_transform(_other);
00142     }
00143     
00144     LPoint3 pos;
00145     LVecBase3 hpr;
00146     LQuaternion quat;
00147     LVecBase3 scale;
00148     LVecBase3 shear;
00149 
00150     if ((_flags & F_end_pos) != 0) {
00151       if ((_flags & F_start_pos) != 0) {
00152         lerp_value(pos, d, _start_pos, _end_pos);
00153 
00154       } else if ((_flags & F_bake_in_start) != 0) {
00155         // Get the current starting pos, and bake it in.
00156         set_start_pos(transform->get_pos());
00157         lerp_value(pos, d, _start_pos, _end_pos);
00158 
00159       } else {
00160         // "smart" lerp from the current pos to the new pos.
00161         pos = transform->get_pos();
00162         lerp_value_from_prev(pos, d, _prev_d, pos, _end_pos);
00163       }
00164     }
00165     if ((_flags & F_end_hpr) != 0) {
00166       if ((_flags & F_start_hpr) != 0) {
00167         lerp_value(hpr, d, _start_hpr, _end_hpr);
00168 
00169       } else if ((_flags & F_start_quat) != 0) {
00170         _start_hpr = _start_quat.get_hpr();
00171         _flags |= F_start_hpr;
00172         lerp_value(hpr, d, _start_hpr, _end_hpr);
00173 
00174       } else if ((_flags & F_bake_in_start) != 0) {
00175         set_start_hpr(transform->get_hpr());
00176         lerp_value(hpr, d, _start_hpr, _end_hpr);
00177 
00178       } else {
00179         hpr = transform->get_hpr();
00180         lerp_value_from_prev(hpr, d, _prev_d, hpr, _end_hpr);
00181       }
00182     }
00183     if ((_flags & F_end_quat) != 0) {
00184       if ((_flags & F_slerp_setup) == 0) {
00185         if ((_flags & F_start_quat) != 0) {
00186           setup_slerp();
00187 
00188         } else if ((_flags & F_start_hpr) != 0) {
00189           _start_quat.set_hpr(_start_hpr);
00190           _flags |= F_start_quat;
00191           setup_slerp();
00192 
00193         } else if ((_flags & F_bake_in_start) != 0) {
00194           set_start_quat(transform->get_quat());
00195           setup_slerp();
00196 
00197         } else {
00198           if (_prev_d == 1.0) {
00199             _start_quat = _end_quat;
00200           } else {
00201             LQuaternion prev_value = transform->get_quat();
00202             _start_quat = (prev_value - _prev_d * _end_quat) / (1.0 - _prev_d);
00203           }
00204           setup_slerp();
00205 
00206           // In this case, clear the slerp_setup flag because we need
00207           // to re-setup the slerp each time.
00208           _flags &= ~F_slerp_setup;
00209         }
00210       }
00211       nassertv(_slerp != NULL);
00212       (this->*_slerp)(quat, d);
00213     }
00214     if ((_flags & F_end_scale) != 0) {
00215       if ((_flags & F_start_scale) != 0) {
00216         lerp_value(scale, d, _start_scale, _end_scale);
00217 
00218       } else if ((_flags & F_bake_in_start) != 0) {
00219         set_start_scale(transform->get_scale());
00220         lerp_value(scale, d, _start_scale, _end_scale);
00221 
00222       } else {
00223         scale = transform->get_scale();
00224         lerp_value_from_prev(scale, d, _prev_d, scale, _end_scale);
00225       }
00226     }
00227     if ((_flags & F_end_shear) != 0) {
00228       if ((_flags & F_start_shear) != 0) {
00229         lerp_value(shear, d, _start_shear, _end_shear);
00230 
00231       } else if ((_flags & F_bake_in_start) != 0) {
00232         set_start_shear(transform->get_shear());
00233         lerp_value(shear, d, _start_shear, _end_shear);
00234 
00235       } else {
00236         shear = transform->get_shear();
00237         lerp_value_from_prev(shear, d, _prev_d, shear, _end_shear);
00238       }
00239     }
00240 
00241     // Now apply the modifications back to the transform.  We want to
00242     // be a little careful here, because we don't want to assume the
00243     // transform has hpr/scale components if they're not needed.  And
00244     // in any case, we only want to apply the components that we
00245     // computed, above.
00246     unsigned int transform_flags = _flags & (F_end_pos | F_end_hpr | F_end_quat | F_end_scale);
00247     switch (transform_flags) {
00248     case 0:
00249       break;
00250 
00251     case F_end_pos:
00252       if (_other.is_empty()) {
00253         _node.set_pos(pos);
00254       } else {
00255         _node.set_pos(_other, pos);
00256       }
00257       break;
00258 
00259     case F_end_hpr:
00260       if (_other.is_empty()) {
00261         _node.set_hpr(hpr);
00262       } else {
00263         _node.set_hpr(_other, hpr);
00264       }
00265       break;
00266 
00267     case F_end_quat:
00268       if (_other.is_empty()) {
00269         _node.set_quat(quat);
00270       } else {
00271         _node.set_quat(_other, quat);
00272       }
00273       break;
00274 
00275     case F_end_scale:
00276       if (_other.is_empty()) {
00277         _node.set_scale(scale);
00278       } else {
00279         _node.set_scale(_other, scale);
00280       }
00281       break;
00282 
00283     case F_end_hpr | F_end_scale:
00284       if (_other.is_empty()) {
00285         _node.set_hpr_scale(hpr, scale);
00286       } else {
00287         _node.set_hpr_scale(hpr, scale);
00288       }
00289       break;
00290 
00291     case F_end_quat | F_end_scale:
00292       if (_other.is_empty()) {
00293         _node.set_quat_scale(quat, scale);
00294       } else {
00295         _node.set_quat_scale(quat, scale);
00296       }
00297       break;
00298 
00299     case F_end_pos | F_end_hpr:
00300       if (_other.is_empty()) {
00301         _node.set_pos_hpr(pos, hpr);
00302       } else {
00303         _node.set_pos_hpr(_other, pos, hpr);
00304       }
00305       break;
00306 
00307     case F_end_pos | F_end_quat:
00308       if (_other.is_empty()) {
00309         _node.set_pos_quat(pos, quat);
00310       } else {
00311         _node.set_pos_quat(_other, pos, quat);
00312       }
00313       break;
00314 
00315     case F_end_pos | F_end_scale:
00316       if (transform->quat_given()) {
00317         if (_other.is_empty()) {
00318           _node.set_pos_quat_scale(pos, transform->get_quat(), scale);
00319         } else {
00320           _node.set_pos_quat_scale(_other, pos, transform->get_quat(), scale);
00321         }
00322       } else {
00323         if (_other.is_empty()) {
00324           _node.set_pos_hpr_scale(pos, transform->get_hpr(), scale);
00325         } else {
00326           _node.set_pos_hpr_scale(_other, pos, transform->get_hpr(), scale);
00327         }
00328       }
00329       break;
00330 
00331     case F_end_pos | F_end_hpr | F_end_scale:
00332       if ((_flags & F_end_shear) != 0) {
00333         // Even better: we have all four components.
00334         if (_other.is_empty()) {
00335           _node.set_pos_hpr_scale_shear(pos, hpr, scale, shear);
00336         } else {
00337           _node.set_pos_hpr_scale_shear(_other, pos, hpr, scale, shear);
00338         }
00339       } else {
00340         // We have only the primary three components.
00341         if (_other.is_empty()) {
00342           _node.set_pos_hpr_scale(pos, hpr, scale);
00343         } else {
00344           _node.set_pos_hpr_scale(_other, pos, hpr, scale);
00345         }
00346       }
00347       break;
00348 
00349     case F_end_pos | F_end_quat | F_end_scale:
00350       if ((_flags & F_end_shear) != 0) {
00351         // Even better: we have all four components.
00352         if (_other.is_empty()) {
00353           _node.set_pos_quat_scale_shear(pos, quat, scale, shear);
00354         } else {
00355           _node.set_pos_quat_scale_shear(_other, pos, quat, scale, shear);
00356         }
00357       } else {
00358         // We have only the primary three components.
00359         if (_other.is_empty()) {
00360           _node.set_pos_quat_scale(pos, quat, scale);
00361         } else {
00362           _node.set_pos_quat_scale(_other, pos, quat, scale);
00363         }
00364       }
00365       break;
00366 
00367     default:
00368       // Some unhandled combination.  We should handle this.
00369       interval_cat.error()
00370         << "Internal error in CLerpNodePathInterval::priv_step().\n";
00371     }
00372     if ((_flags & F_end_shear) != 0) {
00373       // Also apply changes to shear.
00374       if (transform_flags == (F_end_pos | F_end_hpr | F_end_scale) ||
00375           transform_flags == (F_end_pos | F_end_quat | F_end_scale)) {
00376         // Actually, we already handled this case above.
00377 
00378       } else {
00379         if (_other.is_empty()) {
00380           _node.set_shear(shear);
00381         } else {
00382           _node.set_shear(_other, shear);
00383         }
00384       }
00385     }
00386   }
00387 
00388   if ((_flags & F_fluid) != 0) {
00389     // If we have the fluid flag set, we shouldn't mess with the prev
00390     // transform.  Therefore, restore it to what it was before we
00391     // started messing with it.
00392     _node.set_prev_transform(prev_transform);
00393   }
00394 
00395   if ((_flags & (F_end_color | F_end_color_scale | F_end_tex_offset | F_end_tex_rotate | F_end_tex_scale)) != 0) {
00396     // We have some render state lerp.
00397     CPT(RenderState) state;
00398 
00399     if (_other.is_empty()) {
00400       // If there is no other node, it's a local state lerp.  This is
00401       // most common.
00402       state = _node.get_state();
00403     } else {
00404       // If there *is* another node, we get the state relative to that
00405       // node.  This is weird, but you could lerp color (for instance)
00406       // relative to some other node's color.
00407       state = _node.get_state(_other);
00408     }
00409     
00410     // Unlike in the transform case above, we can go ahead and modify
00411     // the state immediately with each attribute change, since these
00412     // attributes don't interrelate.
00413 
00414     if ((_flags & F_end_color) != 0) {
00415       LColor color;
00416 
00417       if ((_flags & F_start_color) != 0) {
00418         lerp_value(color, d, _start_color, _end_color);
00419 
00420       } else {
00421         // Get the previous color.
00422         color.set(1.0f, 1.0f, 1.0f, 1.0f);
00423         const RenderAttrib *attrib =
00424           state->get_attrib(ColorAttrib::get_class_type());
00425         if (attrib != (const RenderAttrib *)NULL) {
00426           const ColorAttrib *ca = DCAST(ColorAttrib, attrib);
00427           if (ca->get_color_type() == ColorAttrib::T_flat) {
00428             color = ca->get_color();
00429           }
00430         }
00431 
00432         lerp_value_from_prev(color, d, _prev_d, color, _end_color);
00433       }
00434 
00435       state = state->add_attrib(ColorAttrib::make_flat(color), _override);
00436     }
00437 
00438     if ((_flags & F_end_color_scale) != 0) {
00439       LVecBase4 color_scale;
00440 
00441       if ((_flags & F_start_color_scale) != 0) {
00442         lerp_value(color_scale, d, _start_color_scale, _end_color_scale);
00443 
00444       } else {
00445         // Get the previous color scale.
00446         color_scale.set(1.0f, 1.0f, 1.0f, 1.0f);
00447         const RenderAttrib *attrib =
00448           state->get_attrib(ColorScaleAttrib::get_class_type());
00449         if (attrib != (const RenderAttrib *)NULL) {
00450           const ColorScaleAttrib *csa = DCAST(ColorScaleAttrib, attrib);
00451           color_scale = csa->get_scale();
00452         }
00453 
00454         lerp_value_from_prev(color_scale, d, _prev_d, color_scale, _end_color_scale);
00455       }
00456 
00457       state = state->add_attrib(ColorScaleAttrib::make(color_scale), _override);
00458     }    
00459 
00460     if ((_flags & (F_end_tex_offset | F_end_tex_rotate | F_end_tex_scale)) != 0) {
00461       // We have a UV lerp.
00462       CPT(TransformState) transform = TransformState::make_identity();
00463 
00464       const RenderAttrib *attrib =
00465         state->get_attrib(TexMatrixAttrib::get_class_type());
00466       CPT(TexMatrixAttrib) tma;
00467       if (attrib != (const TexMatrixAttrib *)NULL) {
00468         tma = DCAST(TexMatrixAttrib, attrib);
00469         transform = tma->get_transform(_texture_stage);
00470       } else {
00471         tma = DCAST(TexMatrixAttrib, TexMatrixAttrib::make());
00472       }
00473 
00474       if ((_flags & F_end_tex_offset) != 0) {
00475         LVecBase2 tex_offset;
00476 
00477         if ((_flags & F_start_tex_offset) != 0) {
00478           lerp_value(tex_offset, d, _start_tex_offset, _end_tex_offset);
00479         } else {
00480           tex_offset = transform->get_pos2d();
00481           lerp_value_from_prev(tex_offset, d, _prev_d, tex_offset, 
00482                                _end_tex_offset);
00483         }
00484 
00485         transform = transform->set_pos2d(tex_offset);
00486       }
00487 
00488       if ((_flags & F_end_tex_rotate) != 0) {
00489         PN_stdfloat tex_rotate;
00490 
00491         if ((_flags & F_start_tex_rotate) != 0) {
00492           lerp_value(tex_rotate, d, _start_tex_rotate, _end_tex_rotate);
00493         } else {
00494           tex_rotate = transform->get_rotate2d();
00495           lerp_value_from_prev(tex_rotate, d, _prev_d, tex_rotate, 
00496                                _end_tex_rotate);
00497         }
00498 
00499         transform = transform->set_rotate2d(tex_rotate);
00500       }
00501 
00502       if ((_flags & F_end_tex_scale) != 0) {
00503         LVecBase2 tex_scale;
00504 
00505         if ((_flags & F_start_tex_scale) != 0) {
00506           lerp_value(tex_scale, d, _start_tex_scale, _end_tex_scale);
00507         } else {
00508           tex_scale = transform->get_scale2d();
00509           lerp_value_from_prev(tex_scale, d, _prev_d, tex_scale, 
00510                                _end_tex_scale);
00511         }
00512 
00513         transform = transform->set_scale2d(tex_scale);
00514       }
00515 
00516       // Apply the modified transform back to the state.
00517       state = state->set_attrib(tma->add_stage(_texture_stage, transform, _override));
00518     }    
00519 
00520 
00521     // Now apply the new state back to the node.
00522     if (_other.is_empty()) {
00523       _node.set_state(state);
00524     } else {
00525       _node.set_state(_other, state);
00526     }
00527   }  _prev_d = d;
00528   _curr_t = t;
00529 }
00530 
00531 ////////////////////////////////////////////////////////////////////
00532 //     Function: CLerpNodePathInterval::reverse_initialize
00533 //       Access: Published, Virtual
00534 //  Description: Similar to priv_initialize(), but this is called when the
00535 //               interval is being played backwards; it indicates that
00536 //               the interval should start at the finishing state and
00537 //               undo any intervening intervals.
00538 ////////////////////////////////////////////////////////////////////
00539 void CLerpNodePathInterval::
00540 priv_reverse_initialize(double t) {
00541   check_stopped(get_class_type(), "priv_reverse_initialize");
00542   recompute();
00543   _state = S_started;
00544   _prev_d = 1.0;
00545   priv_step(t);
00546 }
00547 
00548 ////////////////////////////////////////////////////////////////////
00549 //     Function: CLerpNodePathInterval::reverse_instant
00550 //       Access: Published, Virtual
00551 //  Description: This is called in lieu of priv_reverse_initialize()
00552 //               .. priv_step() .. priv_reverse_finalize(), when everything is
00553 //               to happen within one frame.  The interval should
00554 //               initialize itself, then leave itself in the initial
00555 //               state.
00556 ////////////////////////////////////////////////////////////////////
00557 void CLerpNodePathInterval::
00558 priv_reverse_instant() {
00559   check_stopped(get_class_type(), "priv_reverse_initialize");
00560   recompute();
00561   _state = S_started;
00562   _prev_d = 1.0;
00563   priv_step(0.0);
00564   _state = S_initial;
00565 }
00566 
00567 ////////////////////////////////////////////////////////////////////
00568 //     Function: CLerpNodePathInterval::output
00569 //       Access: Published, Virtual
00570 //  Description: 
00571 ////////////////////////////////////////////////////////////////////
00572 void CLerpNodePathInterval::
00573 output(ostream &out) const {
00574   out << get_name() << ":";
00575 
00576   if ((_flags & F_end_pos) != 0) {
00577     out << " pos";
00578     if ((_flags & F_start_pos) != 0) {
00579       out << " from " << _start_pos;
00580     }
00581     out << " to " << _end_pos;
00582   }
00583 
00584   if ((_flags & F_end_hpr) != 0) {
00585     out << " hpr";
00586     if ((_flags & F_start_hpr) != 0) {
00587       out << " from " << _start_hpr;
00588     }
00589     out << " to " << _end_hpr;
00590   }
00591 
00592   if ((_flags & F_end_quat) != 0) {
00593     out << " quat";
00594     if ((_flags & F_start_quat) != 0) {
00595       out << " from " << _start_quat;
00596     }
00597     out << " to " << _end_quat;
00598   }
00599 
00600   if ((_flags & F_end_scale) != 0) {
00601     out << " scale";
00602     if ((_flags & F_start_scale) != 0) {
00603       out << " from " << _start_scale;
00604     }
00605     out << " to " << _end_scale;
00606   }
00607 
00608   if ((_flags & F_end_shear) != 0) {
00609     out << " shear";
00610     if ((_flags & F_start_shear) != 0) {
00611       out << " from " << _start_shear;
00612     }
00613     out << " to " << _end_shear;
00614   }
00615 
00616   if ((_flags & F_end_color) != 0) {
00617     out << " color";
00618     if ((_flags & F_start_color) != 0) {
00619       out << " from " << _start_color;
00620     }
00621     out << " to " << _end_color;
00622   }
00623 
00624   if ((_flags & F_end_color_scale) != 0) {
00625     out << " color_scale";
00626     if ((_flags & F_start_color_scale) != 0) {
00627       out << " from " << _start_color_scale;
00628     }
00629     out << " to " << _end_color_scale;
00630   }
00631 
00632   out << " dur " << get_duration();
00633 }
00634 
00635 ////////////////////////////////////////////////////////////////////
00636 //     Function: CLerpNodePathInterval::setup_slerp
00637 //       Access: Private
00638 //  Description: Sets up a spherical lerp from _start_quat to
00639 //               _end_quat.  This precomputes some important values
00640 //               (like the angle between the quaternions) and sets up
00641 //               the _slerp method pointer.
00642 ////////////////////////////////////////////////////////////////////
00643 void CLerpNodePathInterval::
00644 setup_slerp() {
00645   if (_start_quat.dot(_end_quat) < 0.0f) {
00646     // Make sure both quaternions are on the same side.
00647     _start_quat = -_start_quat;
00648   }
00649 
00650   _slerp_angle = _start_quat.angle_rad(_end_quat);
00651 
00652   if (_slerp_angle < 0.1f) {
00653     // If the angle is small, use sin(angle)/angle as the denominator,
00654     // to provide better behavior with small divisors.  This is Don
00655     // Hatch's suggestion from http://www.hadron.org/~hatch/rightway.php .
00656     _slerp_denom = csin_over_x(_slerp_angle);
00657     _slerp = &CLerpNodePathInterval::slerp_angle_0;
00658 
00659   } else if (_slerp_angle > 3.14) {
00660     // If the angle is close to 180 degrees, the lerp is ambiguous.
00661     // which plane should we lerp through?  Better pick an
00662     // intermediate point to resolve the ambiguity up front.
00663 
00664     // We pick it by choosing a linear point between the quats and
00665     // normalizing it out; this will give an arbitrary point when the
00666     // angle is exactly 180, but will behave sanely as the angle
00667     // approaches 180.
00668     _slerp_c = (_start_quat + _end_quat);
00669     _slerp_c.normalize();
00670     _slerp_angle = _end_quat.angle_rad(_slerp_c);
00671     _slerp_denom = csin(_slerp_angle);
00672 
00673     _slerp = &CLerpNodePathInterval::slerp_angle_180;
00674     
00675   } else {
00676     // Otherwise, use the original Shoemake equation for spherical
00677     // lerp.
00678     _slerp_denom = csin(_slerp_angle);
00679     _slerp = &CLerpNodePathInterval::slerp_basic;
00680   }
00681 
00682   nassertv(_slerp_denom != 0.0f);
00683   _flags |= F_slerp_setup;
00684 }
00685 
00686 ////////////////////////////////////////////////////////////////////
00687 //     Function: CLerpNodePathInterval::slerp_basic
00688 //       Access: Private
00689 //  Description: Implements Ken Shoemake's spherical lerp equation.
00690 //               This is appropriate when the angle between the
00691 //               quaternions is not near one extreme or the other.
00692 ////////////////////////////////////////////////////////////////////
00693 void CLerpNodePathInterval::
00694 slerp_basic(LQuaternion &result, PN_stdfloat t) const {
00695   nassertv(_slerp_denom != 0.0f);
00696   PN_stdfloat ti = 1.0f - t;
00697   PN_stdfloat ta = t * _slerp_angle;
00698   PN_stdfloat tia = ti * _slerp_angle;
00699 
00700   if (interval_cat.is_spam()) {
00701     interval_cat.spam()
00702       << "slerp_basic, (t = " << t << "), angle = " << _slerp_angle << "\n"
00703       << "_start_quat = " << _start_quat << ", _end_quat = "
00704       << _end_quat << ", denom = " << _slerp_denom << "\n";
00705   }
00706 
00707   result = (csin(tia) * _start_quat + csin(ta) * _end_quat) / _slerp_denom;
00708   nassertv(!result.is_nan());
00709 }
00710 
00711 ////////////////////////////////////////////////////////////////////
00712 //     Function: CLerpNodePathInterval::slerp_angle_0
00713 //       Access: Private
00714 //  Description: Implements Don Hatch's modified spherical lerp
00715 //               equation, appropriate for when the angle between the
00716 //               quaternions approaches zero.
00717 ////////////////////////////////////////////////////////////////////
00718 void CLerpNodePathInterval::
00719 slerp_angle_0(LQuaternion &result, PN_stdfloat t) const {
00720   nassertv(_slerp_denom != 0.0f);
00721   PN_stdfloat ti = 1.0f - t;
00722   PN_stdfloat ta = t * _slerp_angle;
00723   PN_stdfloat tia = ti * _slerp_angle;
00724 
00725   if (interval_cat.is_spam()) {
00726     interval_cat.spam()
00727       << "slerp_angle_0, (t = " << t << "), angle = " << _slerp_angle
00728       << "\n_start_quat = " << _start_quat << ", _end_quat = "
00729       << _end_quat << ", denom = " << _slerp_denom << "\n";
00730   }
00731 
00732   result = (csin_over_x(tia) * ti * _start_quat + csin_over_x(ta) * t * _end_quat) / _slerp_denom;
00733   nassertv(!result.is_nan());
00734 }
00735 
00736 
00737 ////////////////////////////////////////////////////////////////////
00738 //     Function: CLerpNodePathInterval::slerp_angle_180
00739 //       Access: Private
00740 //  Description: Implements a two-part slerp, to an intermediate point
00741 //               and out again, appropriate for when the angle between
00742 //               the quaternions approaches 180 degrees.
00743 ////////////////////////////////////////////////////////////////////
00744 void CLerpNodePathInterval::
00745 slerp_angle_180(LQuaternion &result, PN_stdfloat t) const {
00746   nassertv(_slerp_denom != 0.0f);
00747   if (t < 0.5) {
00748     // The first half of the lerp: _start_quat to _slerp_c.
00749 
00750     t *= 2.0f;
00751 
00752     PN_stdfloat ti = 1.0f - t;
00753     PN_stdfloat ta = t * _slerp_angle;
00754     PN_stdfloat tia = ti * _slerp_angle;
00755 
00756     if (interval_cat.is_spam()) {
00757       interval_cat.spam()
00758         << "slerp_angle_180, first half (t = " << t << "), angle = "
00759         << _slerp_angle << "\n_start_quat = " << _start_quat
00760         << ", _slerp_c = " << _slerp_c << ", denom = " 
00761         << _slerp_denom << "\n";
00762     }
00763     
00764     result = (csin(tia) * _start_quat + csin(ta) * _slerp_c) / _slerp_denom;
00765 
00766   } else {
00767     // The second half of the lerp: _slerp_c to _end_quat.
00768     t = t * 2.0f - 1.0f;
00769 
00770     PN_stdfloat ti = 1.0f - t;
00771     PN_stdfloat ta = t * _slerp_angle;
00772     PN_stdfloat tia = ti * _slerp_angle;
00773 
00774     if (interval_cat.is_spam()) {
00775       interval_cat.spam()
00776         << "slerp_angle_180, second half (t = " << t << "), angle = "
00777         << _slerp_angle << "\n_slerp_c = " << _slerp_c
00778         << ", _end_quat = " << _end_quat << ", denom = " 
00779         << _slerp_denom << "\n";
00780     }
00781     
00782     result = (csin(tia) * _slerp_c + csin(ta) * _end_quat) / _slerp_denom;
00783   }
00784 
00785   nassertv(!result.is_nan());
00786 }
 All Classes Functions Variables Enumerations