00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "partBundle.h"
00017 #include "animBundle.h"
00018 #include "animBundleNode.h"
00019 #include "animControl.h"
00020 #include "loader.h"
00021 #include "animPreloadTable.h"
00022 #include "config_chan.h"
00023 #include "bitArray.h"
00024 #include "string_utils.h"
00025 #include "indent.h"
00026 #include "datagram.h"
00027 #include "datagramIterator.h"
00028 #include "bamReader.h"
00029 #include "bamWriter.h"
00030 #include "configVariableEnum.h"
00031 #include "loaderOptions.h"
00032
00033 #include <algorithm>
00034
00035 TypeHandle PartBundle::_type_handle;
00036
00037
00038 static ConfigVariableEnum<PartBundle::BlendType> anim_blend_type
00039 ("anim-blend-type", PartBundle::BT_normalized_linear,
00040 PRC_DESC("The default blend type to use for blending animations between "
00041 "frames, or between multiple animations. See interpolate-frames, "
00042 "and also PartBundle::set_anim_blend_flag() and "
00043 "PartBundle::set_frame_blend_flag()."));
00044
00045
00046
00047
00048
00049
00050
00051
00052 PartBundle::
00053 PartBundle(const PartBundle ©) :
00054 PartGroup(copy)
00055 {
00056 _anim_preload = copy._anim_preload;
00057 _update_delay = 0.0;
00058
00059 CDWriter cdata(_cycler, true);
00060 CDReader cdata_from(copy._cycler);
00061 cdata->_blend_type = cdata_from->_blend_type;
00062 cdata->_anim_blend_flag = cdata_from->_anim_blend_flag;
00063 cdata->_frame_blend_flag = cdata_from->_frame_blend_flag;
00064 cdata->_root_xform = cdata_from->_root_xform;
00065 }
00066
00067
00068
00069
00070
00071
00072
00073
00074 PartBundle::
00075 PartBundle(const string &name) :
00076 PartGroup(name)
00077 {
00078 _update_delay = 0.0;
00079 }
00080
00081
00082
00083
00084
00085
00086
00087 PartGroup *PartBundle::
00088 make_copy() const {
00089 return new PartBundle(*this);
00090 }
00091
00092
00093
00094
00095
00096
00097
00098 void PartBundle::
00099 merge_anim_preloads(const PartBundle *other) {
00100 if (other->_anim_preload == (AnimPreloadTable *)NULL ||
00101 _anim_preload == other->_anim_preload) {
00102
00103 return;
00104 }
00105
00106 if (_anim_preload == (AnimPreloadTable *)NULL) {
00107
00108 _anim_preload = other->_anim_preload;
00109 return;
00110 }
00111
00112
00113 PT(AnimPreloadTable) anim_preload = _anim_preload.get_write_pointer();
00114 anim_preload->add_anims_from(other->_anim_preload.get_read_pointer());
00115 }
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133 void PartBundle::
00134 set_anim_blend_flag(bool anim_blend_flag) {
00135 nassertv(Thread::get_current_pipeline_stage() == 0);
00136
00137 CDLockedReader cdata(_cycler);
00138 if (cdata->_anim_blend_flag != anim_blend_flag) {
00139 CDWriter cdataw(_cycler, cdata);
00140 cdataw->_anim_blend_flag = anim_blend_flag;
00141
00142 if (!anim_blend_flag && cdataw->_blend.size() > 1) {
00143
00144
00145
00146
00147 nassertv(cdataw->_last_control_set != NULL);
00148 clear_and_stop_intersecting(cdataw->_last_control_set, cdataw);
00149 }
00150
00151 cdataw->_anim_changed = true;
00152 }
00153 }
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163 PT(PartBundle) PartBundle::
00164 apply_transform(const TransformState *transform) {
00165 if (transform->is_identity()) {
00166
00167 return this;
00168 }
00169
00170 AppliedTransforms::iterator ati = _applied_transforms.find(transform);
00171 if (ati != _applied_transforms.end()) {
00172 if ((*ati).first.is_valid_pointer() &&
00173 (*ati).second.is_valid_pointer()) {
00174
00175 return (*ati).second.p();
00176 }
00177 }
00178
00179 PT(PartBundle) new_bundle = DCAST(PartBundle, copy_subgraph());
00180 new_bundle->xform(transform->get_mat());
00181
00182 if (ati != _applied_transforms.end()) {
00183
00184 (*ati).first.refresh();
00185 (*ati).second = new_bundle;
00186 } else {
00187
00188 bool inserted = _applied_transforms.insert(AppliedTransforms::value_type(transform, new_bundle)).second;
00189 nassertr(inserted, new_bundle);
00190 }
00191
00192
00193
00194 new_bundle->force_update();
00195
00196 return new_bundle;
00197 }
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214 void PartBundle::
00215 clear_control_effects() {
00216 nassertv(Thread::get_current_pipeline_stage() == 0);
00217
00218 CDLockedReader cdata(_cycler);
00219 if (!cdata->_blend.empty()) {
00220 CDWriter cdataw(_cycler, cdata);
00221 cdataw->_blend.clear();
00222 cdataw->_net_blend = 0.0f;
00223 cdataw->_anim_changed = true;
00224 determine_effective_channels(cdataw);
00225 }
00226 }
00227
00228
00229
00230
00231
00232
00233 void PartBundle::
00234 output(ostream &out) const {
00235 out << get_type() << " " << get_name();
00236 }
00237
00238
00239
00240
00241
00242
00243
00244 void PartBundle::
00245 write(ostream &out, int indent_level) const {
00246 indent(out, indent_level)
00247 << get_type() << " " << get_name() << " {\n";
00248 write_descendants(out, indent_level + 2);
00249 indent(out, indent_level) << "}\n";
00250 }
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276 PT(AnimControl) PartBundle::
00277 bind_anim(AnimBundle *anim, int hierarchy_match_flags,
00278 const PartSubset &subset) {
00279 PT(AnimControl) control = new AnimControl(anim->get_name(), this, 1.0f, 0);
00280 if (do_bind_anim(control, anim, hierarchy_match_flags, subset)) {
00281 return control;
00282 }
00283
00284 return NULL;
00285 }
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316 PT(AnimControl) PartBundle::
00317 load_bind_anim(Loader *loader, const Filename &filename,
00318 int hierarchy_match_flags, const PartSubset &subset,
00319 bool allow_async) {
00320 nassertr(loader != (Loader *)NULL, NULL);
00321
00322 LoaderOptions anim_options(LoaderOptions::LF_search |
00323 LoaderOptions::LF_report_errors |
00324 LoaderOptions::LF_convert_anim);
00325 string basename = filename.get_basename_wo_extension();
00326
00327 int anim_index = -1;
00328 CPT(AnimPreloadTable) anim_preload = _anim_preload.get_read_pointer();
00329 if (anim_preload != (AnimPreloadTable *)NULL) {
00330 anim_index = anim_preload->find_anim(basename);
00331 }
00332
00333 if (anim_index < 0 || !allow_async || !Thread::is_threading_supported()) {
00334
00335
00336
00337
00338 PT(PandaNode) model = loader->load_sync(filename, anim_options);
00339 if (model == (PandaNode *)NULL) {
00340
00341 return NULL;
00342 }
00343 AnimBundle *anim = AnimBundleNode::find_anim_bundle(model);
00344 if (anim == (AnimBundle *)NULL) {
00345
00346 return NULL;
00347 }
00348 PT(AnimControl) control = bind_anim(anim, hierarchy_match_flags, subset);
00349 if (control == (AnimControl *)NULL) {
00350
00351 return NULL;
00352 }
00353 control->set_anim_model(model);
00354 return control;
00355 }
00356
00357
00358
00359 PN_stdfloat frame_rate = anim_preload->get_base_frame_rate(anim_index);
00360 int num_frames = anim_preload->get_num_frames(anim_index);
00361 PT(AnimControl) control =
00362 new AnimControl(basename, this, frame_rate, num_frames);
00363
00364 if (!subset.is_include_empty()) {
00365
00366 int joint_index = 0;
00367 BitArray bound_joints;
00368 find_bound_joints(joint_index, false, bound_joints, subset);
00369 control->set_bound_joints(bound_joints);
00370 }
00371
00372 PT(BindAnimRequest) request =
00373 new BindAnimRequest(string("bind:") + filename.get_basename(),
00374 filename, anim_options, loader, control,
00375 hierarchy_match_flags, subset);
00376 request->set_priority(async_bind_priority);
00377 loader->load_async(request);
00378
00379 return control;
00380 }
00381
00382
00383
00384
00385
00386
00387
00388
00389 void PartBundle::
00390 wait_pending() {
00391 CDReader cdata(_cycler);
00392 ChannelBlend::const_iterator cbi;
00393 for (cbi = cdata->_blend.begin();
00394 cbi != cdata->_blend.end();
00395 ++cbi) {
00396 AnimControl *control = (*cbi).first;
00397 PN_stdfloat effect = (*cbi).second;
00398 if (effect != 0.0f) {
00399 control->wait_pending();
00400 }
00401 }
00402 }
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417 bool PartBundle::
00418 freeze_joint(const string &joint_name, const TransformState *transform) {
00419 PartGroup *child = find_child(joint_name);
00420 if (child == (PartGroup *)NULL) {
00421 return false;
00422 }
00423
00424 CDWriter cdata(_cycler, false);
00425 cdata->_anim_changed = true;
00426
00427 return child->apply_freeze(transform);
00428 }
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443 bool PartBundle::
00444 freeze_joint(const string &joint_name, const LVecBase3 &pos, const LVecBase3 &hpr, const LVecBase3 &scale) {
00445 PartGroup *child = find_child(joint_name);
00446 if (child == (PartGroup *)NULL) {
00447 return false;
00448 }
00449
00450 CDWriter cdata(_cycler, false);
00451 cdata->_anim_changed = true;
00452
00453 return child->apply_freeze_matrix(pos, hpr, scale);
00454 }
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469 bool PartBundle::
00470 freeze_joint(const string &joint_name, PN_stdfloat value) {
00471 PartGroup *child = find_child(joint_name);
00472 if (child == (PartGroup *)NULL) {
00473 return false;
00474 }
00475
00476 CDWriter cdata(_cycler, false);
00477 cdata->_anim_changed = true;
00478
00479 return child->apply_freeze_scalar(value);
00480 }
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495 bool PartBundle::
00496 control_joint(const string &joint_name, PandaNode *node) {
00497 PartGroup *child = find_child(joint_name);
00498 if (child == (PartGroup *)NULL) {
00499 return false;
00500 }
00501
00502 CDWriter cdata(_cycler, false);
00503 cdata->_anim_changed = true;
00504
00505 return child->apply_control(node);
00506 }
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520 bool PartBundle::
00521 release_joint(const string &joint_name) {
00522 PartGroup *child = find_child(joint_name);
00523 if (child == (PartGroup *)NULL) {
00524 return false;
00525 }
00526
00527 CDWriter cdata(_cycler, false);
00528 cdata->_anim_changed = true;
00529
00530 return child->clear_forced_channel();
00531 }
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543 bool PartBundle::
00544 update() {
00545 Thread *current_thread = Thread::get_current_thread();
00546 CDWriter cdata(_cycler, false, current_thread);
00547 bool any_changed = false;
00548
00549 double now = ClockObject::get_global_clock()->get_frame_time(current_thread);
00550 if (now > cdata->_last_update + _update_delay || cdata->_anim_changed) {
00551 bool anim_changed = cdata->_anim_changed;
00552 bool frame_blend_flag = cdata->_frame_blend_flag;
00553
00554 any_changed = do_update(this, cdata, NULL, false, anim_changed,
00555 current_thread);
00556
00557
00558 ChannelBlend::const_iterator cbi;
00559 for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
00560 AnimControl *control = (*cbi).first;
00561 control->mark_channels(frame_blend_flag);
00562 }
00563
00564 cdata->_anim_changed = false;
00565 cdata->_last_update = now;
00566 }
00567
00568 return any_changed;
00569 }
00570
00571
00572
00573
00574
00575
00576
00577
00578 bool PartBundle::
00579 force_update() {
00580 Thread *current_thread = Thread::get_current_thread();
00581 CDWriter cdata(_cycler, false, current_thread);
00582 bool any_changed = do_update(this, cdata, NULL, true, true, current_thread);
00583
00584
00585 ChannelBlend::const_iterator cbi;
00586 for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
00587 AnimControl *control = (*cbi).first;
00588 control->mark_channels(cdata->_frame_blend_flag);
00589 }
00590
00591 cdata->_anim_changed = false;
00592
00593 return any_changed;
00594 }
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604 void PartBundle::
00605 control_activated(AnimControl *control) {
00606 nassertv(Thread::get_current_pipeline_stage() == 0);
00607 nassertv(control->get_part() == this);
00608
00609 CDLockedReader cdata(_cycler);
00610
00611
00612
00613 if (!cdata->_anim_blend_flag) {
00614 CDWriter cdataw(_cycler, cdata);
00615 do_set_control_effect(control, 1.0f, cdataw);
00616 }
00617 }
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627 bool PartBundle::
00628 do_bind_anim(AnimControl *control, AnimBundle *anim,
00629 int hierarchy_match_flags, const PartSubset &subset) {
00630 nassertr(Thread::get_current_pipeline_stage() == 0, false);
00631
00632
00633
00634 PT(AnimBundle) ptanim = anim;
00635
00636 if ((hierarchy_match_flags & HMF_ok_wrong_root_name) == 0) {
00637
00638 if (get_name() != ptanim->get_name()) {
00639 if (chan_cat.is_error()) {
00640 chan_cat.error()
00641 << "Root name of part (" << get_name()
00642 << ") does not match that of anim (" << ptanim->get_name()
00643 << ")\n";
00644 }
00645 return false;
00646 }
00647 }
00648
00649 if (!check_hierarchy(anim, NULL, hierarchy_match_flags)) {
00650 return false;
00651 }
00652
00653 plist<int> holes;
00654 int channel_index = 0;
00655 pick_channel_index(holes, channel_index);
00656
00657 if (!holes.empty()) {
00658 channel_index = holes.front();
00659 }
00660
00661 int joint_index = 0;
00662 BitArray bound_joints;
00663 if (subset.is_include_empty()) {
00664 bound_joints = BitArray::all_on();
00665 }
00666 bind_hierarchy(ptanim, channel_index, joint_index,
00667 subset.is_include_empty(), bound_joints, subset);
00668 control->setup_anim(this, anim, channel_index, bound_joints);
00669
00670 CDReader cdata(_cycler);
00671 determine_effective_channels(cdata);
00672
00673 return true;
00674 }
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684 void PartBundle::
00685 add_node(PartBundleNode *node) {
00686 nassertv(find(_nodes.begin(), _nodes.end(), node) == _nodes.end());
00687 _nodes.push_back(node);
00688 }
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699 void PartBundle::
00700 remove_node(PartBundleNode *node) {
00701 Nodes::iterator ni = find(_nodes.begin(), _nodes.end(), node);
00702 nassertv(ni != _nodes.end());
00703 _nodes.erase(ni);
00704 }
00705
00706
00707
00708
00709
00710
00711 void PartBundle::
00712 do_set_control_effect(AnimControl *control, PN_stdfloat effect, CData *cdata) {
00713 nassertv(control->get_part() == this);
00714
00715 if (effect == 0.0f) {
00716
00717 ChannelBlend::iterator cbi = cdata->_blend.find(control);
00718 if (cbi != cdata->_blend.end()) {
00719 cdata->_blend.erase(cbi);
00720 cdata->_anim_changed = true;
00721 }
00722
00723 } else {
00724
00725
00726
00727
00728 if (!cdata->_anim_blend_flag) {
00729 clear_and_stop_intersecting(control, cdata);
00730 }
00731
00732 if (do_get_control_effect(control, cdata) != effect) {
00733 cdata->_blend[control] = effect;
00734 cdata->_anim_changed = true;
00735 }
00736 cdata->_last_control_set = control;
00737 }
00738
00739 recompute_net_blend(cdata);
00740 }
00741
00742
00743
00744
00745
00746
00747 PN_stdfloat PartBundle::
00748 do_get_control_effect(AnimControl *control, const CData *cdata) const {
00749 nassertr(control->get_part() == this, 0.0f);
00750
00751 ChannelBlend::const_iterator cbi = cdata->_blend.find(control);
00752 if (cbi == cdata->_blend.end()) {
00753
00754 return 0.0f;
00755 } else {
00756 return (*cbi).second;
00757 }
00758 }
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768 void PartBundle::
00769 recompute_net_blend(CData *cdata) {
00770 cdata->_net_blend = 0.0f;
00771
00772 ChannelBlend::const_iterator bti;
00773 for (bti = cdata->_blend.begin(); bti != cdata->_blend.end(); ++bti) {
00774 cdata->_net_blend += (*bti).second;
00775 }
00776 determine_effective_channels(cdata);
00777 }
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789 void PartBundle::
00790 clear_and_stop_intersecting(AnimControl *control, CData *cdata) {
00791 double new_net_blend = 0.0f;
00792 ChannelBlend new_blend;
00793 bool any_changed = false;
00794
00795 ChannelBlend::iterator cbi;
00796 for (cbi = cdata->_blend.begin(); cbi != cdata->_blend.end(); ++cbi) {
00797 AnimControl *ac = (*cbi).first;
00798 if (ac == control ||
00799 !ac->get_bound_joints().has_bits_in_common(control->get_bound_joints())) {
00800
00801
00802 new_blend.insert(new_blend.end(), (*cbi));
00803 new_net_blend += (*cbi).second;
00804 } else {
00805
00806 ac->stop();
00807 any_changed = true;
00808 }
00809 }
00810
00811 if (any_changed) {
00812 cdata->_net_blend = new_net_blend;
00813 cdata->_blend.swap(new_blend);
00814 cdata->_anim_changed = true;
00815 determine_effective_channels(cdata);
00816 }
00817 }
00818
00819
00820
00821
00822
00823
00824
00825
00826 void PartBundle::
00827 finalize(BamReader *) {
00828 Thread *current_thread = Thread::get_current_thread();
00829 CDWriter cdata(_cycler, true);
00830 do_update(this, cdata, NULL, true, true, current_thread);
00831 }
00832
00833
00834
00835
00836
00837
00838
00839 void PartBundle::
00840 write_datagram(BamWriter *manager, Datagram &dg) {
00841 PartGroup::write_datagram(manager, dg);
00842 manager->write_pointer(dg, _anim_preload.get_read_pointer());
00843 manager->write_cdata(dg, _cycler);
00844 }
00845
00846
00847
00848
00849
00850
00851
00852
00853 int PartBundle::
00854 complete_pointers(TypedWritable **p_list, BamReader *manager) {
00855 int pi = PartGroup::complete_pointers(p_list, manager);
00856
00857 if (manager->get_file_minor_ver() >= 17) {
00858 _anim_preload = DCAST(AnimPreloadTable, p_list[pi++]);
00859 }
00860
00861 return pi;
00862 }
00863
00864
00865
00866
00867
00868
00869 TypedWritable* PartBundle::
00870 make_from_bam(const FactoryParams ¶ms) {
00871 PartBundle *me = new PartBundle;
00872 DatagramIterator scan;
00873 BamReader *manager;
00874
00875 parse_params(params, scan, manager);
00876 me->fillin(scan, manager);
00877 manager->register_finalize(me);
00878 return me;
00879 }
00880
00881
00882
00883
00884
00885
00886
00887
00888 void PartBundle::
00889 fillin(DatagramIterator &scan, BamReader *manager) {
00890 PartGroup::fillin(scan, manager);
00891 if (manager->get_file_minor_ver() >= 17) {
00892 manager->read_pointer(scan);
00893 }
00894 if (manager->get_file_minor_ver() >= 10) {
00895 manager->read_cdata(scan, _cycler);
00896 }
00897 if (manager->get_file_minor_ver() == 11) {
00898
00899 scan.get_bool();
00900 }
00901 }
00902
00903
00904
00905
00906
00907
00908 void PartBundle::
00909 register_with_read_factory() {
00910 BamReader::get_factory()->register_factory(get_class_type(), make_from_bam);
00911 }
00912
00913
00914
00915
00916
00917
00918 PartBundle::CData::
00919 CData() {
00920 _blend_type = anim_blend_type;
00921 _anim_blend_flag = false;
00922 _frame_blend_flag = interpolate_frames;
00923 _root_xform = LMatrix4::ident_mat();
00924 _last_control_set = NULL;
00925 _net_blend = 0.0f;
00926 _anim_changed = false;
00927 _last_update = 0.0;
00928 }
00929
00930
00931
00932
00933
00934
00935 PartBundle::CData::
00936 CData(const PartBundle::CData ©) :
00937 _blend_type(copy._blend_type),
00938 _anim_blend_flag(copy._anim_blend_flag),
00939 _frame_blend_flag(copy._frame_blend_flag),
00940 _root_xform(copy._root_xform),
00941 _last_control_set(copy._last_control_set),
00942 _blend(copy._blend),
00943 _net_blend(copy._net_blend),
00944 _anim_changed(copy._anim_changed),
00945 _last_update(copy._last_update)
00946 {
00947
00948
00949
00950 }
00951
00952
00953
00954
00955
00956
00957 CycleData *PartBundle::CData::
00958 make_copy() const {
00959 return new CData(*this);
00960 }
00961
00962
00963
00964
00965
00966
00967
00968 void PartBundle::CData::
00969 write_datagram(BamWriter *manager, Datagram &dg) const {
00970 dg.add_uint8(_blend_type);
00971 dg.add_bool(_anim_blend_flag);
00972 dg.add_bool(_frame_blend_flag);
00973 _root_xform.write_datagram(dg);
00974
00975
00976 }
00977
00978
00979
00980
00981
00982
00983
00984
00985 void PartBundle::CData::
00986 fillin(DatagramIterator &scan, BamReader *manager) {
00987 _blend_type = (BlendType)scan.get_uint8();
00988 _anim_blend_flag = scan.get_bool();
00989 _frame_blend_flag = scan.get_bool();
00990 _root_xform.read_datagram(scan);
00991 }
00992
00993
00994
00995
00996
00997 ostream &
00998 operator << (ostream &out, PartBundle::BlendType blend_type) {
00999 switch (blend_type) {
01000 case PartBundle::BT_linear:
01001 return out << "linear";
01002
01003 case PartBundle::BT_normalized_linear:
01004 return out << "normalized_linear";
01005
01006 case PartBundle::BT_componentwise:
01007 return out << "componentwise";
01008
01009 case PartBundle::BT_componentwise_quat:
01010 return out << "componentwise_quat";
01011 }
01012
01013 chan_cat->error()
01014 << "Invalid BlendType value: " << (int)blend_type << "\n";
01015 nassertr(false, out);
01016 return out;
01017 }
01018
01019
01020
01021
01022
01023 istream &
01024 operator >> (istream &in, PartBundle::BlendType &blend_type) {
01025 string word;
01026 in >> word;
01027
01028 if (cmp_nocase_uh(word, "linear") == 0) {
01029 blend_type = PartBundle::BT_linear;
01030
01031 } else if (cmp_nocase_uh(word, "normalized_linear") == 0) {
01032 blend_type = PartBundle::BT_normalized_linear;
01033
01034 } else if (cmp_nocase_uh(word, "componentwise") == 0) {
01035 blend_type = PartBundle::BT_componentwise;
01036
01037 } else if (cmp_nocase_uh(word, "componentwise_quat") == 0) {
01038 blend_type = PartBundle::BT_componentwise_quat;
01039
01040 } else {
01041 chan_cat->error()
01042 << "Invalid BlendType string: " << word << "\n";
01043 blend_type = PartBundle::BT_linear;
01044 }
01045 return in;
01046 }