Panda3D
|
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 }