eaa1626f7d80e4152a3e4eb38b0138f1699d411a
[blender.git] / source / blender / collada / AnimationExporter.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup collada
19  */
20
21 #include "GeometryExporter.h"
22 #include "AnimationExporter.h"
23 #include "AnimationClipExporter.h"
24 #include "BCAnimationSampler.h"
25 #include "MaterialExporter.h"
26 #include "collada_utils.h"
27
28 std::string EMPTY_STRING;
29
30 std::string AnimationExporter::get_axis_name(std::string channel, int id)
31 {
32   static std::map<std::string, std::vector<std::string>> BC_COLLADA_AXIS_FROM_TYPE = {
33       {"color", {"R", "G", "B"}},
34       {"specular_color", {"R", "G", "B"}},
35       {"diffuse_color", {"R", "G", "B"}},
36       {"alpha", {"R", "G", "B"}},
37       {"scale", {"X", "Y", "Z"}},
38       {"location", {"X", "Y", "Z"}},
39       {"rotation_euler", {"X", "Y", "Z"}}};
40
41   std::map<std::string, std::vector<std::string>>::const_iterator it;
42   it = BC_COLLADA_AXIS_FROM_TYPE.find(channel);
43   if (it == BC_COLLADA_AXIS_FROM_TYPE.end())
44     return "";
45
46   const std::vector<std::string> &subchannel = it->second;
47   if (id >= subchannel.size())
48     return "";
49   return subchannel[id];
50 }
51
52 bool AnimationExporter::open_animation_container(bool has_container, Object *ob)
53 {
54   if (!has_container) {
55     char anim_id[200];
56     sprintf(anim_id, "action_container-%s", translate_id(id_name(ob)).c_str());
57     openAnimation(anim_id, encode_xml(id_name(ob)));
58   }
59   return true;
60 }
61
62 void AnimationExporter::openAnimationWithClip(std::string action_id, std::string action_name)
63 {
64   std::vector<std::string> anim_meta_entry;
65   anim_meta_entry.push_back(translate_id(action_id));
66   anim_meta_entry.push_back(action_name);
67   anim_meta.push_back(anim_meta_entry);
68
69   openAnimation(translate_id(action_id), action_name);
70 }
71
72 void AnimationExporter::close_animation_container(bool has_container)
73 {
74   if (has_container)
75     closeAnimation();
76 }
77
78 bool AnimationExporter::exportAnimations()
79 {
80   Scene *sce = blender_context.get_scene();
81
82   LinkNode *export_set = this->export_settings->export_set;
83   bool has_anim_data = bc_has_animations(sce, export_set);
84   int animation_count = 0;
85   if (has_anim_data) {
86
87     BCObjectSet animated_subset;
88     BCAnimationSampler::get_animated_from_export_set(animated_subset, *export_set);
89     animation_count = animated_subset.size();
90     BCAnimationSampler animation_sampler(blender_context, animated_subset);
91
92     try {
93       animation_sampler.sample_scene(export_settings->sampling_rate,
94                                      /*keyframe_at_end = */ true,
95                                      export_settings->open_sim,
96                                      export_settings->keep_keyframes,
97                                      export_settings->export_animation_type);
98
99       openLibrary();
100
101       BCObjectSet::iterator it;
102       for (it = animated_subset.begin(); it != animated_subset.end(); ++it) {
103         Object *ob = *it;
104         exportAnimation(ob, animation_sampler);
105       }
106     }
107     catch (std::invalid_argument &iae) {
108       fprintf(stderr, "Animation export interrupted");
109       fprintf(stderr, "Exception was: %s", iae.what());
110     }
111
112     closeLibrary();
113
114 #if 0
115     /* TODO: If all actions shall be exported, we need to call the
116      * AnimationClipExporter which will figure out which actions
117      * need to be exported for which objects
118      */
119     if (this->export_settings->include_all_actions) {
120       AnimationClipExporter ace(eval_ctx, sw, export_settings, anim_meta);
121       ace.exportAnimationClips(sce);
122     }
123 #endif
124   }
125   return animation_count;
126 }
127
128 /* called for each exported object */
129 void AnimationExporter::exportAnimation(Object *ob, BCAnimationSampler &sampler)
130 {
131   bool container_is_open = false;
132
133   /* Transform animations (trans, rot, scale). */
134   container_is_open = open_animation_container(container_is_open, ob);
135
136   /* Now take care of the Object Animations
137    * Note: For Armatures the skeletal animation has already been exported (see above)
138    * However Armatures also can have Object animation.
139    */
140   bool export_as_matrix = this->export_settings->export_transformation_type ==
141                           BC_TRANSFORMATION_TYPE_MATRIX;
142
143   if (export_as_matrix) {
144     /* export all transform_curves as one single matrix animation */
145     export_matrix_animation(ob, sampler);
146   }
147
148   export_curve_animation_set(ob, sampler, export_as_matrix);
149
150   if (ob->type == OB_ARMATURE) {
151
152 #ifdef WITH_MORPH_ANIMATION
153     /* TODO: This needs to be handled by extra profiles, postponed for now */
154     export_morph_animation(ob);
155 #endif
156
157     /* Export skeletal animation (if any) */
158     bArmature *arm = (bArmature *)ob->data;
159     for (Bone *root_bone = (Bone *)arm->bonebase.first; root_bone; root_bone = root_bone->next)
160       export_bone_animations_recursive(ob, root_bone, sampler);
161   }
162
163   close_animation_container(container_is_open);
164 }
165
166 /*
167  * Export all animation FCurves of an Object.
168  *
169  * Note: This uses the keyframes as sample points,
170  * and exports "baked keyframes" while keeping the tangent information
171  * of the FCurves intact. This works for simple cases, but breaks
172  * especially when negative scales are involved in the animation.
173  * And when parent inverse matrices are involved (when exporting
174  * object hierarchies)
175  */
176 void AnimationExporter::export_curve_animation_set(Object *ob,
177                                                    BCAnimationSampler &sampler,
178                                                    bool export_as_matrix)
179 {
180   BCAnimationCurveMap *curves = sampler.get_curves(ob);
181   bool keep_flat_curves = this->export_settings->keep_flat_curves;
182
183   BCAnimationCurveMap::iterator it;
184   for (it = curves->begin(); it != curves->end(); ++it) {
185     BCAnimationCurve &curve = *it->second;
186     if (curve.get_channel_target() == "rotation_quaternion") {
187       /* Can not export Quaternion animation in Collada as far as i know)
188        * Maybe automatically convert to euler rotation?
189        * Discard for now. */
190       continue;
191     }
192
193     if (export_as_matrix && curve.is_transform_curve()) {
194       /* All Transform curves will be exported within a single matrix animation,
195        * see export_matrix_animation()
196        * No need to export the curves here again.
197        */
198       continue;
199     }
200
201     if (!keep_flat_curves && !curve.is_animated()) {
202       continue;
203     }
204
205     BCAnimationCurve *mcurve = get_modified_export_curve(ob, curve, *curves);
206     if (mcurve) {
207       export_curve_animation(ob, *mcurve);
208       delete mcurve;
209     }
210     else {
211       export_curve_animation(ob, curve);
212     }
213   }
214 }
215
216 void AnimationExporter::export_matrix_animation(Object *ob, BCAnimationSampler &sampler)
217 {
218   bool keep_flat_curves = this->export_settings->keep_flat_curves;
219
220   std::vector<float> frames;
221   sampler.get_object_frames(frames, ob);
222   if (frames.size() > 0) {
223     BCMatrixSampleMap samples;
224     bool is_animated = sampler.get_object_samples(samples, ob);
225     if (keep_flat_curves || is_animated) {
226       bAction *action = bc_getSceneObjectAction(ob);
227       std::string name = encode_xml(id_name(ob));
228       std::string action_name = (action == NULL) ? name + "-action" : id_name(action);
229       std::string channel_type = "transform";
230       std::string axis = "";
231       std::string id = bc_get_action_id(action_name, name, channel_type, axis);
232
233       std::string target = translate_id(name) + '/' + channel_type;
234
235       export_collada_matrix_animation(id, name, target, frames, samples);
236     }
237   }
238 }
239
240 /* Write bone animations in transform matrix sources. */
241 void AnimationExporter::export_bone_animations_recursive(Object *ob,
242                                                          Bone *bone,
243                                                          BCAnimationSampler &sampler)
244 {
245   bool keep_flat_curves = this->export_settings->keep_flat_curves;
246
247   std::vector<float> frames;
248   sampler.get_bone_frames(frames, ob, bone);
249
250   if (frames.size()) {
251     BCMatrixSampleMap samples;
252     bool is_animated = sampler.get_bone_samples(samples, ob, bone);
253     if (keep_flat_curves || is_animated) {
254       export_bone_animation(ob, bone, frames, samples);
255     }
256   }
257
258   for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next)
259     export_bone_animations_recursive(ob, child, sampler);
260 }
261
262 /**
263  * In some special cases the exported Curve needs to be replaced
264  * by a modified curve (for collada purposes)
265  * This method checks if a conversion is necessary and if applicable
266  * returns a pointer to the modified BCAnimationCurve.
267  * IMPORTANT: the modified curve must be deleted by the caller when no longer needed
268  * if no conversion is needed this method returns a NULL;
269  */
270 BCAnimationCurve *AnimationExporter::get_modified_export_curve(Object *ob,
271                                                                BCAnimationCurve &curve,
272                                                                BCAnimationCurveMap &curves)
273 {
274   std::string channel_target = curve.get_channel_target();
275   BCAnimationCurve *mcurve = NULL;
276   if (channel_target == "lens") {
277
278     /* Create an xfov curve */
279
280     BCCurveKey key(BC_ANIMATION_TYPE_CAMERA, "xfov", 0);
281     mcurve = new BCAnimationCurve(key, ob);
282
283     /* now tricky part: transform the fcurve */
284     BCValueMap lens_values;
285     curve.get_value_map(lens_values);
286
287     BCAnimationCurve *sensor_curve = NULL;
288     BCCurveKey sensor_key(BC_ANIMATION_TYPE_CAMERA, "sensor_width", 0);
289     BCAnimationCurveMap::iterator cit = curves.find(sensor_key);
290     if (cit != curves.end()) {
291       sensor_curve = cit->second;
292     }
293
294     BCValueMap::const_iterator vit;
295     for (vit = lens_values.begin(); vit != lens_values.end(); ++vit) {
296       int frame = vit->first;
297       float lens_value = vit->second;
298
299       float sensor_value;
300       if (sensor_curve) {
301         sensor_value = sensor_curve->get_value(frame);
302       }
303       else {
304         sensor_value = ((Camera *)ob->data)->sensor_x;
305       }
306       float value = RAD2DEGF(focallength_to_fov(lens_value, sensor_value));
307       mcurve->add_value(value, frame);
308     }
309     /* to reset the handles */
310     mcurve->clean_handles();
311   }
312   return mcurve;
313 }
314
315 void AnimationExporter::export_curve_animation(Object *ob, BCAnimationCurve &curve)
316 {
317   std::string channel_target = curve.get_channel_target();
318
319   /*
320    * Some curves can not be exported as is and need some conversion
321    * For more information see implementation oif get_modified_export_curve()
322    * note: if mcurve is not NULL then it must be deleted at end of this method;
323    */
324
325   int channel_index = curve.get_channel_index();
326   /* RGB or XYZ or "" */
327   std::string axis = get_axis_name(channel_target, channel_index);
328
329   std::string action_name;
330   bAction *action = bc_getSceneObjectAction(ob);
331   action_name = (action) ? id_name(action) : "constraint_anim";
332
333   const std::string curve_name = encode_xml(curve.get_animation_name(ob));
334   std::string id = bc_get_action_id(action_name, curve_name, channel_target, axis, ".");
335
336   std::string collada_target = translate_id(curve_name);
337
338   if (curve.is_of_animation_type(BC_ANIMATION_TYPE_MATERIAL)) {
339     int material_index = curve.get_subindex();
340     Material *ma = give_current_material(ob, material_index + 1);
341     if (ma) {
342       collada_target = translate_id(id_name(ma)) + "-effect/common/" +
343                        get_collada_sid(curve, axis);
344     }
345   }
346   else {
347     collada_target += "/" + get_collada_sid(curve, axis);
348   }
349
350   export_collada_curve_animation(id, curve_name, collada_target, axis, curve);
351 }
352
353 void AnimationExporter::export_bone_animation(Object *ob,
354                                               Bone *bone,
355                                               BCFrames &frames,
356                                               BCMatrixSampleMap &samples)
357 {
358   bAction *action = bc_getSceneObjectAction(ob);
359   std::string bone_name(bone->name);
360   std::string name = encode_xml(id_name(ob));
361   std::string id = bc_get_action_id(id_name(action), name, bone_name, "pose_matrix");
362   std::string target = translate_id(id_name(ob) + "_" + bone_name) + "/transform";
363
364   export_collada_matrix_animation(id, name, target, frames, samples);
365 }
366
367 bool AnimationExporter::is_bone_deform_group(Bone *bone)
368 {
369   bool is_def;
370   /* Check if current bone is deform */
371   if ((bone->flag & BONE_NO_DEFORM) == 0)
372     return true;
373   /* Check child bones */
374   else {
375     for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next) {
376       /* loop through all the children until deform bone is found, and then return */
377       is_def = is_bone_deform_group(child);
378       if (is_def)
379         return true;
380     }
381   }
382   /* no deform bone found in children also */
383   return false;
384 }
385
386 void AnimationExporter::export_collada_curve_animation(std::string id,
387                                                        std::string name,
388                                                        std::string collada_target,
389                                                        std::string axis,
390                                                        BCAnimationCurve &curve)
391 {
392   BCFrames frames;
393   BCValues values;
394   curve.get_frames(frames);
395   curve.get_values(values);
396   std::string channel_target = curve.get_channel_target();
397
398   fprintf(
399       stdout, "Export animation curve %s (%d control points)\n", id.c_str(), int(frames.size()));
400   openAnimation(id, name);
401   BC_animation_source_type source_type = (curve.is_rotation_curve()) ? BC_SOURCE_TYPE_ANGLE :
402                                                                        BC_SOURCE_TYPE_VALUE;
403
404   std::string input_id = collada_source_from_values(
405       BC_SOURCE_TYPE_TIMEFRAME, COLLADASW::InputSemantic::INPUT, frames, id, axis);
406   std::string output_id = collada_source_from_values(
407       source_type, COLLADASW::InputSemantic::OUTPUT, values, id, axis);
408
409   bool has_tangents = false;
410   std::string interpolation_id;
411   if (this->export_settings->keep_smooth_curves)
412     interpolation_id = collada_interpolation_source(curve, id, axis, &has_tangents);
413   else
414     interpolation_id = collada_linear_interpolation_source(frames.size(), id);
415
416   std::string intangent_id;
417   std::string outtangent_id;
418   if (has_tangents) {
419     intangent_id = collada_tangent_from_curve(
420         COLLADASW::InputSemantic::IN_TANGENT, curve, id, axis);
421     outtangent_id = collada_tangent_from_curve(
422         COLLADASW::InputSemantic::OUT_TANGENT, curve, id, axis);
423   }
424
425   std::string sampler_id = std::string(id) + SAMPLER_ID_SUFFIX;
426
427   COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id);
428
429   sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(EMPTY_STRING, input_id));
430   sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(EMPTY_STRING, output_id));
431   sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION,
432                    COLLADABU::URI(EMPTY_STRING, interpolation_id));
433
434   if (has_tangents) {
435     sampler.addInput(COLLADASW::InputSemantic::IN_TANGENT,
436                      COLLADABU::URI(EMPTY_STRING, intangent_id));
437     sampler.addInput(COLLADASW::InputSemantic::OUT_TANGENT,
438                      COLLADABU::URI(EMPTY_STRING, outtangent_id));
439   }
440
441   addSampler(sampler);
442   addChannel(COLLADABU::URI(EMPTY_STRING, sampler_id), collada_target);
443
444   closeAnimation();
445 }
446
447 void AnimationExporter::export_collada_matrix_animation(std::string id,
448                                                         std::string name,
449                                                         std::string target,
450                                                         BCFrames &frames,
451                                                         BCMatrixSampleMap &samples)
452 {
453   fprintf(
454       stdout, "Export animation matrix %s (%d control points)\n", id.c_str(), int(frames.size()));
455
456   openAnimationWithClip(id, name);
457
458   std::string input_id = collada_source_from_values(
459       BC_SOURCE_TYPE_TIMEFRAME, COLLADASW::InputSemantic::INPUT, frames, id, "");
460   std::string output_id = collada_source_from_values(samples, id);
461   std::string interpolation_id = collada_linear_interpolation_source(frames.size(), id);
462
463   std::string sampler_id = std::string(id) + SAMPLER_ID_SUFFIX;
464   COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id);
465
466   sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(EMPTY_STRING, input_id));
467   sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(EMPTY_STRING, output_id));
468   sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION,
469                    COLLADABU::URI(EMPTY_STRING, interpolation_id));
470
471   /* Matrix animation has no tangents */
472
473   addSampler(sampler);
474   addChannel(COLLADABU::URI(EMPTY_STRING, sampler_id), target);
475
476   closeAnimation();
477 }
478
479 std::string AnimationExporter::get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic)
480 {
481   switch (semantic) {
482     case COLLADASW::InputSemantic::INPUT:
483       return INPUT_SOURCE_ID_SUFFIX;
484     case COLLADASW::InputSemantic::OUTPUT:
485       return OUTPUT_SOURCE_ID_SUFFIX;
486     case COLLADASW::InputSemantic::INTERPOLATION:
487       return INTERPOLATION_SOURCE_ID_SUFFIX;
488     case COLLADASW::InputSemantic::IN_TANGENT:
489       return INTANGENT_SOURCE_ID_SUFFIX;
490     case COLLADASW::InputSemantic::OUT_TANGENT:
491       return OUTTANGENT_SOURCE_ID_SUFFIX;
492     default:
493       break;
494   }
495   return "";
496 }
497
498 void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNameList &param,
499                                               COLLADASW::InputSemantic::Semantics semantic,
500                                               bool is_rot,
501                                               const std::string axis,
502                                               bool transform)
503 {
504   switch (semantic) {
505     case COLLADASW::InputSemantic::INPUT:
506       param.push_back("TIME");
507       break;
508     case COLLADASW::InputSemantic::OUTPUT:
509       if (is_rot) {
510         param.push_back("ANGLE");
511       }
512       else {
513         if (axis != "") {
514           param.push_back(axis);
515         }
516         else if (transform) {
517           param.push_back("TRANSFORM");
518         }
519         else {
520           /* assumes if axis isn't specified all axises are added */
521           param.push_back("X");
522           param.push_back("Y");
523           param.push_back("Z");
524         }
525       }
526       break;
527     case COLLADASW::InputSemantic::IN_TANGENT:
528     case COLLADASW::InputSemantic::OUT_TANGENT:
529       param.push_back("X");
530       param.push_back("Y");
531       break;
532     default:
533       break;
534   }
535 }
536
537 std::string AnimationExporter::collada_tangent_from_curve(
538     COLLADASW::InputSemantic::Semantics semantic,
539     BCAnimationCurve &curve,
540     const std::string &anim_id,
541     std::string axis_name)
542 {
543   Scene *scene = blender_context.get_scene();
544   std::string channel = curve.get_channel_target();
545
546   const std::string source_id = anim_id + get_semantic_suffix(semantic);
547
548   bool is_angle = (bc_startswith(channel, "rotation") || channel == "spot_size");
549
550   COLLADASW::FloatSourceF source(mSW);
551   source.setId(source_id);
552   source.setArrayId(source_id + ARRAY_ID_SUFFIX);
553   source.setAccessorCount(curve.sample_count());
554   source.setAccessorStride(2);
555
556   COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
557   add_source_parameters(param, semantic, is_angle, axis_name, false);
558
559   source.prepareToAppendValues();
560
561   const FCurve *fcu = curve.get_fcurve();
562   int tangent = (semantic == COLLADASW::InputSemantic::IN_TANGENT) ? 0 : 2;
563
564   for (int i = 0; i < fcu->totvert; ++i) {
565     BezTriple &bezt = fcu->bezt[i];
566
567     float sampled_time = bezt.vec[tangent][0];
568     float sampled_val = bezt.vec[tangent][1];
569
570     if (is_angle) {
571       sampled_val = RAD2DEGF(sampled_val);
572     }
573
574     source.appendValues(FRA2TIME(sampled_time));
575     source.appendValues(sampled_val);
576   }
577   source.finish();
578   return source_id;
579 }
580
581 std::string AnimationExporter::collada_source_from_values(
582     BC_animation_source_type source_type,
583     COLLADASW::InputSemantic::Semantics semantic,
584     std::vector<float> &values,
585     const std::string &anim_id,
586     const std::string axis_name)
587 {
588   Scene *scene = blender_context.get_scene();
589   /* T can be float, int or double */
590
591   int stride = 1;
592   int entry_count = values.size() / stride;
593   std::string source_id = anim_id + get_semantic_suffix(semantic);
594
595   COLLADASW::FloatSourceF source(mSW);
596   source.setId(source_id);
597   source.setArrayId(source_id + ARRAY_ID_SUFFIX);
598   source.setAccessorCount(entry_count);
599   source.setAccessorStride(stride);
600
601   COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
602   add_source_parameters(param, semantic, source_type == BC_SOURCE_TYPE_ANGLE, axis_name, false);
603
604   source.prepareToAppendValues();
605
606   for (int i = 0; i < entry_count; i++) {
607     float val = values[i];
608     switch (source_type) {
609       case BC_SOURCE_TYPE_TIMEFRAME:
610         val = FRA2TIME(val);
611         break;
612       case BC_SOURCE_TYPE_ANGLE:
613         val = RAD2DEGF(val);
614         break;
615       default:
616         break;
617     }
618     source.appendValues(val);
619   }
620
621   source.finish();
622
623   return source_id;
624 }
625
626 /**
627  * Create a collada matrix source for a set of samples.
628  */
629 std::string AnimationExporter::collada_source_from_values(BCMatrixSampleMap &samples,
630                                                           const std::string &anim_id)
631 {
632   COLLADASW::InputSemantic::Semantics semantic = COLLADASW::InputSemantic::OUTPUT;
633   std::string source_id = anim_id + get_semantic_suffix(semantic);
634
635   COLLADASW::Float4x4Source source(mSW);
636   source.setId(source_id);
637   source.setArrayId(source_id + ARRAY_ID_SUFFIX);
638   source.setAccessorCount(samples.size());
639   source.setAccessorStride(16);
640
641   COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
642   add_source_parameters(param, semantic, false, "", true);
643
644   source.prepareToAppendValues();
645
646   BCMatrixSampleMap::iterator it;
647   /* could be made configurable */
648   int precision = (this->export_settings->limit_precision) ? 6 : -1;
649   for (it = samples.begin(); it != samples.end(); it++) {
650     const BCMatrix *sample = it->second;
651     double daemat[4][4];
652     sample->get_matrix(daemat, true, precision);
653     source.appendValues(daemat);
654   }
655
656   source.finish();
657   return source_id;
658 }
659
660 std::string AnimationExporter::collada_interpolation_source(const BCAnimationCurve &curve,
661                                                             const std::string &anim_id,
662                                                             const std::string axis,
663                                                             bool *has_tangents)
664 {
665   std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION);
666
667   COLLADASW::NameSource source(mSW);
668   source.setId(source_id);
669   source.setArrayId(source_id + ARRAY_ID_SUFFIX);
670   source.setAccessorCount(curve.sample_count());
671   source.setAccessorStride(1);
672
673   COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
674   param.push_back("INTERPOLATION");
675
676   source.prepareToAppendValues();
677
678   *has_tangents = false;
679
680   std::vector<float> frames;
681   curve.get_frames(frames);
682
683   for (unsigned int i = 0; i < curve.sample_count(); i++) {
684     float frame = frames[i];
685     int ipo = curve.get_interpolation_type(frame);
686     if (ipo == BEZT_IPO_BEZ) {
687       source.appendValues(BEZIER_NAME);
688       *has_tangents = true;
689     }
690     else if (ipo == BEZT_IPO_CONST) {
691       source.appendValues(STEP_NAME);
692     }
693     else {
694       /* BEZT_IPO_LIN */
695       source.appendValues(LINEAR_NAME);
696     }
697   }
698   /* unsupported? -- HERMITE, CARDINAL, BSPLINE, NURBS */
699
700   source.finish();
701
702   return source_id;
703 }
704
705 std::string AnimationExporter::collada_linear_interpolation_source(int tot,
706                                                                    const std::string &anim_id)
707 {
708   std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION);
709
710   COLLADASW::NameSource source(mSW);
711   source.setId(source_id);
712   source.setArrayId(source_id + ARRAY_ID_SUFFIX);
713   source.setAccessorCount(tot);
714   source.setAccessorStride(1);
715
716   COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
717   param.push_back("INTERPOLATION");
718
719   source.prepareToAppendValues();
720
721   for (int i = 0; i < tot; i++) {
722     source.appendValues(LINEAR_NAME);
723   }
724
725   source.finish();
726
727   return source_id;
728 }
729
730 const std::string AnimationExporter::get_collada_name(std::string channel_target) const
731 {
732   /*
733    * Translation table to map FCurve animation types to Collada animation.
734    * Todo: Maybe we can keep the names from the fcurves here instead of
735    * mapping. However this is what i found in the old code. So keep
736    * this map for now.
737    */
738   static std::map<std::string, std::string> BC_CHANNEL_BLENDER_TO_COLLADA = {
739       {"rotation", "rotation"},
740       {"rotation_euler", "rotation"},
741       {"rotation_quaternion", "rotation"},
742       {"scale", "scale"},
743       {"location", "location"},
744
745       /* Materials */
746       {"specular_color", "specular"},
747       {"diffuse_color", "diffuse"},
748       {"ior", "index_of_refraction"},
749       {"specular_hardness", "specular_hardness"},
750       {"alpha", "alpha"},
751
752       /* Lights */
753       {"color", "color"},
754       {"fall_off_angle", "falloff_angle"},
755       {"spot_size", "falloff_angle"},
756       {"fall_off_exponent", "falloff_exponent"},
757       {"spot_blend", "falloff_exponent"},
758       /* Special blender profile (todo: make this more elegant). */
759       {"blender/blender_dist", "blender/blender_dist"},
760       /* Special blender profile (todo: make this more elegant). */
761       {"distance", "blender/blender_dist"},
762
763       /* Cameras */
764       {"lens", "xfov"},
765       {"xfov", "xfov"},
766       {"xmag", "xmag"},
767       {"zfar", "zfar"},
768       {"znear", "znear"},
769       {"ortho_scale", "xmag"},
770       {"clip_end", "zfar"},
771       {"clip_start", "znear"}};
772
773   std::map<std::string, std::string>::iterator name_it = BC_CHANNEL_BLENDER_TO_COLLADA.find(
774       channel_target);
775   if (name_it == BC_CHANNEL_BLENDER_TO_COLLADA.end())
776     return "";
777
778   std::string tm_name = name_it->second;
779   return tm_name;
780 }
781
782 /*
783  * Assign sid of the animated parameter or transform for rotation,
784  * axis name is always appended and the value of append_axis is ignored
785  */
786 std::string AnimationExporter::get_collada_sid(const BCAnimationCurve &curve,
787                                                const std::string axis_name)
788 {
789   std::string channel_target = curve.get_channel_target();
790   std::string tm_name = get_collada_name(channel_target);
791
792   bool is_angle = curve.is_rotation_curve();
793
794   if (tm_name.size()) {
795     if (is_angle)
796       return tm_name + std::string(axis_name) + ".ANGLE";
797     else if (axis_name != "")
798       return tm_name + "." + std::string(axis_name);
799     else
800       return tm_name;
801   }
802
803   return tm_name;
804 }
805
806 #ifdef WITH_MORPH_ANIMATION
807 /* TODO: This function needs to be implemented similar to the material animation export
808  * So we have to update BCSample for this to work. */
809 void AnimationExporter::export_morph_animation(Object *ob, BCAnimationSampler &sampler)
810 {
811   FCurve *fcu;
812   Key *key = BKE_key_from_object(ob);
813   if (!key)
814     return;
815
816   if (key->adt && key->adt->action) {
817     fcu = (FCurve *)key->adt->action->curves.first;
818
819     while (fcu) {
820       BC_animation_transform_type tm_type = get_transform_type(fcu->rna_path);
821
822       create_keyframed_animation(ob, fcu, tm_type, true, sampler);
823
824       fcu = fcu->next;
825     }
826   }
827 }
828 #endif