Merge branch 'master' into blender2.8
authorCampbell Barton <ideasman42@gmail.com>
Sat, 24 Nov 2018 21:01:30 +0000 (08:01 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 24 Nov 2018 21:01:53 +0000 (08:01 +1100)
18 files changed:
1  2 
intern/cycles/render/scene.cpp
intern/ghost/intern/GHOST_SystemX11.cpp
source/blender/blenkernel/intern/main.c
source/blender/blenkernel/intern/object.c
source/blender/collada/AnimationCurveCache.cpp
source/blender/collada/AnimationCurveCache.h
source/blender/collada/AnimationExporter.cpp
source/blender/collada/AnimationExporter.h
source/blender/collada/BCAnimationCurve.cpp
source/blender/collada/BCAnimationSampler.cpp
source/blender/collada/BCAnimationSampler.h
source/blender/collada/BCSampleData.cpp
source/blender/collada/ErrorHandler.cpp
source/blender/collada/SceneExporter.cpp
source/blender/collada/collada_utils.cpp
source/blender/collada/collada_utils.h
source/blender/draw/intern/draw_cache_impl_metaball.c
source/blender/editors/io/io_collada.c

Simple merge
index 6291e35,0000000..c063af4
mode 100644,000000..100644
--- /dev/null
@@@ -1,446 -1,0 +1,445 @@@
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 + * All rights reserved.
 + *
 + * The Original Code is: all of this file.
 + *
 + * Contributor(s): none yet.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file blender/blenkernel/intern/main.c
 + *  \ingroup bke
 + *
 + * Contains management of Main database itself.
 + */
 +
 +#include <string.h>
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_blenlib.h"
 +#include "BLI_ghash.h"
 +#include "BLI_mempool.h"
 +#include "BLI_threads.h"
 +
 +#include "DNA_ID.h"
 +
 +#include "BKE_global.h"
 +#include "BKE_library.h"
 +#include "BKE_library_query.h"
 +#include "BKE_main.h"
 +
 +#include "IMB_imbuf.h"
 +#include "IMB_imbuf_types.h"
 +
 +Main *BKE_main_new(void)
 +{
 +      Main *bmain = MEM_callocN(sizeof(Main), "new main");
 +      bmain->lock = MEM_mallocN(sizeof(SpinLock), "main lock");
 +      BLI_spin_init((SpinLock *)bmain->lock);
 +      return bmain;
 +}
 +
 +void BKE_main_free(Main *mainvar)
 +{
 +      /* also call when reading a file, erase all, etc */
 +      ListBase *lbarray[MAX_LIBARRAY];
 +      int a;
 +
 +      MEM_SAFE_FREE(mainvar->blen_thumb);
 +
 +      a = set_listbasepointers(mainvar, lbarray);
 +      while (a--) {
 +              ListBase *lb = lbarray[a];
 +              ID *id;
 +
 +              while ( (id = lb->first) ) {
 +#if 1
 +                      BKE_libblock_free_ex(mainvar, id, false, false);
 +#else
 +                      /* errors freeing ID's can be hard to track down,
 +                       * enable this so valgrind will give the line number in its error log */
 +                      switch (a) {
 +                              case   0: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   1: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   2: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   3: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   4: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   5: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   6: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   7: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   8: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case   9: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  10: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  11: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  12: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  13: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  14: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  15: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  16: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  17: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  18: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  19: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  20: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  21: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  22: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  23: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  24: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  25: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  26: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  27: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  28: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  29: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  30: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  31: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  32: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  33: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              case  34: BKE_libblock_free_ex(mainvar, id, false, false); break;
 +                              default:
 +                                      BLI_assert(0);
 +                                      break;
 +                      }
 +#endif
 +              }
 +      }
 +
 +      if (mainvar->relations) {
 +              BKE_main_relations_free(mainvar);
 +      }
 +
 +      BLI_spin_end((SpinLock *)mainvar->lock);
 +      MEM_freeN(mainvar->lock);
 +      MEM_freeN(mainvar);
 +}
 +
 +void BKE_main_lock(struct Main *bmain)
 +{
 +      BLI_spin_lock((SpinLock *) bmain->lock);
 +}
 +
 +void BKE_main_unlock(struct Main *bmain)
 +{
 +      BLI_spin_unlock((SpinLock *) bmain->lock);
 +}
 +
 +
 +static int main_relations_create_cb(void *user_data, ID *id_self, ID **id_pointer, int cb_flag)
 +{
 +      MainIDRelations *rel = user_data;
 +
 +      if (*id_pointer) {
 +              MainIDRelationsEntry *entry, **entry_p;
 +
 +              entry = BLI_mempool_alloc(rel->entry_pool);
 +              if (BLI_ghash_ensure_p(rel->id_user_to_used, id_self, (void ***)&entry_p)) {
 +                      entry->next = *entry_p;
 +              }
 +              else {
 +                      entry->next = NULL;
 +              }
 +              entry->id_pointer = id_pointer;
 +              entry->usage_flag = cb_flag;
 +              *entry_p = entry;
 +
 +              entry = BLI_mempool_alloc(rel->entry_pool);
 +              if (BLI_ghash_ensure_p(rel->id_used_to_user, *id_pointer, (void ***)&entry_p)) {
 +                      entry->next = *entry_p;
 +              }
 +              else {
 +                      entry->next = NULL;
 +              }
 +              entry->id_pointer = (ID **)id_self;
 +              entry->usage_flag = cb_flag;
 +              *entry_p = entry;
 +      }
 +
 +      return IDWALK_RET_NOP;
 +}
 +
 +/** Generate the mappings between used IDs and their users, and vice-versa. */
 +void BKE_main_relations_create(Main *bmain)
 +{
 +      ListBase *lbarray[MAX_LIBARRAY];
 +      ID *id;
 +      int a;
 +
 +      if (bmain->relations != NULL) {
 +              BKE_main_relations_free(bmain);
 +      }
 +
 +      bmain->relations = MEM_mallocN(sizeof(*bmain->relations), __func__);
 +      bmain->relations->id_used_to_user = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +      bmain->relations->id_user_to_used = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
 +      bmain->relations->entry_pool = BLI_mempool_create(sizeof(MainIDRelationsEntry), 128, 128, BLI_MEMPOOL_NOP);
 +
 +      for (a = set_listbasepointers(bmain, lbarray); a--; ) {
 +              for (id = lbarray[a]->first; id; id = id->next) {
 +                      BKE_library_foreach_ID_link(NULL, id, main_relations_create_cb, bmain->relations, IDWALK_READONLY);
 +              }
 +      }
 +}
 +
 +void BKE_main_relations_free(Main *bmain)
 +{
 +      if (bmain->relations) {
 +              if (bmain->relations->id_used_to_user) {
 +                      BLI_ghash_free(bmain->relations->id_used_to_user, NULL, NULL);
 +              }
 +              if (bmain->relations->id_user_to_used) {
 +                      BLI_ghash_free(bmain->relations->id_user_to_used, NULL, NULL);
 +              }
 +              BLI_mempool_destroy(bmain->relations->entry_pool);
 +              MEM_freeN(bmain->relations);
 +              bmain->relations = NULL;
 +      }
 +}
 +
 +/**
 + * Generates a raw .blend file thumbnail data from given image.
 + *
 + * \param bmain If not NULL, also store generated data in this Main.
 + * \param img ImBuf image to generate thumbnail data from.
 + * \return The generated .blend file raw thumbnail data.
 + */
 +BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img)
 +{
 +      BlendThumbnail *data = NULL;
 +
 +      if (bmain) {
 +              MEM_SAFE_FREE(bmain->blen_thumb);
 +      }
 +
 +      if (img) {
 +              const size_t sz = BLEN_THUMB_MEMSIZE(img->x, img->y);
 +              data = MEM_mallocN(sz, __func__);
 +
 +              IMB_rect_from_float(img);  /* Just in case... */
 +              data->width = img->x;
 +              data->height = img->y;
 +              memcpy(data->rect, img->rect, sz - sizeof(*data));
 +      }
 +
 +      if (bmain) {
 +              bmain->blen_thumb = data;
 +      }
 +      return data;
 +}
 +
 +/**
 + * Generates an image from raw .blend file thumbnail \a data.
 + *
 + * \param bmain Use this bmain->blen_thumb data if given \a data is NULL.
 + * \param data Raw .blend file thumbnail data.
 + * \return An ImBuf from given data, or NULL if invalid.
 + */
 +ImBuf *BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data)
 +{
 +      ImBuf *img = NULL;
 +
 +      if (!data && bmain) {
 +              data = bmain->blen_thumb;
 +      }
 +
 +      if (data) {
 +              /* Note: we cannot use IMB_allocFromBuffer(), since it tries to dupalloc passed buffer, which will fail
 +               *       here (we do not want to pass the first two ints!). */
 +              img = IMB_allocImBuf((unsigned int)data->width, (unsigned int)data->height, 32, IB_rect | IB_metadata);
 +              memcpy(img->rect, data->rect, BLEN_THUMB_MEMSIZE(data->width, data->height) - sizeof(*data));
 +      }
 +
 +      return img;
 +}
 +
 +/**
 + * Generates an empty (black) thumbnail for given Main.
 + */
 +void BKE_main_thumbnail_create(struct Main *bmain)
 +{
 +      MEM_SAFE_FREE(bmain->blen_thumb);
 +
 +      bmain->blen_thumb = MEM_callocN(BLEN_THUMB_MEMSIZE(BLEN_THUMB_SIZE, BLEN_THUMB_SIZE), __func__);
 +      bmain->blen_thumb->width = BLEN_THUMB_SIZE;
 +      bmain->blen_thumb->height = BLEN_THUMB_SIZE;
 +}
 +
 +/**
 + * Return filepath of given \a main.
 + */
 +const char *BKE_main_blendfile_path(const Main *bmain)
 +{
 +      return bmain->name;
 +}
 +
 +/**
 + * Return filepath of global main (G_MAIN).
 + *
 + * \warning Usage is not recommended, you should always try to get a valid Main pointer from context...
 + */
 +const char *BKE_main_blendfile_path_from_global(void)
 +{
 +      return BKE_main_blendfile_path(G_MAIN);
 +}
 +
 +/**
 + * \return A pointer to the \a ListBase of given \a bmain for requested \a type ID type.
 + */
 +ListBase *which_libbase(Main *bmain, short type)
 +{
 +      switch ((ID_Type)type) {
 +              case ID_SCE:
 +                      return &(bmain->scene);
 +              case ID_LI:
 +                      return &(bmain->library);
 +              case ID_OB:
 +                      return &(bmain->object);
 +              case ID_ME:
 +                      return &(bmain->mesh);
 +              case ID_CU:
 +                      return &(bmain->curve);
 +              case ID_MB:
 +                      return &(bmain->mball);
 +              case ID_MA:
 +                      return &(bmain->mat);
 +              case ID_TE:
 +                      return &(bmain->tex);
 +              case ID_IM:
 +                      return &(bmain->image);
 +              case ID_LT:
 +                      return &(bmain->latt);
 +              case ID_LA:
 +                      return &(bmain->lamp);
 +              case ID_CA:
 +                      return &(bmain->camera);
 +              case ID_IP:
 +                      return &(bmain->ipo);
 +              case ID_KE:
 +                      return &(bmain->key);
 +              case ID_WO:
 +                      return &(bmain->world);
 +              case ID_SCR:
 +                      return &(bmain->screen);
 +              case ID_VF:
 +                      return &(bmain->vfont);
 +              case ID_TXT:
 +                      return &(bmain->text);
 +              case ID_SPK:
 +                      return &(bmain->speaker);
 +              case ID_LP:
 +                      return &(bmain->lightprobe);
 +              case ID_SO:
 +                      return &(bmain->sound);
 +              case ID_GR:
 +                      return &(bmain->collection);
 +              case ID_AR:
 +                      return &(bmain->armature);
 +              case ID_AC:
 +                      return &(bmain->action);
 +              case ID_NT:
 +                      return &(bmain->nodetree);
 +              case ID_BR:
 +                      return &(bmain->brush);
 +              case ID_PA:
 +                      return &(bmain->particle);
 +              case ID_WM:
 +                      return &(bmain->wm);
 +              case ID_GD:
 +                      return &(bmain->gpencil);
 +              case ID_MC:
 +                      return &(bmain->movieclip);
 +              case ID_MSK:
 +                      return &(bmain->mask);
 +              case ID_LS:
 +                      return &(bmain->linestyle);
 +              case ID_PAL:
 +                      return &(bmain->palettes);
 +              case ID_PC:
 +                      return &(bmain->paintcurves);
 +              case ID_CF:
 +                      return &(bmain->cachefiles);
 +              case ID_WS:
 +                      return &(bmain->workspaces);
 +      }
 +      return NULL;
 +}
 +
 +/**
 + * puts into array *lb pointers to all the ListBase structs in main,
 + * and returns the number of them as the function result. This is useful for
 + * generic traversal of all the blocks in a Main (by traversing all the
 + * lists in turn), without worrying about block types.
 + *
 + * \note MAX_LIBARRAY define should match this code */
 +int set_listbasepointers(Main *bmain, ListBase **lb)
 +{
 +      /* BACKWARDS! also watch order of free-ing! (mesh<->mat), first items freed last.
 +       * This is important because freeing data decreases usercounts of other datablocks,
 +       * if this data is its self freed it can crash. */
 +      lb[INDEX_ID_LI] = &(bmain->library);  /* Libraries may be accessed from pretty much any other ID... */
 +      lb[INDEX_ID_IP] = &(bmain->ipo);
 +      lb[INDEX_ID_AC] = &(bmain->action); /* moved here to avoid problems when freeing with animato (aligorith) */
 +      lb[INDEX_ID_KE] = &(bmain->key);
 +      lb[INDEX_ID_PAL] = &(bmain->palettes); /* referenced by gpencil, so needs to be before that to avoid crashes */
 +      lb[INDEX_ID_GD] = &(bmain->gpencil); /* referenced by nodes, objects, view, scene etc, before to free after. */
 +      lb[INDEX_ID_NT] = &(bmain->nodetree);
 +      lb[INDEX_ID_IM] = &(bmain->image);
 +      lb[INDEX_ID_TE] = &(bmain->tex);
 +      lb[INDEX_ID_MA] = &(bmain->mat);
 +      lb[INDEX_ID_VF] = &(bmain->vfont);
 +
 +      /* Important!: When adding a new object type,
 +       * the specific data should be inserted here
 +       */
 +
 +      lb[INDEX_ID_AR] = &(bmain->armature);
 +
 +      lb[INDEX_ID_CF] = &(bmain->cachefiles);
 +      lb[INDEX_ID_ME] = &(bmain->mesh);
 +      lb[INDEX_ID_CU] = &(bmain->curve);
 +      lb[INDEX_ID_MB] = &(bmain->mball);
 +
 +      lb[INDEX_ID_LT] = &(bmain->latt);
 +      lb[INDEX_ID_LA] = &(bmain->lamp);
 +      lb[INDEX_ID_CA] = &(bmain->camera);
 +
 +      lb[INDEX_ID_TXT] = &(bmain->text);
 +      lb[INDEX_ID_SO]  = &(bmain->sound);
 +      lb[INDEX_ID_GR]  = &(bmain->collection);
 +      lb[INDEX_ID_PAL] = &(bmain->palettes);
 +      lb[INDEX_ID_PC]  = &(bmain->paintcurves);
 +      lb[INDEX_ID_BR]  = &(bmain->brush);
 +      lb[INDEX_ID_PA]  = &(bmain->particle);
 +      lb[INDEX_ID_SPK] = &(bmain->speaker);
 +      lb[INDEX_ID_LP]  = &(bmain->lightprobe);
 +
 +      lb[INDEX_ID_WO]  = &(bmain->world);
 +      lb[INDEX_ID_MC]  = &(bmain->movieclip);
 +      lb[INDEX_ID_SCR] = &(bmain->screen);
 +      lb[INDEX_ID_OB]  = &(bmain->object);
 +      lb[INDEX_ID_LS]  = &(bmain->linestyle); /* referenced by scenes */
 +      lb[INDEX_ID_SCE] = &(bmain->scene);
 +      lb[INDEX_ID_WS]  = &(bmain->workspaces); /* before wm, so it's freed after it! */
 +      lb[INDEX_ID_WM]  = &(bmain->wm);
 +      lb[INDEX_ID_MSK] = &(bmain->mask);
 +
 +      lb[INDEX_ID_NULL] = NULL;
 +
 +      return (MAX_LIBARRAY - 1);
 +}
@@@ -4041,21 -3697,3 +4041,21 @@@ bool BKE_object_modifier_update_subfram
  
        return false;
  }
- }
 +
 +bool BKE_image_empty_visible_in_view3d(const Object *ob, const RegionView3D *rv3d)
 +{
 +      int visibility_flag = ob->empty_image_visibility_flag;
 +
 +      if ((visibility_flag & OB_EMPTY_IMAGE_VISIBLE_BACKSIDE) == 0) {
 +              if (dot_v3v3((float *)&ob->obmat[2], (float *)&rv3d->viewinv[2]) < 0.0f) {
 +                      return false;
 +              }
 +      }
 +
 +      if (rv3d->is_persp) {
 +              return visibility_flag & OB_EMPTY_IMAGE_VISIBLE_PERSPECTIVE;
 +      }
 +      else {
 +              return visibility_flag & OB_EMPTY_IMAGE_VISIBLE_ORTHOGRAPHIC;
 +      }
++}
index 3ad921b,0000000..f580ffc
mode 100644,000000..100644
--- /dev/null
@@@ -1,391 -1,0 +1,391 @@@
-                       /* When this SamplePoint is not for a Bone, 
 +/*
 +* ***** BEGIN GPL LICENSE BLOCK *****
 +*
 +* This program is free software; you can redistribute it and/or
 +* modify it under the terms of the GNU General Public License
 +* as published by the Free Software Foundation; either version 2
 +* of the License, or (at your option) any later version.
 +*
 +* This program is distributed in the hope that it will be useful,
 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +* GNU General Public License for more details.
 +*
 +* You should have received a copy of the GNU General Public License
 +* along with this program; if not, write to the Free Software Foundation,
 +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +*
 +* Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Jan Diederich, Tod Liverseed.
 +*
 +* ***** END GPL LICENSE BLOCK *****
 +*/
 +
 +#include "AnimationCurveCache.h"
 +
 +extern "C" {
 +#include "BKE_action.h"
 +#include "BLI_listbase.h"
 +}
 +
 +AnimationCurveCache::AnimationCurveCache(bContext *C):
 +      mContext(C)
 +{
 +}
 +
 +AnimationCurveCache::~AnimationCurveCache()
 +{
 +      clear_cache();
 +}
 +
 +void AnimationCurveCache::clear_cache()
 +{
 +
 +}
 +
 +void AnimationCurveCache::clear_cache(Object *ob)
 +{
 +
 +}
 +
 +void AnimationCurveCache::create_curves(Object *ob)
 +{
 +
 +}
 +
 +void AnimationCurveCache::addObject(Object *ob)
 +{
 +      cached_objects.push_back(ob);
 +}
 +
 +bool AnimationCurveCache::bone_matrix_local_get(Object *ob, Bone *bone, float(&mat)[4][4], bool for_opensim)
 +{
 +
 +      /* Ok, lets be super cautious and check if the bone exists */
 +      bPose *pose = ob->pose;
 +      bPoseChannel *pchan = BKE_pose_channel_find_name(pose, bone->name);
 +      if (!pchan) {
 +              return false;
 +      }
 +
 +      bAction *action = bc_getSceneObjectAction(ob);
 +      bPoseChannel *parchan = pchan->parent;
 +      enable_fcurves(action, bone->name);
 +      float ipar[4][4];
 +
 +      if (bone->parent) {
 +              invert_m4_m4(ipar, parchan->pose_mat);
 +              mul_m4_m4m4(mat, ipar, pchan->pose_mat);
 +      }
 +      else
 +              copy_m4_m4(mat, pchan->pose_mat);
 +
 +      /* OPEN_SIM_COMPATIBILITY
 +      * AFAIK animation to second life is via BVH, but no
 +      * reason to not have the collada-animation be correct
 +      */
 +      if (for_opensim) {
 +              float temp[4][4];
 +              copy_m4_m4(temp, bone->arm_mat);
 +              temp[3][0] = temp[3][1] = temp[3][2] = 0.0f;
 +              invert_m4(temp);
 +
 +              mul_m4_m4m4(mat, mat, temp);
 +
 +              if (bone->parent) {
 +                      copy_m4_m4(temp, bone->parent->arm_mat);
 +                      temp[3][0] = temp[3][1] = temp[3][2] = 0.0f;
 +
 +                      mul_m4_m4m4(mat, temp, mat);
 +              }
 +      }
 +
 +      return true;
 +}
 +
 +void AnimationCurveCache::sampleMain(Scene *scene, BC_export_transformation_type atm_type, bool for_opensim)
 +{
 +      std::map<int, std::vector<SamplePoint>>::iterator frame;
 +      for (frame = sample_frames.begin(); frame != sample_frames.end(); frame++) {
 +              int frame_index = frame->first;
 +              std::vector<SamplePoint> sample_points = frame->second;
 +
 +              bc_update_scene(mContext, scene, frame_index);
 +
 +              for (int spi = 0; spi < sample_points.size(); spi++) {
 +                      SamplePoint &point = sample_points[spi];
 +                      Object *ob = point.get_object();
 +                      float mat[4][4];
 +
 +                      if (ob->type == OB_ARMATURE) {
 +                              /* For Armatures we need to check if this maybe is a pose sample point*/
 +                              Bone *bone = point.get_bone();
 +                              if (bone) {
 +                                      if (bone_matrix_local_get(ob, bone, mat, for_opensim)) {
 +                                              point.set_matrix(mat);
 +                                      }
 +                                      continue;
 +                              }
 +                      }
 +
- * 
++                      /* When this SamplePoint is not for a Bone,
 +                       * then we just store the Object local matrix here
 +                       */
 +
 +                      BKE_object_matrix_local_get(ob, mat);
 +                      point.set_matrix(mat);
 +
 +              }
 +      }
 +}
 +
 +/*
 +* enable fcurves driving a specific bone, disable all the rest
 +* if bone_name = NULL enable all fcurves
 +*/
 +void AnimationCurveCache::enable_fcurves(bAction *act, char *bone_name)
 +{
 +      FCurve *fcu;
 +      char prefix[200];
 +
 +      if (bone_name)
 +              BLI_snprintf(prefix, sizeof(prefix), "pose.bones[\"%s\"]", bone_name);
 +
 +      for (fcu = (FCurve *)act->curves.first; fcu; fcu = fcu->next) {
 +              if (bone_name) {
 +                      if (STREQLEN(fcu->rna_path, prefix, strlen(prefix)))
 +                              fcu->flag &= ~FCURVE_DISABLED;
 +                      else
 +                              fcu->flag |= FCURVE_DISABLED;
 +              }
 +              else {
 +                      fcu->flag &= ~FCURVE_DISABLED;
 +              }
 +      }
 +}
 +/*
 +* Sample the scene at frames where object fcurves
 +* have defined keys.
 +*/
 +void AnimationCurveCache::sampleScene(Scene *scene, BC_export_transformation_type atm_type, bool for_opensim, bool keyframe_at_end)
 +{
 +      create_sample_frames_from_keyframes();
 +      sampleMain(scene, atm_type, for_opensim);
 +}
 +
 +void AnimationCurveCache::sampleScene(Scene *scene, BC_export_transformation_type atm_type, int sampling_rate, bool for_opensim, bool keyframe_at_end)
 +{
 +      create_sample_frames_generated(scene->r.sfra, scene->r.efra, sampling_rate, keyframe_at_end);
 +      sampleMain(scene, atm_type, for_opensim);
 +}
 +
 +std::vector<FCurve *> *AnimationCurveCache::getSampledCurves(Object *ob)
 +{
 +      std::map<Object *, std::vector<FCurve *>>::iterator fcurves;
 +      fcurves = cached_curves.find(ob);
 +      return (fcurves == cached_curves.end()) ? NULL : &fcurves->second;
 +}
 +
 +std::vector<SamplePoint> &AnimationCurveCache::getFrameInfos(int frame_index)
 +{
 +      std::map<int, std::vector<SamplePoint>>::iterator frames = sample_frames.find(frame_index);
 +      if (frames == sample_frames.end()) {
 +              std::vector<SamplePoint> sample_points;
 +              sample_frames[frame_index] = sample_points;
 +      }
 +      return sample_frames[frame_index];
 +}
 +
 +
 +void AnimationCurveCache::add_sample_point(SamplePoint &point)
 +{
 +      int frame_index = point.get_frame();
 +      std::vector<SamplePoint> &frame_infos = getFrameInfos(frame_index);
 +      frame_infos.push_back(point);
 +}
 +
 +/*
 +* loop over all cached objects
 +*     loop over all fcurves
 +*         record all keyframes
-               do {            
++*
 +* The vector sample_frames finally contains a list of vectors
 +* where each vector contains a list of SamplePoints which
 +* need to be processed when evaluating the animation.
 +*/
 +void AnimationCurveCache::create_sample_frames_from_keyframes()
 +{
 +      sample_frames.clear();
 +      for (int i = 0; i < cached_objects.size(); i++) {
 +              Object *ob = cached_objects[i];
 +              bAction *action = bc_getSceneObjectAction(ob);
 +              FCurve *fcu = (FCurve *)action->curves.first;
 +
 +              for (; fcu; fcu = fcu->next) {
 +                      for (unsigned int i = 0; i < fcu->totvert; i++) {
 +                              float f = fcu->bezt[i].vec[1][0];
 +                              int frame_index = int(f);
 +                              SamplePoint sample_point(frame_index, ob, fcu, i);
 +                              add_sample_point(sample_point);
 +                      }
 +              }
 +      }
 +}
 +
 +/*
 +* loop over all cached objects
 +*     loop over active action using a stesize of sampling_rate
 +*         record all frames
 +*
 +* The vector sample_frames finally contains a list of vectors
 +* where each vector contains a list of SamplePoints which
 +* need to be processed when evaluating the animation.
 +* Note: The FCurves of the objects will not be used here.
 +*/
 +void AnimationCurveCache::create_sample_frames_generated(float sfra, float efra, int sampling_rate, int keyframe_at_end)
 +{
 +      sample_frames.clear();
 +
 +      for (int i = 0; i < cached_objects.size(); i++) {
 +
 +              Object *ob = cached_objects[i];
 +              float f = sfra;
 +
- }
++              do {
 +                      int frame_index = int(f);
 +                      SamplePoint sample_point(frame_index, ob);
 +                      add_sample_point(sample_point);
 +
 +                      /* Depending on the Object type add more sample points here
 +                      */
 +
 +                      if (ob && ob->type == OB_ARMATURE) {
 +                              LISTBASE_FOREACH(bPoseChannel *, pchan, &ob->pose->chanbase) {
 +                                      SamplePoint point(frame_index, ob, pchan->bone);
 +                                      add_sample_point(sample_point);
 +                              }
 +                      }
 +
 +                      if (f == efra)
 +                              break;
 +                      f += sampling_rate;
 +                      if (f > efra)
 +                              if (keyframe_at_end)
 +                                      f = efra; // make sure the last frame is always exported
 +                              else
 +                                      break;
 +              } while (true);
 +      }
 +}
 +
 +Matrix::Matrix()
 +{
 +      unit_m4(matrix);
 +}
 +
 +Matrix::Matrix(float (&mat)[4][4])
 +{
 +      set_matrix(mat);
 +}
 +
 +void Matrix::set_matrix(float(&mat)[4][4])
 +{
 +      copy_m4_m4(matrix, mat);
 +}
 +
 +void Matrix::set_matrix(Matrix &mat)
 +{
 +      copy_m4_m4(matrix, mat.matrix);
 +}
 +
 +void Matrix::get_matrix(float(&mat)[4][4])
 +{
 +      copy_m4_m4(mat, matrix);
 +}
 +
 +SamplePoint::SamplePoint(int frame, Object *ob)
 +{
 +      this->frame = frame;
 +      this->fcu = NULL;
 +      this->ob = ob;
 +      this->pose_bone = NULL;
 +      this->index = -1;
 +}
 +
 +SamplePoint::SamplePoint(int frame, Object *ob, FCurve *fcu, int index)
 +{
 +      this->frame = frame;
 +      this->fcu = fcu;
 +      this->ob = ob;
 +      this->pose_bone = NULL;
 +      this->index = index;
 +      this->path = std::string(fcu->rna_path);
 +
 +      /* Further elaborate on what this Fcurve is doing by checking
 +       * its rna_path
 +     */
 +
 +      if (ob && ob->type == OB_ARMATURE) {
 +              char *boneName = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
 +              bPose *pose = ob->pose;
 +              if (boneName) {
 +                      bPoseChannel *pchan = BKE_pose_channel_find_name(pose, boneName);
 +                      this->pose_bone = pchan->bone;
 +              }
 +      }
 +}
 +
 +
 +SamplePoint::SamplePoint(int frame, Object *ob, Bone *bone)
 +{
 +      this->frame = frame;
 +      this->fcu = NULL;
 +      this->ob = ob;
 +      this->pose_bone = bone;
 +      this->index = -1;
 +      this->path = "pose.bones[\"" + id_name(bone) + "\"].matrix";
 +}
 +
 +Matrix &SamplePoint::get_matrix()
 +{
 +      return matrix;
 +}
 +
 +void SamplePoint::set_matrix(Matrix &mat)
 +{
 +      this->matrix.set_matrix(mat);
 +}
 +
 +void SamplePoint::set_matrix(float(&mat)[4][4])
 +{
 +
 +}
 +
 +Object *SamplePoint::get_object()
 +{
 +      return this->ob;
 +}
 +
 +Bone *SamplePoint::get_bone()
 +{
 +      return this->pose_bone;
 +}
 +
 +FCurve *SamplePoint::get_fcurve()
 +{
 +      return this->fcu;
 +}
 +
 +int SamplePoint::get_frame()
 +{
 +      return this->frame;
 +}
 +
 +int SamplePoint::get_fcurve_index()
 +{
 +      return this->index;
 +}
 +
 +std::string &SamplePoint::get_path()
 +{
 +      return path;
++}
index 4a4cc77,0000000..3d74e93
mode 100644,000000..100644
--- /dev/null
@@@ -1,130 -1,0 +1,130 @@@
-       
-       void sampleMain(Scene *scene, 
-               BC_export_transformation_type atm_type, 
 +/*
 +* ***** BEGIN GPL LICENSE BLOCK *****
 +*
 +* This program is free software; you can redistribute it and/or
 +* modify it under the terms of the GNU General Public License
 +* as published by the Free Software Foundation; either version 2
 +* of the License, or (at your option) any later version.
 +*
 +* This program is distributed in the hope that it will be useful,
 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +* GNU General Public License for more details.
 +*
 +* You should have received a copy of the GNU General Public License
 +* along with this program; if not, write to the Free Software Foundation,
 +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +*
 +* Contributor(s): Chingiz Dyussenov, Arystanbek Dyussenov, Jan Diederich, Tod Liverseed.
 +*
 +* ***** END GPL LICENSE BLOCK *****
 +*/
 +
 +#ifndef __ANIMATION_CURVE_CACHE_H__
 +#define __ANIMATION_CURVE_CACHE_H__
 +
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <math.h>
 +#include <vector>
 +#include <map>
 +#include <algorithm>    // std::find
 +
 +#include "exportSettings.h"
 +#include "collada_utils.h"
 +
 +extern "C"
 +{
 +#include "DNA_object_types.h"
 +#include "DNA_anim_types.h"
 +}
 +
 +class Matrix {
 +private:
 +      float matrix[4][4];
 +public:
 +      Matrix();
 +      Matrix(float (&mat)[4][4]);
 +      void set_matrix(float (&mat)[4][4]);
 +      void set_matrix(Matrix &mat);
 +      void get_matrix(float (&mat)[4][4]);
 +};
 +
 +class SamplePoint {
 +
 +private:
 +
 +      Object * ob;
 +      Bone *pose_bone;
 +      FCurve *fcu;
 +      int frame; /* frame in timeline (not sure if we actually should store a float here) */
 +      int index; /* Keyframe index in fcurve (makes sense only when fcu is also set) */
 +      std::string path; /* Do not mixup with rna_path. It is used for different purposes! */
 +
 +      Matrix matrix; /* Local matrix, by default unit matrix, will be set when sampling */
 +
 +public:
 +
 +      SamplePoint(int frame, Object *ob);
 +      SamplePoint(int frame, Object *ob, FCurve *fcu, int index);
 +      SamplePoint(int frame, Object *ob, Bone *bone);
 +
 +      Object *get_object();
 +      Bone *get_bone();
 +      FCurve *get_fcurve();
 +      int get_frame();
 +      int get_fcurve_index();
 +      Matrix &get_matrix();
 +      std::string &get_path();
 +
 +      void set_matrix(Matrix &matrix);
 +      void set_matrix(float(&mat)[4][4]);
 +};
 +
 +
 +class AnimationCurveCache {
 +private:
 +      void clear_cache(); // remove all sampled FCurves
 +      void clear_cache(Object *ob); //remove sampled FCurves for single object
 +      void create_curves(Object *ob);
 +
 +      std::vector<Object *> cached_objects; // list of objects for caching
 +      std::map<Object *, std::vector<FCurve *>> cached_curves; //map of cached FCurves
 +      std::map<int, std::vector<SamplePoint>> sample_frames; // list of frames where objects need to be sampled
 +
 +      std::vector<SamplePoint> &getFrameInfos(int frame_index);
 +      void add_sample_point(SamplePoint &point);
 +      void enable_fcurves(bAction *act, char *bone_name);
 +      bool bone_matrix_local_get(Object *ob, Bone *bone, float (&mat)[4][4], bool for_opensim);
 +
 +      bContext *mContext;
 +
 +public:
 +
 +      AnimationCurveCache(bContext *C);
 +      ~AnimationCurveCache();
 +
 +      void addObject(Object *obj);
-       
-       void sampleScene(Scene *scene, 
-               BC_export_transformation_type atm_type, 
++
++      void sampleMain(Scene *scene,
++              BC_export_transformation_type atm_type,
 +              bool for_opensim);
-       void sampleScene(Scene *scene, 
++
++      void sampleScene(Scene *scene,
++              BC_export_transformation_type atm_type,
 +              bool for_opensim,
 +              bool keyframe_at_end = true); // use keys from FCurves, use timeline boundaries
 +
++      void sampleScene(Scene *scene,
 +              BC_export_transformation_type atm_type,
 +              int sampling_rate, bool for_opensim,
 +              bool keyframe_at_end = true ); // generate keyframes for frames use timeline boundaries
 +
 +      std::vector<FCurve *> *getSampledCurves(Object *ob);
 +
 +      void create_sample_frames_from_keyframes();
 +      void create_sample_frames_generated(float sfra, float efra, int sampling_rate, int keyframe_at_end);
 +};
 +
 +
 +#endif
@@@ -65,74 -60,230 +65,74 @@@ bool AnimationExporter::open_animation_
        }
        return true;
  }
 -/*
 - * This function creates a complete LINEAR Collada <Animation> Entry with all needed
 - * <source>, <sampler>, and <channel> entries.
 - * This is is used for creating sampled Transformation Animations for either:
 - *
 - * 1-axis animation:
 - *   times contains the time points in seconds from within the timeline
 - *   values contains the data (list of single floats)
 - *   channel_count = 1
 - *   axis_name = ['X' | 'Y' | 'Z']
 - *   is_rot indicates if the animation is a rotation
 - *
 - * 3-axis animation:
 - *   times contains the time points in seconds from within the timeline
 - *   values contains the data (list of floats where each 3 entries are one vector)
 - *   channel_count = 3
 - *   axis_name = "" (actually not used)
 - *   is_rot = false (see xxx below)
 - *
 - * xxx:
 - *   I tried to create a 3 axis rotation animation
 - *   like for translation or scale. But i could not
 - *   figure out how to setup the channel for this case.
 - *   So for now rotations are exported as 3 separate 1-axis collada animations
 - *   See export_sampled_animation() further down.
 - */
 -void AnimationExporter::create_sampled_animation(int channel_count,
 -      std::vector<float> &times,
 -      std::vector<float> &values,
 -      std::string ob_name,
 -      std::string label,
 -      std::string axis_name,
 -      bool is_rot)
 -{
 -      char anim_id[200];
 -
 -      if (is_flat_line(values, channel_count))
 -              return;
 -
 -      BLI_snprintf(anim_id, sizeof(anim_id), "%s_%s_%s", (char *)translate_id(ob_name).c_str(), label.c_str(), axis_name.c_str());
 -
 -      openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING);
 -
 -      /* create input source */
 -      std::string input_id = create_source_from_vector(COLLADASW::InputSemantic::INPUT, times, false, anim_id, "");
 -
 -      /* create output source */
 -      std::string output_id;
 -      if (channel_count == 1)
 -              output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, &values[0], values.size(), is_rot, anim_id, axis_name.c_str());
 -      else if (channel_count == 3)
 -              output_id = create_xyz_source(&values[0], times.size(), anim_id);
 -      else if (channel_count == 16)
 -              output_id = create_4x4_source(times, values, anim_id);
 -
 -      std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX;
 -      COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id);
 -      std::string empty;
 -      sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id));
 -      sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id));
 -
 -      /* TODO create in/out tangents source (LINEAR) */
 -      std::string interpolation_id = fake_interpolation_source(times.size(), anim_id, "");
 -
 -      /* Create Sampler */
 -      sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
 -      addSampler(sampler);
 -
 -      /* Create channel */
 -      std::string target = translate_id(ob_name) + "/" + label + axis_name + ((is_rot) ? ".ANGLE" : "");
 -      addChannel(COLLADABU::URI(empty, sampler_id), target);
 -
 -      closeAnimation();
 -
 -}
  
 -/*
 - * Export all animation FCurves of an Object.
 - *
 - * Note: This uses the keyframes as sample points,
 - * and exports "baked keyframes" while keeping the tangent information
 - * of the FCurves intact. This works for simple cases, but breaks
 - * especially when negative scales are involved in the animation.
 - *
 - * If it is necessary to conserve the Animation precisely then
 - * use export_sampled_animation_set() instead.
 - */
 -void AnimationExporter::export_keyframed_animation_set(Object *ob)
 +void AnimationExporter::openAnimationWithClip(std::string action_id, std::string action_name)
  {
 -      FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
 -      if (!fcu) {
 -              return; /* object has no animation */
 -      }
 -
 -      if (this->export_settings->export_transformation_type == BC_TRANSFORMATION_TYPE_MATRIX) {
 +      std::vector<std::string> anim_meta_entry;
 +      anim_meta_entry.push_back(translate_id(action_id));
 +      anim_meta_entry.push_back(action_name);
 +      anim_meta.push_back(anim_meta_entry);
  
 -              std::vector<float> ctimes;
 -              find_keyframes(ob, ctimes);
 -              if (ctimes.size() > 0)
 -                      export_sampled_matrix_animation(ob, ctimes);
 -      }
 -      else {
 -              char *transformName;
 -              while (fcu) {
 -                      //for armature animations as objects
 -                      if (ob->type == OB_ARMATURE)
 -                              transformName = fcu->rna_path;
 -                      else
 -                              transformName = extract_transform_name(fcu->rna_path);
 -
 -                      if (
 -                              STREQ(transformName, "location") ||
 -                              STREQ(transformName, "scale") ||
 -                              (STREQ(transformName, "rotation_euler") && ob->rotmode == ROT_MODE_EUL) ||
 -                              STREQ(transformName, "rotation_quaternion"))
 -                      {
 -                              create_keyframed_animation(ob, fcu, transformName, false);
 -                      }
 -                      fcu = fcu->next;
 -              }
 -      }
 +      openAnimation(translate_id(action_id), action_name);
  }
  
 -/*
 - * Export the sampled animation of an Object.
 - *
 - * Note: This steps over all animation frames (step size is given in export_settings.sample_size)
 - * and then evaluates the transformation,
 - * and exports "baked samples" This works always, however currently the interpolation type is set
 - * to LINEAR for now. (maybe later this can be changed to BEZIER)
 - *
 - * Note: If it is necessary to keep the FCurves intact, then use export_keyframed_animation_set() instead.
 - * However be aware that exporting keyframed animation may modify the animation slightly.
 - * Also keyframed animation exports tend to break when negative scales are involved.
 - */
 -void AnimationExporter::export_sampled_animation_set(Object *ob)
 +void AnimationExporter::close_animation_container(bool has_container)
  {
 -      std::vector<float>ctimes;
 -      find_sampleframes(ob, ctimes);
 -      if (ctimes.size() > 0) {
 -              if (this->export_settings->export_transformation_type == BC_TRANSFORMATION_TYPE_MATRIX)
 -                      export_sampled_matrix_animation(ob, ctimes);
 -              else
 -                      export_sampled_transrotloc_animation(ob, ctimes);
 -      }
 +      if (has_container)
 +              closeAnimation();
  }
  
 -void AnimationExporter::export_sampled_matrix_animation(Object *ob, std::vector<float> &ctimes)
 +bool AnimationExporter::exportAnimations()
  {
 -      UnitConverter converter;
 +      Scene *sce = blender_context.get_scene();
  
 -      std::vector<float> values;
 +      LinkNode &export_set = *this->export_settings->export_set;
 +      bool has_anim_data = bc_has_animations(sce, export_set);
 +      int animation_count = 0;
 +      if (has_anim_data) {
  
 -      for (std::vector<float>::iterator ctime = ctimes.begin(); ctime != ctimes.end(); ++ctime) {
 -              float fmat[4][4];
 +              BCObjectSet animated_subset;
 +              BCAnimationSampler::get_animated_from_export_set(animated_subset, export_set);
 +              animation_count = animated_subset.size();
 +              BCAnimationSampler animation_sampler(blender_context, animated_subset);
  
 -              bc_update_scene(m_bmain, scene, *ctime);
 -              BKE_object_matrix_local_get(ob, fmat);
 -              if (this->export_settings->limit_precision)
 -                      bc_sanitize_mat(fmat, 6);
 +              try {
 +                      animation_sampler.sample_scene(
 +                              export_settings->sampling_rate,
 +                              /*keyframe_at_end = */ true,
 +                              export_settings->open_sim,
 +                              export_settings->keep_keyframes,
 +                              export_settings->export_animation_type
 +                      );
  
 -              for (int i = 0; i < 4; i++)
 -                      for (int j = 0; j < 4; j++)
 -                              values.push_back(fmat[i][j]);
 -      }
 -
 -      std::string ob_name = id_name(ob);
 +                      openLibrary();
  
 -      create_sampled_animation(16, ctimes, values, ob_name, "transform", "", false);
 -}
 +                      BCObjectSet::iterator it;
 +                      for (it = animated_subset.begin(); it != animated_subset.end(); ++it) {
 +                              Object *ob = *it;
 +                              exportAnimation(ob, animation_sampler);
 +                      }
 +              }
 +              catch (std::invalid_argument &iae)
 +              {
 +                      fprintf(stderr, "Animation export interrupted");
 +                      fprintf(stderr, "Exception was: %s", iae.what());
 +              }
  
 -void AnimationExporter::export_sampled_transrotloc_animation(Object *ob, std::vector<float> &ctimes)
 -{
 -      static int LOC   = 0;
 -      static int EULX  = 1;
 -      static int EULY  = 2;
 -      static int EULZ  = 3;
 -      static int SCALE = 4;
 -
 -      std::vector<float> baked_curves[5];
 -
 -      for (std::vector<float>::iterator ctime = ctimes.begin(); ctime != ctimes.end(); ++ctime ) {
 -              float fmat[4][4];
 -              float floc[3];
 -              float fquat[4];
 -              float fsize[3];
 -              float feul[3];
 -
 -              bc_update_scene(m_bmain, scene, *ctime);
 -              BKE_object_matrix_local_get(ob, fmat);
 -              mat4_decompose(floc, fquat, fsize, fmat);
 -              quat_to_eul(feul, fquat);
 -
 -              baked_curves[LOC].push_back(floc[0]);
 -              baked_curves[LOC].push_back(floc[1]);
 -              baked_curves[LOC].push_back(floc[2]);
 -
 -              baked_curves[EULX].push_back(feul[0]);
 -              baked_curves[EULY].push_back(feul[1]);
 -              baked_curves[EULZ].push_back(feul[2]);
 -
 -              baked_curves[SCALE].push_back(fsize[0]);
 -              baked_curves[SCALE].push_back(fsize[1]);
 -              baked_curves[SCALE].push_back(fsize[2]);
 +              closeLibrary();
  
-                */ 
 +#if 0
 +              /* TODO: If all actions shall be exported, we need to call the
 +               * AnimationClipExporter which will figure out which actions
 +               * need to be exported for which objects
++               */
 +              if (this->export_settings->include_all_actions) {
 +                      AnimationClipExporter ace(eval_ctx, sw, export_settings, anim_meta);
 +                      ace.exportAnimationClips(sce);
 +              }
 +#endif
        }
 -
 -      std::string ob_name = id_name(ob);
 -
 -      create_sampled_animation(3, ctimes, baked_curves[SCALE], ob_name, "scale",   "", false);
 -      create_sampled_animation(3, ctimes, baked_curves[LOC],  ob_name, "location", "", false);
 -
 -      /* Not sure how to export rotation as a 3channel animation,
 -       * so separate into 3 single animations for now:
 -       */
 -
 -      create_sampled_animation(1, ctimes, baked_curves[EULX], ob_name, "rotation", "X", true);
 -      create_sampled_animation(1, ctimes, baked_curves[EULY], ob_name, "rotation", "Y", true);
 -      create_sampled_animation(1, ctimes, baked_curves[EULZ], ob_name, "rotation", "Z", true);
 -
 -      fprintf(stdout, "Animation Export: Baked %d frames for %s (sampling rate: %d)\n",
 -              (int)baked_curves[0].size(),
 -              ob->id.name,
 -              this->export_settings->sampling_rate);
 +      return animation_count;
  }
  
  /* called for each exported object */
@@@ -220,144 -371,307 +220,144 @@@ void AnimationExporter::export_curve_an
        }
  }
  
 -void AnimationExporter::export_object_constraint_animation(Object *ob)
 +void AnimationExporter::export_matrix_animation(Object *ob, BCAnimationSampler &sampler)
  {
 -      std::vector<float> fra;
 -      //Takes frames of target animations
 -      make_anim_frames_from_targets(ob, fra);
 -
 -      if (fra.size())
 -              dae_baked_object_animation(fra, ob);
 -}
 -
 -void AnimationExporter::export_morph_animation(Object *ob)
 -{
 -      FCurve *fcu;
 -      char *transformName;
 -      Key *key = BKE_key_from_object(ob);
 -      if (!key) return;
 -
 -      if (key->adt && key->adt->action) {
 -              fcu = (FCurve *)key->adt->action->curves.first;
 -
 -              while (fcu) {
 -                      transformName = extract_transform_name(fcu->rna_path);
 +      std::vector<float> frames;
 +      sampler.get_object_frames(frames, ob);
 +      if (frames.size() > 0) {
 +              BCMatrixSampleMap samples;
 +              bool is_animated = sampler.get_object_samples(samples, ob);
 +              if (is_animated) {
 +                      bAction *action = bc_getSceneObjectAction(ob);
 +                      std::string name = encode_xml(id_name(ob));
 +                      std::string action_name = (action == NULL) ? name + "-action" : id_name(action);
 +                      std::string channel_type = "transform";
 +                      std::string axis = "";
 +                      std::string id = bc_get_action_id(action_name, name, channel_type, axis);
  
 -                      create_keyframed_animation(ob, fcu, transformName, true);
 +                      std::string target = translate_id(name) + '/' + channel_type;
  
 -                      fcu = fcu->next;
 +                      export_collada_matrix_animation(id, name, target, frames, samples);
                }
        }
 -
  }
  
 -void AnimationExporter::make_anim_frames_from_targets(Object *ob, std::vector<float> &frames )
 +//write bone animations in transform matrix sources
 +void AnimationExporter::export_bone_animations_recursive(Object *ob, Bone *bone, BCAnimationSampler &sampler)
  {
 -      ListBase *conlist = get_active_constraints(ob);
 -      if (conlist == NULL) return;
 -      bConstraint *con;
 -      for (con = (bConstraint *)conlist->first; con; con = con->next) {
 -              ListBase targets = {NULL, NULL};
 -
 -              const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
 -
 -              if (!validateConstraints(con)) continue;
 -
 -              if (cti && cti->get_constraint_targets) {
 -                      bConstraintTarget *ct;
 -                      Object *obtar;
 -                      /* get targets
 -                       * - constraints should use ct->matrix, not directly accessing values
 -                       * - ct->matrix members have not yet been calculated here!
 -                       */
 -                      cti->get_constraint_targets(con, &targets);
 +      std::vector<float> frames;
 +      sampler.get_bone_frames(frames, ob, bone);
-       
 -                      for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
 -                              obtar = ct->tar;
 -
 -                              if (obtar)
 -                                      find_keyframes(obtar, frames);
 -                      }
 -
 -                      if (cti->flush_constraint_targets)
 -                              cti->flush_constraint_targets(con, &targets, 1);
 +      if (frames.size()) {
 +              BCMatrixSampleMap samples;
 +              bool is_animated = sampler.get_bone_samples(samples, ob, bone);
 +              if (is_animated) {
 +                      export_bone_animation(ob, bone, frames, samples);
                }
        }
 +
 +      for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next)
 +              export_bone_animations_recursive(ob, child, sampler);
  }
  
 -//euler sources from quternion sources
 -float *AnimationExporter::get_eul_source_for_quat(Object *ob)
 -{
 -      FCurve *fcu = (FCurve *)ob->adt->action->curves.first;
 -      const int keys = fcu->totvert;
 -      float *quat = (float *)MEM_callocN(sizeof(float) * fcu->totvert * 4, "quat output source values");
 -      float *eul = (float *)MEM_callocN(sizeof(float) * fcu->totvert * 3, "quat output source values");
 -      float temp_quat[4];
 -      float temp_eul[3];
 -      while (fcu) {
 -              char *transformName = extract_transform_name(fcu->rna_path);
 -
 -              if (STREQ(transformName, "rotation_quaternion") ) {
 -                      for (int i = 0; i < fcu->totvert; i++) {
 -                              *(quat + (i * 4) + fcu->array_index) = fcu->bezt[i].vec[1][1];
 +/*
 +* In some special cases the exported Curve needs to be replaced
 +* by a modified curve (for collada purposes)
 +* This method checks if a conversion is necessary and if applicable
 +* returns a pointer to the modified BCAnimationCurve.
 +* IMPORTANT: the modified curve must be deleted by the caller when no longer needed
 +* if no conversion is needed this method returns a NULL;
 +*/
 +BCAnimationCurve *AnimationExporter::get_modified_export_curve(Object *ob, BCAnimationCurve &curve, BCAnimationCurveMap &curves)
 +{
 +      std::string channel_target = curve.get_channel_target();
 +      BCAnimationCurve *mcurve = NULL;
 +      if (channel_target == "lens") {
 +
 +              /* Create an xfov curve */
 +
 +              BCCurveKey key(BC_ANIMATION_TYPE_CAMERA, "xfov", 0);
 +              mcurve = new BCAnimationCurve(key, ob);
 +
 +              // now tricky part: transform the fcurve
 +              BCValueMap lens_values;
 +              curve.get_value_map(lens_values);
 +
 +              BCAnimationCurve *sensor_curve = NULL;
 +              BCCurveKey sensor_key(BC_ANIMATION_TYPE_CAMERA, "sensor_width", 0);
 +              BCAnimationCurveMap::iterator cit = curves.find(sensor_key);
 +              if (cit != curves.end()) {
 +                      sensor_curve = cit->second;
 +              }
 +
 +              BCValueMap::const_iterator vit;
 +              for (vit = lens_values.begin(); vit != lens_values.end(); ++vit) {
 +                      int frame = vit->first;
 +                      float lens_value = vit->second;
 +
 +                      float sensor_value;
 +                      if (sensor_curve) {
 +                              sensor_value = sensor_curve->get_value(frame);
 +                      }
 +                      else {
 +                              sensor_value = ((Camera *)ob->data)->sensor_x;
                        }
 +                      float value = RAD2DEGF(focallength_to_fov(lens_value, sensor_value));
 +                      mcurve->add_value(value, frame);
                }
 -              fcu = fcu->next;
 -      }
 -
 -      for (int i = 0; i < keys; i++) {
 -              for (int j = 0; j < 4; j++)
 -                      temp_quat[j] = quat[(i * 4) + j];
 -
 -              quat_to_eul(temp_eul, temp_quat);
 -
 -              for (int k = 0; k < 3; k++)
 -                      eul[i * 3 + k] = temp_eul[k];
 -
 +              mcurve->clean_handles(); // to reset the handles
        }
 -      MEM_freeN(quat);
 -      return eul;
 -
 -}
 -
 -//Get proper name for bones
 -std::string AnimationExporter::getObjectBoneName(Object *ob, const FCurve *fcu)
 -{
 -      //hard-way to derive the bone name from rna_path. Must find more compact method
 -      std::string rna_path = std::string(fcu->rna_path);
 -
 -      char *boneName = strtok((char *)rna_path.c_str(), "\"");
 -      boneName = strtok(NULL, "\"");
 -
 -      if (boneName != NULL)
 -              return /*id_name(ob) + "_" +*/ std::string(boneName);
 -      else
 -              return id_name(ob);
 -}
 -
 -std::string AnimationExporter::getAnimationPathId(const FCurve *fcu)
 -{
 -      std::string rna_path = std::string(fcu->rna_path);
 -      return translate_id(rna_path);
 +      return mcurve;
  }
  
 -/* convert f-curves to animation curves and write */
 -void AnimationExporter::create_keyframed_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma)
 +void AnimationExporter::export_curve_animation(
 +      Object *ob,
 +      BCAnimationCurve &curve)
  {
 -      const char *axis_name = NULL;
 -      char anim_id[200];
 -
 -      bool has_tangents = false;
 -      bool quatRotation = false;
 -
 -      Object *obj = NULL;
 -
 -      if (STREQ(transformName, "rotation_quaternion") ) {
 -              fprintf(stderr, "quaternion rotation curves are not supported. rotation curve will not be exported\n");
 -              quatRotation = true;
 -              return;
 -      }
 -
 -      //axis names for colors
 -      else if (STREQ(transformName, "color") ||
 -               STREQ(transformName, "specular_color") ||
 -               STREQ(transformName, "diffuse_color") ||
 -               STREQ(transformName, "alpha"))
 -      {
 -              const char *axis_names[] = {"R", "G", "B"};
 -              if (fcu->array_index < 3)
 -                      axis_name = axis_names[fcu->array_index];
 -      }
 +      std::string channel_target = curve.get_channel_target();
  
        /*
 -       * Note: Handle transformation animations separately (to apply matrix inverse to fcurves)
 -       * We will use the object to evaluate the animation on all keyframes and calculate the
 -       * resulting object matrix. We need this to incorporate the
 -       * effects of the parent inverse matrix (when it contains a rotation component)
 -       *
 -       * TODO: try to combine exported fcurves into 3 channel animations like done
 -       * in export_sampled_animation(). For now each channel is exported as separate <Animation>.
 +       * Some curves can not be exported as is and need some conversion
 +       * For more information see implementation oif get_modified_export_curve()
 +       * note: if mcurve is not NULL then it must be deleted at end of this method;
         */
  
 -      else if (
 -              STREQ(transformName, "scale") ||
 -              STREQ(transformName, "location") ||
 -              STREQ(transformName, "rotation_euler"))
 -      {
 -              const char *axis_names[] = {"X", "Y", "Z"};
 -              if (fcu->array_index < 3) {
 -                      axis_name = axis_names[fcu->array_index];
 -                      obj = ob;
 -              }
 -      }
 -      else {
 -              /* no axis name. single parameter */
 -              axis_name = "";
 -      }
 -
 -      std::string ob_name = std::string("null");
 -
 -      /* Create anim Id */
 -      if (ob->type == OB_ARMATURE) {
 -              ob_name =  getObjectBoneName(ob, fcu);
 -              BLI_snprintf(
 -                      anim_id,
 -                      sizeof(anim_id),
 -                      "%s_%s.%s",
 -                      (char *)translate_id(ob_name).c_str(),
 -                      (char *)translate_id(transformName).c_str(),
 -                      axis_name);
 -      }
 -      else {
 -              if (ma)
 -                      ob_name = id_name(ob) + "_material";
 -              else
 -                      ob_name = id_name(ob);
 -
 -              BLI_snprintf(
 -                      anim_id,
 -                      sizeof(anim_id),
 -                      "%s_%s_%s",
 -                      (char *)translate_id(ob_name).c_str(),
 -                      (char *)getAnimationPathId(fcu).c_str(),
 -                      axis_name);
 -      }
 +      int channel_index = curve.get_channel_index();
 +      std::string axis = get_axis_name(channel_target, channel_index); // RGB or XYZ or ""
  
 -      openAnimation(anim_id, COLLADABU::Utils::EMPTY_STRING);
 +      std::string action_name;
 +      bAction *action = bc_getSceneObjectAction(ob);
 +      action_name = (action) ? id_name(action) : "constraint_anim";
  
 -      // create input source
 -      std::string input_id = create_source_from_fcurve(COLLADASW::InputSemantic::INPUT, fcu, anim_id, axis_name);
 +      const std::string curve_name = encode_xml(curve.get_animation_name(ob));
 +      std::string id = bc_get_action_id(action_name, curve_name, channel_target, axis, ".");
  
 -      // create output source
 -      std::string output_id;
 +      std::string collada_target = translate_id(curve_name);
  
 -      //quat rotations are skipped for now, because of complications with determining axis.
 -      if (quatRotation) {
 -              float *eul  = get_eul_source_for_quat(ob);
 -              float *eul_axis = (float *)MEM_callocN(sizeof(float) * fcu->totvert, "quat output source values");
 -              for (int i = 0; i < fcu->totvert; i++) {
 -                      eul_axis[i] = eul[i * 3 + fcu->array_index];
 +      if (curve.is_of_animation_type(BC_ANIMATION_TYPE_MATERIAL)) {
 +              int material_index = curve.get_subindex();
 +              Material *ma = give_current_material(ob, material_index + 1);
 +              if (ma) {
 +                      collada_target = translate_id(id_name(ma)) + "-effect/common/" + get_collada_sid(curve, axis);
                }
 -              output_id = create_source_from_array(COLLADASW::InputSemantic::OUTPUT, eul_axis, fcu->totvert, quatRotation, anim_id, axis_name);
 -              MEM_freeN(eul);
 -              MEM_freeN(eul_axis);
 -      }
 -      else if (STREQ(transformName, "lens") && (ob->type == OB_CAMERA)) {
 -              output_id = create_lens_source_from_fcurve((Camera *) ob->data, COLLADASW::InputSemantic::OUTPUT, fcu, anim_id);
        }
        else {
 -              output_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUTPUT, fcu, anim_id, axis_name, obj);
 -      }
 -
 -      // create interpolations source
 -      std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name, &has_tangents);
 -
 -      // handle tangents (if required)
 -      std::string intangent_id;
 -      std::string outtangent_id;
 -
 -      if (has_tangents) {
 -              // create in_tangent source
 -              intangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::IN_TANGENT, fcu, anim_id, axis_name, obj);
 -
 -              // create out_tangent source
 -              outtangent_id = create_source_from_fcurve(COLLADASW::InputSemantic::OUT_TANGENT, fcu, anim_id, axis_name, obj);
 -      }
 -
 -      std::string sampler_id = std::string(anim_id) + SAMPLER_ID_SUFFIX;
 -      COLLADASW::LibraryAnimations::Sampler sampler(sw, sampler_id);
 -      std::string empty;
 -      sampler.addInput(COLLADASW::InputSemantic::INPUT, COLLADABU::URI(empty, input_id));
 -      sampler.addInput(COLLADASW::InputSemantic::OUTPUT, COLLADABU::URI(empty, output_id));
 -
 -      // this input is required
 -      sampler.addInput(COLLADASW::InputSemantic::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
 -
 -      if (has_tangents) {
 -              sampler.addInput(COLLADASW::InputSemantic::IN_TANGENT, COLLADABU::URI(empty, intangent_id));
 -              sampler.addInput(COLLADASW::InputSemantic::OUT_TANGENT, COLLADABU::URI(empty, outtangent_id));
 +              collada_target += "/" + get_collada_sid(curve, axis);
        }
  
 -      addSampler(sampler);
 -
 -      std::string target;
 -
 -      if (!is_param)
 -              target = translate_id(ob_name) +
 -                       "/" + get_transform_sid(fcu->rna_path, -1, axis_name, true);
 -      else {
 -              if (ob->type == OB_LAMP)
 -                      target = get_light_id(ob) +
 -                               "/" + get_light_param_sid(fcu->rna_path, -1, axis_name, true);
 -
 -              if (ob->type == OB_CAMERA)
 -                      target = get_camera_id(ob) +
 -                               "/" + get_camera_param_sid(fcu->rna_path, -1, axis_name, true);
 -
 -              if (ma)
 -                      target = translate_id(id_name(ma)) + "-effect" +
 -                               "/common/" /*profile common is only supported */ + get_transform_sid(fcu->rna_path, -1, axis_name, true);
 -              //if shape key animation, this is the main problem, how to define the channel targets.
 -              /*target = get_morph_id(ob) +
 -                               "/value" +*/
 -      }
 -      addChannel(COLLADABU::URI(empty, sampler_id), target);
 +      export_collada_curve_animation(id, curve_name, collada_target, axis, curve);
  
 -      closeAnimation();
  }
  
 -
 -
 -//write bone animations in transform matrix sources
 -void AnimationExporter::write_bone_animation_matrix(Object *ob_arm, Bone *bone)
 +void AnimationExporter::export_bone_animation(Object *ob, Bone *bone, BCFrames &frames, BCMatrixSampleMap &samples)
  {
 -      if (!ob_arm->adt)
 -              return;
 -
 -      //This will only export animations of bones in deform group.
 -      /* if (!is_bone_deform_group(bone)) return; */
 -
 -      sample_and_write_bone_animation_matrix(ob_arm, bone);
 +      bAction* action = bc_getSceneObjectAction(ob);
 +      std::string bone_name(bone->name);
 +      std::string name = encode_xml(id_name(ob));
 +      std::string id = bc_get_action_id(id_name(action), name, bone_name, "pose_matrix");
 +      std::string target = translate_id(id_name(ob) + "_" + bone_name) + "/transform";
  
 -      for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next)
 -              write_bone_animation_matrix(ob_arm, child);
 +      export_collada_matrix_animation(id, name, target, frames, samples);
  }
  
  bool AnimationExporter::is_bone_deform_group(Bone *bone)
@@@ -477,10 -909,7 +477,10 @@@ std::string AnimationExporter::get_sema
  }
  
  void AnimationExporter::add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param,
 -                                              COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis, bool transform)
 +      COLLADASW::InputSemantic::Semantics semantic,
-       bool is_rot, 
-       const std::string axis, 
++      bool is_rot,
++      const std::string axis,
 +      bool transform)
  {
        switch (semantic) {
                case COLLADASW::InputSemantic::INPUT:
@@@ -632,10 -1414,8 +632,10 @@@ std::string AnimationExporter::collada_
        return source_id;
  }
  
 -std::string AnimationExporter::create_interpolation_source(FCurve *fcu, const std::string& anim_id, const char *axis_name, bool *has_tangents)
 +std::string AnimationExporter::collada_interpolation_source(const BCAnimationCurve &curve,
-       const std::string& anim_id, 
++      const std::string& anim_id,
 +      const std::string axis,
 +      bool *has_tangents)
  {
        std::string source_id = anim_id + get_semantic_suffix(COLLADASW::InputSemantic::INTERPOLATION);
  
@@@ -144,123 -136,70 +144,123 @@@ protected
  
        float convert_angle(float angle);
  
-               Object *ob_arm, 
-               Bone *bone, 
 +      std::vector<std::vector<std::string>> anim_meta;
 +
 +      /* Main entry point into Animation export (called for each exported object) */
 +      void exportAnimation(Object *ob, BCAnimationSampler &sampler);
 +
 +      /* export animation as separate trans/rot/scale curves */
 +      void export_curve_animation_set(
 +              Object *ob,
 +              BCAnimationSampler &sampler,
 +              bool export_tm_curves);
 +
 +      /* export one single curve */
 +      void export_curve_animation(
 +              Object *ob,
 +              BCAnimationCurve &curve);
 +
 +      /* export animation as matrix data */
 +      void export_matrix_animation(
 +              Object *ob,
 +              BCAnimationSampler &sampler);
 +
 +      /* step through the bone hierarchy */
 +      void export_bone_animations_recursive(
-               std::vector<float> &values,  
-               const std::string& anim_id, 
++              Object *ob_arm,
++              Bone *bone,
 +              BCAnimationSampler &sampler);
 +
 +      /* Export for one bone */
 +      void export_bone_animation(
 +              Object *ob,
 +              Bone *bone,
 +              BCFrames &frames,
 +              BCMatrixSampleMap &outmats);
 +
 +      /* call to the low level collada exporter */
 +      void export_collada_curve_animation(
 +              std::string id,
 +              std::string name,
 +              std::string target,
 +              std::string axis,
 +              BCAnimationCurve &curve);
 +
 +      /* call to the low level collada exporter */
 +      void export_collada_matrix_animation(
 +              std::string id,
 +              std::string name,
 +              std::string target,
 +              BCFrames &frames,
 +              BCMatrixSampleMap &outmats);
 +
 +      BCAnimationCurve *get_modified_export_curve(Object *ob, BCAnimationCurve &curve, BCAnimationCurveMap &curves);
 +
 +      /* Helper functions */
 +      void openAnimationWithClip(std::string id, std::string name);
 +      bool open_animation_container(bool has_container, Object *ob);
 +      void close_animation_container(bool has_container);
 +
 +      /* Input and Output sources (single valued) */
 +      std::string collada_source_from_values(
 +              BC_animation_source_type tm_channel,
 +              COLLADASW::InputSemantic::Semantics semantic,
-       
++              std::vector<float> &values,
++              const std::string& anim_id,
 +              const std::string axis_name);
-               int tot, 
++
 +      /* Output sources (matrix data) */
 +      std::string collada_source_from_values(
 +              BCMatrixSampleMap &samples,
 +              const std::string& anim_id);
 +
 +      /* Interpolation sources */
 +      std::string collada_linear_interpolation_source(
++              int tot,
 +              const std::string& anim_id);
 +
 +      /* source ID = animation_name + semantic_suffix */
 +
        std::string get_semantic_suffix(COLLADASW::InputSemantic::Semantics semantic);
  
        void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param,
 -                                 COLLADASW::InputSemantic::Semantics semantic, bool is_rot, const char *axis, bool transform);
 -
 -      void get_source_values(BezTriple *bezt, COLLADASW::InputSemantic::Semantics semantic, bool is_angle, float *values, int *length);
 -
 -      float* get_eul_source_for_quat(Object *ob );
 -
 -      bool is_flat_line(std::vector<float> &values, int channel_count);
 -      void export_keyframed_animation_set(Object *ob);
 -      void create_keyframed_animation(Object *ob, FCurve *fcu, char *transformName, bool is_param, Material *ma = NULL);
 -      void export_sampled_animation_set(Object *ob);
 -      void export_sampled_transrotloc_animation(Object *ob, std::vector<float> &ctimes);
 -      void export_sampled_matrix_animation(Object *ob, std::vector<float> &ctimes);
 -      void create_sampled_animation(int channel_count, std::vector<float> &times, std::vector<float> &values, std::string, std::string label, std::string axis_name, bool is_rot);
 -
 -      void evaluate_anim_with_constraints(Object *ob, float ctime);
 +              COLLADASW::InputSemantic::Semantics semantic,
 +              bool is_rot,
 +              const std::string axis,
 +              bool transform);
  
 -      std::string create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name);
 -      std::string create_source_from_fcurve(COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id, const char *axis_name, Object *ob);
 +      int get_point_in_curve(BCBezTriple &bezt, COLLADASW::InputSemantic::Semantics semantic, bool is_angle, float *values);
 +      int get_point_in_curve(const BCAnimationCurve &curve, float sample_frame, COLLADASW::InputSemantic::Semantics semantic, bool is_angle, float *values);
  
 -      std::string create_lens_source_from_fcurve(Camera *cam, COLLADASW::InputSemantic::Semantics semantic, FCurve *fcu, const std::string& anim_id);
 +      std::string collada_tangent_from_curve(
 +              COLLADASW::InputSemantic::Semantics semantic,
 +              BCAnimationCurve &curve,
 +              const std::string& anim_id,
 +              const std::string axis_name);
  
 -      std::string create_source_from_array(COLLADASW::InputSemantic::Semantics semantic, float *v, int tot, bool is_rot, const std::string& anim_id, const char *axis_name);
 +      std::string collada_interpolation_source(const BCAnimationCurve &curve, const std::string& anim_id, std::string axis_name, bool *has_tangents);
-               
 -      std::string create_source_from_vector(COLLADASW::InputSemantic::Semantics semantic, std::vector<float> &fra, bool is_rot, const std::string& anim_id, const char *axis_name);
 +      std::string get_axis_name(std::string channel, int id);
 +      const std::string get_collada_name(std::string channel_target) const;
 +      std::string get_collada_sid(const BCAnimationCurve &curve, const std::string axis_name);
  
 -      std::string create_xyz_source(float *v, int tot, const std::string& anim_id);
 -      std::string create_4x4_source(std::vector<float> &times, std::vector<float> &values, const std::string& anim_id);
 -      std::string create_4x4_source(std::vector<float> &frames, Object * ob_arm, Bone *bone, const std::string& anim_id);
 +      /* ===================================== */
 +      /* Currently unused or not (yet?) needed */
 +      /* ===================================== */
  
 -      std::string create_interpolation_source(FCurve *fcu, const std::string& anim_id, const char *axis_name, bool *has_tangents);
 -
 -      std::string fake_interpolation_source(int tot, const std::string& anim_id, const char *axis_name);
 -
 -      // for rotation, axis name is always appended and the value of append_axis is ignored
 -      std::string get_transform_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
 -      std::string get_light_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
 -      std::string get_camera_param_sid(char *rna_path, int tm_type, const char *axis_name, bool append_axis);
 -
 -      void find_keyframes(Object *ob, std::vector<float> &fra, const char *prefix, const char *tm_name);
 -      void find_keyframes(Object *ob, std::vector<float> &fra);
 -      void find_sampleframes(Object *ob, std::vector<float> &fra);
 -
 -
 -      void make_anim_frames_from_targets(Object *ob, std::vector<float> &frames );
 -
 -      void find_rotation_frames(Object *ob, std::vector<float> &fra, const char *prefix, int rotmode);
 -
 -      // enable fcurves driving a specific bone, disable all the rest
 -      // if bone_name = NULL enable all fcurves
 -      void enable_fcurves(bAction *act, char *bone_name);
 -
 -      bool hasAnimations(Scene *sce);
 -
 -      char *extract_transform_name(char *rna_path);
 -
 -      std::string getObjectBoneName(Object *ob, const FCurve * fcu);
 -      std::string getAnimationPathId(const FCurve *fcu);
 -
 -      void getBakedPoseData(Object *obarm, int startFrame, int endFrame, bool ActionBake, bool ActionBakeFirstFrame);
 +      bool is_bone_deform_group(Bone * bone);
  
 -      bool validateConstraints(bConstraint *con);
 +#if 0
 +      BC_animation_transform_type _get_transform_type(const std::string path);
 +      void get_eul_source_for_quat(std::vector<float> &cache, Object *ob);
 +#endif
  
 +#ifdef WITH_MORPH_ANIMATION
 +      void export_morph_animation(
 +              Object *ob,
 +              BCAnimationSampler &sampler);
 +#endif
  
  };
 +
 +#endif
index 903a6ea,0000000..87cf07c
mode 100644,000000..100644
--- /dev/null
@@@ -1,679 -1,0 +1,678 @@@
-               fcu, 
-               frame_index, val, 
 +/*
 +* ***** BEGIN GPL LICENSE BLOCK *****
 +*
 +* This program is free software; you can redistribute it and/or
 +* modify it under the terms of the GNU General Public License
 +* as published by the Free Software Foundation; either version 2
 +* of the License, or (at your option) any later version.
 +*
 +* This program is distributed in the hope that it will be useful,
 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +* GNU General Public License for more details.
 +*
 +* You should have received a copy of the GNU General Public License
 +* along with this program; if not, write to the Free Software Foundation,
 +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +*
 +* The Original Code is Copyright (C) 2008 Blender Foundation.
 +* All rights reserved.
 +*
 +* Contributor(s): Blender Foundation
 +*
 +* ***** END GPL LICENSE BLOCK *****
 +*/
 +
 +#include "BCAnimationCurve.h"
 +
 +BCAnimationCurve::BCAnimationCurve()
 +{
 +      this->curve_key.set_object_type(BC_ANIMATION_TYPE_OBJECT);
 +      this->fcurve = NULL;
 +      this->curve_is_local_copy = false;
 +}
 +
 +BCAnimationCurve::BCAnimationCurve(const BCAnimationCurve &other)
 +{
 +      this->min = other.min;
 +      this->max = other.max;
 +      this->fcurve = other.fcurve;
 +      this->curve_key = other.curve_key;
 +      this->curve_is_local_copy = false;
 +      this->id_ptr = other.id_ptr;
 +
 +      /* The fcurve of the new instance is a copy and can be modified */
 +
 +      get_edit_fcurve();
 +}
 +
 +BCAnimationCurve::BCAnimationCurve(BCCurveKey key, Object *ob, FCurve *fcu)
 +{
 +      this->min = 0;
 +      this->max = 0;
 +      this->curve_key = key;
 +      this->fcurve = fcu;
 +      this->curve_is_local_copy = false;
 +      init_pointer_rna(ob);
 +}
 +
 +BCAnimationCurve::BCAnimationCurve(const BCCurveKey &key, Object *ob)
 +{
 +      this->curve_key = key;
 +      this->fcurve = NULL;
 +      this->curve_is_local_copy = false;
 +      init_pointer_rna(ob);
 +}
 +
 +void BCAnimationCurve::init_pointer_rna(Object *ob)
 +{
 +      switch (this->curve_key.get_animation_type()) {
 +      case BC_ANIMATION_TYPE_BONE:
 +      {
 +              bArmature * arm = (bArmature *)ob->data;
 +              RNA_id_pointer_create(&arm->id, &id_ptr);
 +      }
 +      break;
 +      case BC_ANIMATION_TYPE_OBJECT:
 +      {
 +              RNA_id_pointer_create(&ob->id, &id_ptr);
 +      }
 +      break;
 +      case BC_ANIMATION_TYPE_MATERIAL:
 +      {
 +              Material *ma = give_current_material(ob, curve_key.get_subindex() + 1);
 +              RNA_id_pointer_create(&ma->id, &id_ptr);
 +      }
 +      break;
 +      case BC_ANIMATION_TYPE_CAMERA:
 +      {
 +              Camera * camera = (Camera *)ob->data;
 +              RNA_id_pointer_create(&camera->id, &id_ptr);
 +      }
 +      break;
 +      case BC_ANIMATION_TYPE_LIGHT:
 +      {
 +              Lamp * lamp = (Lamp *)ob->data;
 +              RNA_id_pointer_create(&lamp->id, &id_ptr);
 +      }
 +      break;
 +      default:
 +              fprintf(stderr, "BC_animation_curve_type %d not supported", this->curve_key.get_array_index());
 +              break;
 +      }
 +}
 +
 +void BCAnimationCurve::delete_fcurve(FCurve *fcu)
 +{
 +      free_fcurve(fcu);
 +}
 +
 +FCurve *BCAnimationCurve::create_fcurve(int array_index, const char *rna_path)
 +{
 +      FCurve *fcu = (FCurve *)MEM_callocN(sizeof(FCurve), "FCurve");
 +      fcu->flag = (FCURVE_VISIBLE | FCURVE_AUTO_HANDLES | FCURVE_SELECTED);
 +      fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
 +      fcu->array_index = array_index;
 +      return fcu;
 +}
 +
 +void BCAnimationCurve::create_bezt(float frame, float output)
 +{
 +      FCurve *fcu = get_edit_fcurve();
 +      BezTriple bez;
 +      memset(&bez, 0, sizeof(BezTriple));
 +      bez.vec[1][0] = frame;
 +      bez.vec[1][1] = output;
 +      bez.ipo = U.ipo_new; /* use default interpolation mode here... */
 +      bez.f1 = bez.f2 = bez.f3 = SELECT;
 +      bez.h1 = bez.h2 = HD_AUTO;
 +      insert_bezt_fcurve(fcu, &bez, INSERTKEY_NOFLAGS);
 +      calchandles_fcurve(fcu);
 +}
 +
 +BCAnimationCurve::~BCAnimationCurve()
 +{
 +      if (curve_is_local_copy && fcurve) {
 +              //fprintf(stderr, "removed fcurve %s\n", fcurve->rna_path);
 +              delete_fcurve(fcurve);
 +              this->fcurve = NULL;
 +      }
 +}
 +
 +const bool BCAnimationCurve::is_of_animation_type(BC_animation_type type) const
 +{
 +      return curve_key.get_animation_type() == type;
 +}
 +
 +const std::string BCAnimationCurve::get_channel_target() const
 +{
 +      const std::string path = curve_key.get_path();
 +      return bc_string_after(path, '.');
 +}
 +
 +const std::string BCAnimationCurve::get_animation_name(Object *ob) const
 +{
 +      std::string name;
 +
 +      switch (curve_key.get_animation_type()) {
 +              case BC_ANIMATION_TYPE_OBJECT:
 +              {
 +                      name = id_name(ob);
 +              }
 +              break;
 +
 +              case BC_ANIMATION_TYPE_BONE:
 +              {
 +                      if (fcurve == NULL || fcurve->rna_path == NULL)
 +                              name = "";
 +                      else {
 +                              const char *boneName = BLI_str_quoted_substrN(fcurve->rna_path, "pose.bones[");
 +                              name = (boneName) ? std::string(boneName) : "";
 +                      }
 +              }
 +              break;
 +
 +              case BC_ANIMATION_TYPE_CAMERA:
 +              {
 +                      Camera *camera = (Camera *)ob->data;
 +                      name = id_name(ob) + "-" + id_name(camera) + "-camera";
 +              }
 +              break;
 +
 +              case BC_ANIMATION_TYPE_LIGHT:
 +              {
 +                      Lamp *lamp = (Lamp *)ob->data;
 +                      name = id_name(ob) + "-" + id_name(lamp) + "-light";
 +              }
 +              break;
 +
 +              case BC_ANIMATION_TYPE_MATERIAL:
 +              {
 +                      Material * ma = give_current_material(ob, this->curve_key.get_subindex() + 1);
 +                      name = id_name(ob) + "-" + id_name(ma) + "-material";
 +              }
 +              break;
 +
 +              default:
 +              {
 +                      name = "";
 +              }
 +      }
 +
 +      return name;
 +}
 +
 +const int BCAnimationCurve::get_channel_index() const
 +{
 +      return curve_key.get_array_index();
 +}
 +
 +const int BCAnimationCurve::get_subindex() const
 +{
 +      return curve_key.get_subindex();
 +}
 +
 +const std::string BCAnimationCurve::get_rna_path() const
 +{
 +      return curve_key.get_path();
 +}
 +
 +const int BCAnimationCurve::sample_count() const
 +{
 +      if (fcurve == NULL)
 +              return 0;
 +      return fcurve->totvert;
 +}
 +
 +const int BCAnimationCurve::closest_index_above(const float sample_frame, const int start_at) const
 +{
 +      if (fcurve == NULL)
 +              return -1;
 +
 +      const int cframe = fcurve->bezt[start_at].vec[1][0]; // inacurate!
 +
 +      if (fabs(cframe - sample_frame) < 0.00001)
 +              return start_at;
 +      return (fcurve->totvert > start_at + 1) ? start_at + 1 : start_at;
 +}
 +
 +const int BCAnimationCurve::closest_index_below(const float sample_frame) const
 +{
 +      if (fcurve == NULL)
 +              return -1;
 +
 +      float lower_frame = sample_frame;
 +      float upper_frame = sample_frame;
 +      int lower_index = 0;
 +      int upper_index = 0;
 +
 +      for (int fcu_index = 0; fcu_index < fcurve->totvert; ++fcu_index) {
 +              upper_index = fcu_index;
 +
 +              const int cframe = fcurve->bezt[fcu_index].vec[1][0]; // inacurate!
 +              if (cframe <= sample_frame) {
 +                      lower_frame = cframe;
 +                      lower_index = fcu_index;
 +              }
 +              if (cframe >= sample_frame) {
 +                      upper_frame = cframe;
 +                      break;
 +              }
 +      }
 +
 +      if (lower_index == upper_index)
 +              return lower_index;
 +
 +      const float fraction = float(sample_frame - lower_frame) / (upper_frame - lower_frame);
 +      return (fraction < 0.5) ? lower_index : upper_index;
 +}
 +
 +const int BCAnimationCurve::get_interpolation_type(float sample_frame) const
 +{
 +      const int index = closest_index_below(sample_frame);
 +      if (index < 0)
 +              return BEZT_IPO_BEZ;
 +      return fcurve->bezt[index].ipo;
 +}
 +
 +const FCurve *BCAnimationCurve::get_fcurve() const
 +{
 +      return fcurve;
 +}
 +
 +FCurve *BCAnimationCurve::get_edit_fcurve()
 +{
 +      if (!curve_is_local_copy) {
 +              const int index = curve_key.get_array_index();
 +              const std::string &path = curve_key.get_path();
 +              fcurve = create_fcurve(index, path.c_str());
 +
 +              /* Caution here:
 +              Replacing the pointer here is OK only because the original value
 +              of FCurve was a const pointer into Blender territory. We do not
 +              touch that! We use the local copy to prepare data for export.
 +              */
 +
 +              curve_is_local_copy = true;
 +      }
 +      return fcurve;
 +}
 +
 +void BCAnimationCurve::clean_handles()
 +{
 +      if (fcurve == NULL)
 +              fcurve = get_edit_fcurve();
 +
 +      /* Keep old bezt data for copy)*/
 +      BezTriple *old_bezts = fcurve->bezt;
 +      int totvert = fcurve->totvert;
 +      fcurve->bezt = NULL;
 +      fcurve->totvert = 0;
 +
 +      for (int i = 0; i < totvert; i++) {
 +              BezTriple *bezt = &old_bezts[i];
 +              float x = bezt->vec[1][0];
 +              float y = bezt->vec[1][1];
 +              insert_vert_fcurve(fcurve, x, y, (eBezTriple_KeyframeType)BEZKEYTYPE(bezt), INSERTKEY_NOFLAGS);
 +              BezTriple *lastb = fcurve->bezt + (fcurve->totvert - 1);
 +              lastb->f1 = lastb->f2 = lastb->f3 = 0;
 +      }
 +
 +      /* now free the memory used by the old BezTriples */
 +      if (old_bezts)
 +              MEM_freeN(old_bezts);
 +}
 +
 +const bool BCAnimationCurve::is_transform_curve() const
 +{
 +      std::string channel_target = this->get_channel_target();
 +      return (
 +              is_rotation_curve() ||
 +              channel_target == "scale" ||
 +              channel_target == "location"
 +              );
 +}
 +
 +const bool BCAnimationCurve::is_rotation_curve() const
 +{
 +      std::string channel_target = this->get_channel_target();
 +      return (
 +              channel_target == "rotation" ||
 +              channel_target == "rotation_euler" ||
 +              channel_target == "rotation_quaternion"
 +              );
 +}
 +
 +const float BCAnimationCurve::get_value(const float frame)
 +{
 +      if (fcurve) {
 +              return evaluate_fcurve(fcurve, frame);
 +      }
 +      return 0; // TODO: handle case where neither sample nor fcu exist
 +}
 +
 +void BCAnimationCurve::update_range(float val)
 +{
 +      if (val < min) {
 +                      min = val;
 +      }
 +      if (val > max) {
 +              max = val;
 +      }
 +}
 +
 +void BCAnimationCurve::init_range(float val)
 +{
 +      min = max = val;
 +}
 +
 +void BCAnimationCurve::adjust_range(const int frame_index)
 +{
 +      if (fcurve && fcurve->totvert > 1) {
 +              const float eval = evaluate_fcurve(fcurve, frame_index);
 +
 +              int first_frame = fcurve->bezt[0].vec[1][0];
 +              if (first_frame == frame_index) {
 +                      init_range(eval);
 +              }
 +              else {
 +                      update_range(eval);
 +              }
 +      }
 +}
 +
 +void BCAnimationCurve::add_value(const float val, const int frame_index)
 +{
 +      FCurve *fcu = get_edit_fcurve();
 +      fcu->auto_smoothing = FCURVE_SMOOTH_CONT_ACCEL;
 +      insert_vert_fcurve(
++              fcu,
++              frame_index, val,
 +              BEZT_KEYTYPE_KEYFRAME,
 +              INSERTKEY_NOFLAGS);
 +
 +      if (fcu->totvert == 1) {
 +              init_range(val);
 +      }
 +      else {
 +              update_range(val);
 +      }
 +}
 +
 +bool BCAnimationCurve::add_value_from_matrix(const BCSample &sample, const int frame_index)
 +{
 +      int array_index = curve_key.get_array_index();
 +
 +      /* transformation curves are feeded directly from the transformation matrix
 +      * to resolve parent inverse matrix issues with object hierarchies.
 +      * Maybe this can be unified with the
 +      */
 +      const std::string channel_target = get_channel_target();
 +      float val = 0;
 +      /* Pick the value from the sample according to the definition of the FCurve */
 +      bool good = sample.get_value(channel_target, array_index, &val);
 +      if (good) {
 +              add_value(val, frame_index);
 +      }
 +      return good;
 +}
 +
 +bool BCAnimationCurve::add_value_from_rna(const int frame_index)
 +{
 +      PointerRNA ptr;
 +      PropertyRNA *prop;
 +      float value = 0.0f;
 +      int array_index = curve_key.get_array_index();
 +      const std::string full_path = curve_key.get_full_path();
 +
 +      /* get property to read from, and get value as appropriate */
 +      bool path_resolved = RNA_path_resolve_full(&id_ptr, full_path.c_str(), &ptr, &prop, &array_index);
 +      if (!path_resolved && array_index == 0) {
 +              const std::string rna_path = curve_key.get_path();
 +              path_resolved = RNA_path_resolve_full(&id_ptr, rna_path.c_str(), &ptr, &prop, &array_index);
 +      }
 +
 +      if (path_resolved) {
 +              bool is_array = RNA_property_array_check(prop);
 +              if (is_array) {
 +                      /* array */
 +                      if ((array_index >= 0) && (array_index < RNA_property_array_length(&ptr, prop))) {
 +                              switch (RNA_property_type(prop)) {
 +                              case PROP_BOOLEAN:
 +                                      value = (float)RNA_property_boolean_get_index(&ptr, prop, array_index);
 +                                      break;
 +                              case PROP_INT:
 +                                      value = (float)RNA_property_int_get_index(&ptr, prop, array_index);
 +                                      break;
 +                              case PROP_FLOAT:
 +                                      value = RNA_property_float_get_index(&ptr, prop, array_index);
 +                                      break;
 +                              default:
 +                                      break;
 +                              }
 +                      }
 +                      else {
 +                              fprintf(stderr, "Out of Bounds while reading data for Curve %s\n", curve_key.get_full_path().c_str());
 +                              return false;
 +                      }
 +              }
 +              else {
 +                      /* not an array */
 +                      switch (RNA_property_type(prop)) {
 +                      case PROP_BOOLEAN:
 +                              value = (float)RNA_property_boolean_get(&ptr, prop);
 +                              break;
 +                      case PROP_INT:
 +                              value = (float)RNA_property_int_get(&ptr, prop);
 +                              break;
 +                      case PROP_FLOAT:
 +                              value = RNA_property_float_get(&ptr, prop);
 +                              break;
 +                      case PROP_ENUM:
 +                              value = (float)RNA_property_enum_get(&ptr, prop);
 +                              break;
 +                      default:
 +                              fprintf(stderr, "property type %d not supported for Curve %s\n", RNA_property_type(prop), curve_key.get_full_path().c_str());
 +                              return false;
 +                              break;
 +                      }
 +              }
 +      }
 +      else {
 +              /* path couldn't be resolved */
 +              fprintf(stderr, "Path not recognized for Curve %s\n", curve_key.get_full_path().c_str());
 +              return false;
 +      }
 +
 +      add_value(value, frame_index);
 +      return true;
 +}
 +
 +void BCAnimationCurve::get_value_map(BCValueMap &value_map)
 +{
 +      value_map.clear();
 +      if (fcurve == NULL) {
 +              return;
 +      }
 +
 +      for (int i = 0; i < fcurve->totvert; i++) {
 +              const float frame = fcurve->bezt[i].vec[1][0];
 +              const float val = fcurve->bezt[i].vec[1][1];
 +              value_map[frame] = val;
 +      }
 +}
 +
 +void BCAnimationCurve::get_frames(BCFrames &frames) const
 +{
 +      frames.clear();
 +      if (fcurve) {
 +              for (int i = 0; i < fcurve->totvert; i++) {
 +                      const float val = fcurve->bezt[i].vec[1][0];
 +                      frames.push_back(val);
 +              }
 +      }
 +}
 +
 +void BCAnimationCurve::get_values(BCValues &values) const
 +{
 +      values.clear();
 +      if (fcurve) {
 +              for (int i = 0; i < fcurve->totvert; i++) {
 +                      const float val = fcurve->bezt[i].vec[1][1];
 +                      values.push_back(val);
 +              }
 +      }
 +}
 +
 +bool BCAnimationCurve::is_animated()
 +{
 +      static float MIN_DISTANCE = 0.00001;
 +      return fabs(max - min) > MIN_DISTANCE;
 +}
 +
 +
 +bool BCAnimationCurve::is_keyframe(int frame) {
 +      if (this->fcurve == NULL)
 +              return false;
 +
 +      for (int i = 0; i < fcurve->totvert; ++i) {
 +              const int cframe = nearbyint(fcurve->bezt[i].vec[1][0]);
 +              if (cframe == frame)
 +                      return true;
 +              if (cframe > frame)
 +                      break;
 +      }
 +      return false;
 +}
 +
 +/* Needed for adding a BCAnimationCurve into a BCAnimationCurveSet */
 +inline bool operator< (const BCAnimationCurve& lhs, const BCAnimationCurve& rhs) {
 +      std::string lhtgt = lhs.get_channel_target();
 +      std::string rhtgt = rhs.get_channel_target();
 +      if (lhtgt == rhtgt)
 +      {
 +              const int lha = lhs.get_channel_index();
 +              const int rha = rhs.get_channel_index();
 +              return lha < rha;
 +      }
 +      else
 +              return lhtgt < rhtgt;
 +}
 +
 +BCCurveKey::BCCurveKey()
 +{
 +      this->key_type = BC_ANIMATION_TYPE_OBJECT;
 +      this->rna_path = "";
 +      this->curve_array_index = 0;
 +      this->curve_subindex = -1;
 +}
 +
 +BCCurveKey::BCCurveKey(const BC_animation_type type, const std::string path, const int array_index, const int subindex)
 +{
 +      this->key_type = type;
 +      this->rna_path = path;
 +      this->curve_array_index = array_index;
 +      this->curve_subindex = subindex;
 +}
 +
 +void BCCurveKey::operator=(const BCCurveKey &other)
 +{
 +      this->key_type = other.key_type;
 +      this->rna_path = other.rna_path;
 +      this->curve_array_index = other.curve_array_index;
 +      this->curve_subindex = other.curve_subindex;
 +}
 +
 +const std::string BCCurveKey::get_full_path() const
 +{
 +      return this->rna_path + '[' + std::to_string(this->curve_array_index) + ']';
 +}
 +
 +const std::string BCCurveKey::get_path() const
 +{
 +      return this->rna_path;
 +}
 +
 +const int BCCurveKey::get_array_index() const
 +{
 +      return this->curve_array_index;
 +}
 +
 +const int BCCurveKey::get_subindex() const
 +{
 +      return this->curve_subindex;
 +}
 +
 +void BCCurveKey::set_object_type(BC_animation_type object_type)
 +{
 +      this->key_type = object_type;
 +}
 +
 +const BC_animation_type BCCurveKey::get_animation_type() const
 +{
 +      return this->key_type;
 +}
 +
 +const bool BCCurveKey::operator<(const BCCurveKey &other) const
 +{
 +      /* needed for using this class as key in maps and sets */
 +      if (this->key_type != other.key_type)
 +              return this->key_type < other.key_type;
 +
 +      if (this->curve_subindex != other.curve_subindex)
 +              return this->curve_subindex < other.curve_subindex;
 +
 +      if (this->rna_path != other.rna_path)
 +              return this->rna_path < other.rna_path;
 +
 +      return this->curve_array_index < other.curve_array_index;
 +}
 +
 +BCBezTriple::BCBezTriple(BezTriple &bezt) :
 +      bezt(bezt) {}
 +
 +const float BCBezTriple::get_frame() const
 +{
 +      return bezt.vec[1][0];
 +}
 +
 +const float BCBezTriple::get_time(Scene *scene) const
 +{
 +      return FRA2TIME(bezt.vec[1][0]);
 +}
 +
 +const float BCBezTriple::get_value() const
 +{
 +      return bezt.vec[1][1];
 +}
 +
 +const float BCBezTriple::get_angle() const
 +{
 +      return RAD2DEGF(get_value());
 +}
 +
 +void BCBezTriple::get_in_tangent(Scene *scene, float point[2], bool as_angle) const
 +{
 +      get_tangent(scene, point, as_angle, 0);
 +}
 +
 +void BCBezTriple::get_out_tangent(Scene *scene, float point[2], bool as_angle) const
 +{
 +      get_tangent(scene, point, as_angle, 2);
 +}
 +
 +void BCBezTriple::get_tangent(Scene *scene, float point[2], bool as_angle, int index) const
 +{
 +      point[0] = FRA2TIME(bezt.vec[index][0]);
 +      if (bezt.ipo != BEZT_IPO_BEZ) {
 +              /* We're in a mixed interpolation scenario, set zero as it's irrelevant but value might contain unused data */
 +              point[0] = 0;
 +              point[1] = 0;
 +      }
 +      else if (as_angle) {
 +              point[1] = RAD2DEGF(bezt.vec[index][1]);
 +      }
 +      else {
 +              point[1] = bezt.vec[index][1];
 +      }
 +}
index e736569,0000000..5c0c1b4
mode 100644,000000..100644
--- /dev/null
@@@ -1,645 -1,0 +1,645 @@@
-    Add sampled values to FCurve 
 +/*
 +* ***** BEGIN GPL LICENSE BLOCK *****
 +*
 +* This program is free software; you can redistribute it and/or
 +* modify it under the terms of the GNU General Public License
 +* as published by the Free Software Foundation; either version 2
 +* of the License, or (at your option) any later version.
 +*
 +* This program is distributed in the hope that it will be useful,
 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +* GNU General Public License for more details.
 +*
 +* You should have received a copy of the GNU General Public License
 +* along with this program; if not, write to the Free Software Foundation,
 +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +*
 +* The Original Code is Copyright (C) 2008 Blender Foundation.
 +* All rights reserved.
 +*
 +* Contributor(s): Blender Foundation
 +*
 +* ***** END GPL LICENSE BLOCK *****
 +*/
 +
 +#include <vector>
 +#include <map>
 +#include <algorithm> // std::find
 +
 +#include "ExportSettings.h"
 +#include "BCAnimationCurve.h"
 +#include "BCAnimationSampler.h"
 +#include "collada_utils.h"
 +
 +extern "C" {
 +#include "BKE_action.h"
 +#include "BKE_constraint.h"
 +#include "BKE_key.h"
 +#include "BKE_main.h"
 +#include "BKE_library.h"
 +#include "BKE_material.h"
 +#include "BLI_listbase.h"
 +#include "DNA_anim_types.h"
 +#include "DNA_scene_types.h"
 +#include "DNA_key_types.h"
 +#include "DNA_constraint_types.h"
 +#include "ED_object.h"
 +}
 +
 +static std::string EMPTY_STRING;
 +static BCAnimationCurveMap BCEmptyAnimationCurves;
 +
 +BCAnimationSampler::BCAnimationSampler(BlenderContext &blender_context, BCObjectSet &object_set):
 +      blender_context(blender_context)
 +{
 +      BCObjectSet::iterator it;
 +      for (it = object_set.begin(); it != object_set.end(); ++it) {
 +              Object *ob = *it;
 +              add_object(ob);
 +      }
 +}
 +
 +BCAnimationSampler::~BCAnimationSampler()
 +{
 +      BCAnimationObjectMap::iterator it;
 +      for (it = objects.begin(); it != objects.end(); ++it) {
 +              BCAnimation *animation = it->second;
 +              delete animation;
 +      }
 +}
 +
 +void BCAnimationSampler::add_object(Object *ob)
 +{
 +      BCAnimation *animation = new BCAnimation(blender_context.get_context(), ob);
 +      objects[ob] = animation;
 +
 +      initialize_keyframes(animation->frame_set, ob);
 +      initialize_curves(animation->curve_map, ob);
 +}
 +
 +BCAnimationCurveMap *BCAnimationSampler::get_curves(Object *ob)
 +{
 +      BCAnimation &animation = *objects[ob];
 +      if (animation.curve_map.size() == 0)
 +              initialize_curves(animation.curve_map, ob);
 +      return &animation.curve_map;
 +}
 +
 +static void get_sample_frames(BCFrameSet &sample_frames, int sampling_rate, bool keyframe_at_end, Scene *scene)
 +{
 +      sample_frames.clear();
 +
 +      if (sampling_rate < 1)
 +              return; // no sample frames in this case
 +
 +      float sfra = scene->r.sfra;
 +      float efra = scene->r.efra;
 +
 +      int frame_index;
 +      for (frame_index = nearbyint(sfra); frame_index < efra; frame_index += sampling_rate) {
 +              sample_frames.insert(frame_index);
 +      }
 +
 +      if (frame_index >= efra && keyframe_at_end)
 +      {
 +              sample_frames.insert(efra);
 +      }
 +}
 +
 +static bool is_object_keyframe(Object *ob, int frame_index)
 +{
 +      return false;
 +}
 +
 +static void add_keyframes_from(bAction *action, BCFrameSet &frameset)
 +{
 +      if (action) {
 +              FCurve *fcu = NULL;
 +              for (fcu = (FCurve *)action->curves.first; fcu; fcu = fcu->next) {
 +                      BezTriple  *bezt = fcu->bezt;
 +                      for (int i = 0; i < fcu->totvert; bezt++, i++) {
 +                              int frame_index = nearbyint(bezt->vec[1][0]);
 +                              frameset.insert(frame_index);
 +                      }
 +              }
 +      }
 +}
 +
 +void BCAnimationSampler::check_property_is_animated(BCAnimation &animation, float *ref, float *val, std::string data_path, int length)
 +{
 +      for (int array_index =0; array_index < length; ++array_index) {
 +              if (!bc_in_range(ref[length], val[length], 0.00001)) {
 +                      BCCurveKey key(BC_ANIMATION_TYPE_OBJECT, data_path, array_index);
 +                      BCAnimationCurveMap::iterator it = animation.curve_map.find(key);
 +                      if (it == animation.curve_map.end()) {
 +                              animation.curve_map[key] = new BCAnimationCurve(key, animation.get_reference());
 +                      }
 +              }
 +      }
 +}
 +
 +void BCAnimationSampler::update_animation_curves(BCAnimation &animation, BCSample &sample, Object *ob, int frame)
 +{
 +      BCAnimationCurveMap::iterator it;
 +      for (it = animation.curve_map.begin(); it != animation.curve_map.end(); ++it) {
 +              BCAnimationCurve *curve = it->second;
 +              if (curve->is_transform_curve()) {
 +                      curve->add_value_from_matrix(sample, frame);
 +              }
 +              else {
 +                      curve->add_value_from_rna(frame);
 +              }
 +      }
 +}
 +
 +BCSample &BCAnimationSampler::sample_object(Object *ob, int frame_index, bool for_opensim)
 +{
 +      BCSample &ob_sample = sample_data.add(ob, frame_index);
 +
 +      if (ob->type == OB_ARMATURE) {
 +              bPoseChannel *pchan;
 +              for (pchan = (bPoseChannel *)ob->pose->chanbase.first; pchan; pchan = pchan->next) {
 +                      Bone *bone = pchan->bone;
 +                      Matrix bmat;
 +                      if (bc_bone_matrix_local_get(ob, bone, bmat, for_opensim)) {
 +                              ob_sample.add_bone_matrix(bone, bmat);
 +                      }
 +              }
 +      }
 +      return ob_sample;
 +}
 +
 +void BCAnimationSampler::sample_scene(
 +      int sampling_rate,
 +      int keyframe_at_end,
 +      bool for_opensim,
 +      bool keep_keyframes,
 +      BC_export_animation_type export_animation_type)
 +{
 +      Scene *scene = blender_context.get_scene();
 +      BCFrameSet scene_sample_frames;
 +      get_sample_frames(scene_sample_frames, sampling_rate, keyframe_at_end, scene);
 +      BCFrameSet::iterator it;
 +
 +      int startframe = scene->r.sfra;
 +      int endframe = scene->r.efra;
 +
 +      for (int frame_index = startframe; frame_index <= endframe; ++frame_index) {
 +              /* Loop over all frames and decide for each frame if sampling is necessary */
 +              bool is_scene_sample_frame = false;
 +              bool needs_update = true;
 +              if (scene_sample_frames.find(frame_index) != scene_sample_frames.end()) {
 +                      bc_update_scene(blender_context, frame_index);
 +                      needs_update = false;
 +                      is_scene_sample_frame = true;
 +              }
 +
 +              bool needs_sampling = is_scene_sample_frame || keep_keyframes || export_animation_type == BC_ANIMATION_EXPORT_KEYS;
 +              if (!needs_sampling) {
 +                      continue;
 +              }
 +
 +              BCAnimationObjectMap::iterator obit;
 +              for (obit = objects.begin(); obit != objects.end(); ++obit) {
 +                      Object *ob = obit->first;
 +                      BCAnimation *animation = obit->second;
 +                      BCFrameSet &object_keyframes = animation->frame_set;
 +                      if (is_scene_sample_frame || object_keyframes.find(frame_index) != object_keyframes.end()) {
 +
 +                              if (needs_update) {
 +                                      bc_update_scene(blender_context, frame_index);
 +                                      needs_update = false;
 +                              }
 +
 +                              BCSample &sample = sample_object(ob, frame_index, for_opensim);
 +                              update_animation_curves(*animation, sample, ob, frame_index);
 +                      }
 +              }
 +      }
 +}
 +
 +bool BCAnimationSampler::is_animated_by_constraint(Object *ob, ListBase *conlist, std::set<Object *> &animated_objects)
 +{
 +      bConstraint *con;
 +      for (con = (bConstraint *)conlist->first; con; con = con->next) {
 +              ListBase targets = { NULL, NULL };
 +
 +              const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
 +
 +              if (!bc_validateConstraints(con))
 +                      continue;
 +
 +              if (cti && cti->get_constraint_targets) {
 +                      bConstraintTarget *ct;
 +                      Object *obtar;
 +                      cti->get_constraint_targets(con, &targets);
 +                      for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
 +                              obtar = ct->tar;
 +                              if (obtar) {
 +                                      if (animated_objects.find(obtar) != animated_objects.end())
 +                                              return true;
 +                              }
 +                      }
 +              }
 +      }
 +      return false;
 +}
 +
 +void BCAnimationSampler::find_depending_animated(std::set<Object *> &animated_objects, std::set<Object *> &candidates)
 +{
 +      bool found_more;
 +      do {
 +              found_more = false;
 +              std::set<Object *>::iterator it;
 +              for (it = candidates.begin(); it != candidates.end(); ++it) {
 +                      Object *cob = *it;
 +                      ListBase *conlist = get_active_constraints(cob);
 +                      if (is_animated_by_constraint(cob, conlist, animated_objects)) {
 +                              animated_objects.insert(cob);
 +                              candidates.erase(cob);
 +                              found_more = true;
 +                              break;
 +                      }
 +              }
 +      } while (found_more && candidates.size() > 0);
 +}
 +
 +void BCAnimationSampler::get_animated_from_export_set(std::set<Object *> &animated_objects, LinkNode &export_set)
 +{
 +      /*
 +      Check if this object is animated. That is: Check if it has its own action, or
 +
 +      - Check if it has constraints to other objects
 +      - at least one of the other objects is animated as well
 +      */
 +
 +      animated_objects.clear();
 +      std::set<Object *> static_objects;
 +      std::set<Object *> candidates;
 +
 +      LinkNode *node;
 +      for (node = &export_set; node; node = node->next) {
 +              Object *cob = (Object *)node->link;
 +              if (bc_has_animations(cob)) {
 +                      animated_objects.insert(cob);
 +              }
 +              else {
 +                      ListBase conlist = cob->constraints;
 +                      if (conlist.first)
 +                              candidates.insert(cob);
 +              }
 +      }
 +      find_depending_animated(animated_objects, candidates);
 +}
 +
 +void BCAnimationSampler::get_object_frames(BCFrames &frames, Object *ob)
 +{
 +      sample_data.get_frames(ob, frames);
 +}
 +
 +void BCAnimationSampler::get_bone_frames(BCFrames &frames, Object *ob, Bone *bone)
 +{
 +      sample_data.get_frames(ob, bone, frames);
 +}
 +
 +bool BCAnimationSampler::get_bone_samples(BCMatrixSampleMap &samples, Object *ob, Bone *bone)
 +{
 +      sample_data.get_matrices(ob, bone, samples);
 +      return bc_is_animated(samples);
 +}
 +
 +bool BCAnimationSampler::get_object_samples(BCMatrixSampleMap &samples, Object *ob)
 +{
 +      sample_data.get_matrices(ob, samples);
 +      return bc_is_animated(samples);
 +}
 +
 +#if 0
 +/*
-                       
++   Add sampled values to FCurve
 +   If no FCurve exists, create a temporary FCurve;
 +   Note: The temporary FCurve will later be removed when the
 +   BCAnimationSampler is removed (by its destructor)
 +
 +   curve: The curve to whioch the data is added
 +   matrices: The set of matrix values from where the data is taken
 +   animation_type BC_ANIMATION_EXPORT_SAMPLES: Use all matrix data
 +   animation_type BC_ANIMATION_EXPORT_KEYS: Only take data from matrices for keyframes
 +*/
 +
 +void BCAnimationSampler::add_value_set(
 +      BCAnimationCurve &curve,
 +      BCFrameSampleMap &samples,
 +      BC_export_animation_type animation_type)
 +{
 +      int array_index = curve.get_array_index();
 +      const BC_animation_transform_type tm_type = curve.get_transform_type();
 +
 +      BCFrameSampleMap::iterator it;
 +      for (it = samples.begin(); it != samples.end(); ++it) {
 +              const int frame_index = nearbyint(it->first);
 +              if (animation_type == BC_ANIMATION_EXPORT_SAMPLES || curve.is_keyframe(frame_index)) {
 +
 +                      const BCSample *sample = it->second;
 +                      float val = 0;
 +
 +                      int subindex = curve.get_subindex();
 +                      bool good;
 +                      if (subindex == -1) {
 +                              good = sample->get_value(tm_type, array_index, &val);
 +                      }
 +                      else {
 +                              good = sample->get_value(tm_type, array_index, &val, subindex);
 +                      }
++
 +                      if (good) {
 +                              curve.add_value(val, frame_index);
 +                      }
 +              }
 +      }
 +      curve.remove_unused_keyframes();
 +      curve.calchandles();
 +}
 +#endif
 +
 +void BCAnimationSampler::generate_transform(
 +      Object *ob,
 +    const BCCurveKey &key,
 +      BCAnimationCurveMap &curves)
 +{
 +      BCAnimationCurveMap::const_iterator it = curves.find(key);
 +      if (it == curves.end()) {
 +              curves[key] = new BCAnimationCurve(key, ob);
 +      }
 +}
 +
 +void BCAnimationSampler::generate_transforms(
 +      Object *ob,
 +      const std::string prep,
 +      const BC_animation_type type,
 +      BCAnimationCurveMap &curves)
 +{
 +      generate_transform(ob, BCCurveKey(type, prep+"location", 0), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"location", 1), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"location", 2), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"rotation_euler", 0), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"rotation_euler", 1), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"rotation_euler", 2), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"scale", 0), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"scale", 1), curves);
 +      generate_transform(ob, BCCurveKey(type, prep+"scale", 2), curves);
 +}
 +
 +void BCAnimationSampler::generate_transforms(Object *ob, Bone *bone, BCAnimationCurveMap &curves)
 +{
 +      std::string prep = "pose.bones[\"" + std::string(bone->name) + "\"].";
 +      generate_transforms(ob, prep, BC_ANIMATION_TYPE_BONE, curves);
 +
 +      for (Bone *child = (Bone *)bone->childbase.first; child; child = child->next)
 +              generate_transforms(ob, child, curves);
 +}
 +
 +/*
 +* Collect all keyframes from all animation curves related to the object
 +* The bc_get... functions check for NULL and correct object type
 +* The add_keyframes_from() function checks for NULL
 +*/
 +void BCAnimationSampler::initialize_keyframes(BCFrameSet &frameset, Object *ob)
 +{
 +      frameset.clear();
 +      add_keyframes_from(bc_getSceneObjectAction(ob), frameset);
 +      add_keyframes_from(bc_getSceneCameraAction(ob), frameset);
 +      add_keyframes_from(bc_getSceneLampAction(ob), frameset);
 +
 +      for (int a = 0; a < ob->totcol; a++) {
 +              Material *ma = give_current_material(ob, a + 1);
 +              add_keyframes_from(bc_getSceneMaterialAction(ma), frameset);
 +      }
 +}
 +
 +void BCAnimationSampler::initialize_curves(BCAnimationCurveMap &curves, Object *ob)
 +{
 +      BC_animation_type object_type = BC_ANIMATION_TYPE_OBJECT;
 +
 +      bAction *action = bc_getSceneObjectAction(ob);
 +      if (action) {
 +              FCurve *fcu = (FCurve *)action->curves.first;
 +
 +              for (; fcu; fcu = fcu->next) {
 +                      object_type = BC_ANIMATION_TYPE_OBJECT;
 +                      if (ob->type == OB_ARMATURE) {
 +                              char *boneName = BLI_str_quoted_substrN(fcu->rna_path, "pose.bones[");
 +                              if (boneName) {
 +                                      object_type = BC_ANIMATION_TYPE_BONE;
 +                              }
 +                      }
 +
 +                      /* Adding action curves on object */
 +                      BCCurveKey key(object_type, fcu->rna_path, fcu->array_index);
 +                      curves[key] = new BCAnimationCurve(key, ob, fcu);
 +              }
 +      }
 +
 +      /* Add missing curves */
 +      object_type = BC_ANIMATION_TYPE_OBJECT;
 +      generate_transforms(ob, EMPTY_STRING, object_type, curves);
 +      if (ob->type == OB_ARMATURE) {
 +              bArmature *arm = (bArmature *)ob->data;
 +              for (Bone *root_bone = (Bone *)arm->bonebase.first; root_bone; root_bone = root_bone->next)
 +                      generate_transforms(ob, root_bone, curves);
 +      }
 +
 +      /* Add curves on Object->data actions */
 +      action = NULL;
 +      if (ob->type == OB_CAMERA) {
 +              action = bc_getSceneCameraAction(ob);
 +              object_type = BC_ANIMATION_TYPE_CAMERA;
 +      }
 +      else if (ob->type == OB_LAMP) {
 +              action = bc_getSceneLampAction(ob);
 +              object_type = BC_ANIMATION_TYPE_LIGHT;
 +      }
 +
 +      if (action) {
 +              /* Add lamp action or Camera action */
 +              FCurve *fcu = (FCurve *)action->curves.first;
 +              for (; fcu; fcu = fcu->next) {
 +                      BCCurveKey key(object_type, fcu->rna_path, fcu->array_index);
 +                      curves[key] = new BCAnimationCurve(key, ob, fcu);
 +              }
 +      }
 +
 +      /* Add curves on Object->material actions*/
 +      object_type = BC_ANIMATION_TYPE_MATERIAL;
 +      for (int a = 0; a < ob->totcol; a++) {
 +              /* Export Material parameter animations. */
 +              Material *ma = give_current_material(ob, a + 1);
 +              if (ma) {
 +                      action = bc_getSceneMaterialAction(ma);
 +                      if (action) {
 +                              /* isMatAnim = true; */
 +                              FCurve *fcu = (FCurve *)action->curves.first;
 +                              for (; fcu; fcu = fcu->next) {
 +                                      BCCurveKey key(object_type, fcu->rna_path, fcu->array_index, a);
 +                                      curves[key] = new BCAnimationCurve(key, ob, fcu);
 +                              }
 +                      }
 +              }
 +      }
 +}
 +
 +/* ==================================================================== */
 +
 +BCSample &BCSampleFrame::add(Object *ob)
 +{
 +      BCSample *sample = new BCSample(ob);
 +      sampleMap[ob] = sample;
 +      return *sample;
 +}
 +
 +/* Get the matrix for the given key, returns Unity when the key does not exist */
 +const BCSample *BCSampleFrame::get_sample(Object *ob) const
 +{
 +      BCSampleMap::const_iterator it = sampleMap.find(ob);
 +      if (it == sampleMap.end()) {
 +              return NULL;
 +      }
 +      return it->second;
 +}
 +
 +const BCMatrix *BCSampleFrame::get_sample_matrix(Object *ob) const
 +{
 +      BCSampleMap::const_iterator it = sampleMap.find(ob);
 +      if (it == sampleMap.end()) {
 +              return NULL;
 +      }
 +      BCSample *sample = it->second;
 +      return &sample->get_matrix();
 +}
 +
 +/* Get the matrix for the given Bone, returns Unity when the Objewct is not sampled */
 +const BCMatrix *BCSampleFrame::get_sample_matrix(Object *ob, Bone *bone) const
 +{
 +      BCSampleMap::const_iterator it = sampleMap.find(ob);
 +      if (it == sampleMap.end()) {
 +              return NULL;
 +      }
 +
 +      BCSample *sample = it->second;
 +      const BCMatrix *bc_bone = sample->get_matrix(bone);
 +      return bc_bone;
 +}
 +
 +/* Check if the key is in this BCSampleFrame */
 +const bool BCSampleFrame::has_sample_for(Object *ob) const
 +{
 +      return sampleMap.find(ob) != sampleMap.end();
 +}
 +
 +/* Check if the Bone is in this BCSampleFrame */
 +const bool BCSampleFrame::has_sample_for(Object *ob, Bone *bone) const
 +{
 +      const BCMatrix *bc_bone = get_sample_matrix(ob, bone);
 +      return (bc_bone);
 +}
 +
 +/* ==================================================================== */
 +
 +BCSample &BCSampleFrameContainer::add(Object *ob, int frame_index)
 +{
 +      BCSampleFrame &frame = sample_frames[frame_index];
 +      return frame.add(ob);
 +}
 +
 +
 +/* ====================================================== */
 +/* Below are the getters which we need to export the data */
 +/* ====================================================== */
 +
 +/* Return either the BCSampleFrame or NULL if frame does not exist*/
 +BCSampleFrame * BCSampleFrameContainer::get_frame(int frame_index)
 +{
 +      BCSampleFrameMap::iterator it = sample_frames.find(frame_index);
 +      BCSampleFrame *frame = (it == sample_frames.end()) ? NULL : &it->second;
 +      return frame;
 +}
 +
 +/* Return a list of all frames that need to be sampled */
 +const int BCSampleFrameContainer::get_frames(std::vector<int> &frames) const
 +{
 +      frames.clear(); // safety;
 +      BCSampleFrameMap::const_iterator it;
 +      for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
 +              frames.push_back(it->first);
 +      }
 +      return frames.size();
 +}
 +
 +const int BCSampleFrameContainer::get_frames(Object *ob, BCFrames &frames) const
 +{
 +      frames.clear(); // safety;
 +      BCSampleFrameMap::const_iterator it;
 +      for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
 +              const BCSampleFrame &frame = it->second;
 +              if (frame.has_sample_for(ob)) {
 +                      frames.push_back(it->first);
 +              }
 +      }
 +      return frames.size();
 +}
 +
 +const int BCSampleFrameContainer::get_frames(Object *ob, Bone *bone, BCFrames &frames) const
 +{
 +      frames.clear(); // safety;
 +      BCSampleFrameMap::const_iterator it;
 +      for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
 +              const BCSampleFrame &frame = it->second;
 +              if (frame.has_sample_for(ob, bone)) {
 +                      frames.push_back(it->first);
 +              }
 +      }
 +      return frames.size();
 +}
 +
 +const int BCSampleFrameContainer::get_samples(Object *ob, BCFrameSampleMap &samples) const
 +{
 +      samples.clear(); // safety;
 +      BCSampleFrameMap::const_iterator it;
 +      for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
 +              const BCSampleFrame &frame = it->second;
 +              const BCSample *sample = frame.get_sample(ob);
 +              if (sample) {
 +                      samples[it->first] = sample;
 +              }
 +      }
 +      return samples.size();
 +}
 +
 +const int BCSampleFrameContainer::get_matrices(Object *ob, BCMatrixSampleMap &samples) const
 +{
 +      samples.clear(); // safety;
 +      BCSampleFrameMap::const_iterator it;
 +      for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
 +              const BCSampleFrame &frame = it->second;
 +              const BCMatrix *matrix = frame.get_sample_matrix(ob);
 +              if (matrix) {
 +                      samples[it->first] = matrix;
 +              }
 +      }
 +      return samples.size();
 +}
 +
 +const int BCSampleFrameContainer::get_matrices(Object *ob, Bone *bone, BCMatrixSampleMap &samples) const
 +{
 +      samples.clear(); // safety;
 +      BCSampleFrameMap::const_iterator it;
 +      for (it = sample_frames.begin(); it != sample_frames.end(); ++it) {
 +              const BCSampleFrame &frame = it->second;
 +              const BCMatrix *sample = frame.get_sample_matrix(ob, bone);
 +              if (sample) {
 +                      samples[it->first] = sample;
 +              }
 +      }
 +      return samples.size();
 +}
index 4becafb,0000000..8bf2967
mode 100644,000000..100644
--- /dev/null
@@@ -1,205 -1,0 +1,205 @@@
-       * An Animation is made of multiple FCurves where each FCurve can 
 +/*
 +* ***** BEGIN GPL LICENSE BLOCK *****
 +*
 +* This program is free software; you can redistribute it and/or
 +* modify it under the terms of the GNU General Public License
 +* as published by the Free Software Foundation; either version 2
 +* of the License, or (at your option) any later version.
 +*
 +* This program is distributed in the hope that it will be useful,
 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +* GNU General Public License for more details.
 +*
 +* You should have received a copy of the GNU General Public License
 +* along with this program; if not, write to the Free Software Foundation,
 +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +*
 +* The Original Code is Copyright (C) 2008 Blender Foundation.
 +* All rights reserved.
 +*
 +* Contributor(s): Blender Foundation
 +*
 +* ***** END GPL LICENSE BLOCK *****
 +*/
 +
 +#ifndef __BC_ANIMATION_CURVE_CONTAINER_H__
 +#define __BC_ANIMATION_CURVE_CONTAINER_H__
 +
 +#include "BCAnimationCurve.h"
 +#include "BCSampleData.h"
 +#include "collada_utils.h"
 +
 +extern "C" {
 +#include "BKE_action.h"
 +#include "BKE_library.h"
 +#include "BLI_math_rotation.h"
 +#include "DNA_action_types.h"
 +}
 +
 +/* Collection of animation curves */
 +class BCAnimation {
 +private:
 +      Object *reference = NULL;
 +      bContext *mContext;
 +
 +
 +public:
 +      BCFrameSet frame_set;
 +      BCAnimationCurveMap curve_map;
 +
 +      BCAnimation(bContext *C, Object *ob):
 +              mContext(C)
 +      {
 +              Main *bmain = CTX_data_main(mContext);
 +              reference = BKE_object_copy(bmain, ob);
 +      }
 +
 +      ~BCAnimation()
 +      {
 +              BCAnimationCurveMap::iterator it;
 +              for (it = curve_map.begin(); it != curve_map.end(); ++it) {
 +                      delete it->second;
 +              }
 +
 +              if (reference && reference->id.us == 0)
 +              {
 +                      Main *bmain = CTX_data_main(mContext);
 +                      BKE_libblock_delete(bmain, &reference->id);
 +              }
 +              curve_map.clear();
 +      }
 +
 +      Object *get_reference()
 +      {
 +              return reference;
 +      }
 +};
 +
 +typedef std::map<Object *, BCAnimation *> BCAnimationObjectMap;
 +
 +class BCSampleFrame {
 +
 +      /*
 +      Each frame on the timeline that needs to be sampled will have
 +      one BCSampleFrame where we collect sample information about all objects
 +      that need to be sampled for that frame.
 +      */
 +
 +private:
 +      BCSampleMap sampleMap;
 +
 +public:
 +
 +      ~BCSampleFrame()
 +      {
 +              BCSampleMap::iterator it;
 +              for (it = sampleMap.begin(); it != sampleMap.end(); ++it) {
 +                      BCSample *sample = it->second;
 +                      delete sample;
 +              }
 +              sampleMap.clear();
 +      }
 +
 +      BCSample &add(Object *ob);
 +
 +      /* Following methods return NULL if object is not in the sampleMap*/
 +      const BCSample *get_sample(Object *ob) const;
 +      const BCMatrix *get_sample_matrix(Object *ob) const;
 +      const BCMatrix *get_sample_matrix(Object *ob, Bone *bone) const;
 +
 +      const bool has_sample_for(Object *ob) const;
 +      const bool has_sample_for(Object *ob, Bone *bone) const;
 +};
 +
 +typedef std::map<int, BCSampleFrame> BCSampleFrameMap;
 +
 +class BCSampleFrameContainer {
 +
 +      /*
 +      * The BCSampleFrameContainer stores a map of BCSampleFrame objects
 +      * with the timeline frame as key.
 +      *
 +      * Some details on the purpose:
-       * of sample frames at equidistant locations (sample period). 
-       * In any case we must resample first need to resample it fully 
++      * An Animation is made of multiple FCurves where each FCurve can
 +      * have multiple keyframes. When we want to export the animation we
 +      * also can decide whether we want to export the keyframes or a set
-       * then sample each frame that contains at least one keyFrame or 
++      * of sample frames at equidistant locations (sample period).
++      * In any case we must resample first need to resample it fully
 +      * to resolve things like:
 +      *
 +      * - animations by constraints
 +      * - animations by drivers
 +      *
 +      * For this purpose we need to step through the entire animation and
-       * The entire set of BCSampleframes is finally collected into 
++      * then sample each frame that contains at least one keyFrame or
 +      * sampleFrame. Then for each frame we have to store the transform
 +      * information for all exported objects in a BCSampleframe
 +      *
++      * The entire set of BCSampleframes is finally collected into
 +      * a BCSampleframneContainer
 +      */
 +
 +private:
 +      BCSampleFrameMap sample_frames;
 +
 +public:
 +
 +      ~BCSampleFrameContainer()
 +      {
 +      }
 +
 +      BCSample &add(Object *ob, int frame_index);
 +      BCSampleFrame * get_frame(int frame_index); // returns NULL if frame does not exist
 +
 +      const int get_frames(std::vector<int> &frames) const;
 +      const int get_frames(Object *ob, BCFrames &frames) const;
 +      const int get_frames(Object *ob, Bone *bone, BCFrames &frames) const;
 +
 +      const int get_samples(Object *ob, BCFrameSampleMap &samples) const;
 +      const int get_matrices(Object *ob, BCMatrixSampleMap &matrices) const;
 +      const int get_matrices(Object *ob, Bone *bone, BCMatrixSampleMap &bones) const;
 +};
 +
 +class BCAnimationSampler {
 +private:
 +      BlenderContext &blender_context;
 +      BCSampleFrameContainer sample_data;
 +      BCAnimationObjectMap objects;
 +
 +      void generate_transform(Object *ob, const BCCurveKey &key, BCAnimationCurveMap &curves);
 +      void generate_transforms(Object *ob, const std::string prep, const BC_animation_type type, BCAnimationCurveMap &curves);
 +      void generate_transforms(Object *ob, Bone *bone, BCAnimationCurveMap &curves);
 +
 +      void initialize_curves(BCAnimationCurveMap &curves, Object *ob);
 +      void initialize_keyframes(BCFrameSet &frameset, Object *ob);
 +      BCSample &sample_object(Object *ob, int frame_index, bool for_opensim);
 +      void update_animation_curves(BCAnimation &animation, BCSample &sample, Object *ob, int frame_index);
 +      void check_property_is_animated(BCAnimation &animation, float *ref, float *val, std::string data_path, int length);
 +
 +public:
 +      BCAnimationSampler(BlenderContext &blender_context, BCObjectSet &animated_subset);
 +      ~BCAnimationSampler();
 +
 +      void add_object(Object *ob);
 +
 +      void sample_scene(
 +              int sampling_rate,
 +              int keyframe_at_end,
 +              bool for_opensim,
 +              bool keep_keyframes,
 +              BC_export_animation_type export_animation_type);
 +
 +      BCAnimationCurveMap *get_curves(Object *ob);
 +      void get_object_frames(BCFrames &frames, Object *ob);
 +      bool get_object_samples(BCMatrixSampleMap &samples, Object *ob);
 +      void get_bone_frames(BCFrames &frames, Object *ob, Bone *bone);
 +      bool get_bone_samples(BCMatrixSampleMap &samples, Object *ob, Bone *bone);
 +
 +      static void get_animated_from_export_set(std::set<Object *> &animated_objects, LinkNode &export_set);
 +      static void find_depending_animated(std::set<Object *> &animated_objects, std::set<Object *> &candidates);
 +      static bool is_animated_by_constraint(Object *ob, ListBase *conlist, std::set<Object *> &animated_objects);
 +
 +};
 +
 +#endif
index 32210c9,0000000..95898bb
mode 100644,000000..100644
--- /dev/null
@@@ -1,188 -1,0 +1,186 @@@
 +/*
 +* ***** BEGIN GPL LICENSE BLOCK *****
 +*
 +* This program is free software; you can redistribute it and/or
 +* modify it under the terms of the GNU General Public License
 +* as published by the Free Software Foundation; either version 2
 +* of the License, or (at your option) any later version.
 +*
 +* This program is distributed in the hope that it will be useful,
 +* but WITHOUT ANY WARRANTY; without even the implied warranty of
 +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +* GNU General Public License for more details.
 +*
 +* You should have received a copy of the GNU General Public License
 +* along with this program; if not, write to the Free Software Foundation,
 +* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 +*
 +* The Original Code is Copyright (C) 2008 Blender Foundation.
 +* All rights reserved.
 +*
 +* Contributor(s): Blender Foundation
 +*
 +* ***** END GPL LICENSE BLOCK *****
 +*/
 +
 +#include "BCSampleData.h"
 +#include "collada_utils.h"
 +
 +BCSample::BCSample(Object *ob):
 +      obmat(ob)
 +{}
 +
 +BCSample::~BCSample()
 +{
 +      BCBoneMatrixMap::iterator it;
 +      for (it = bonemats.begin(); it != bonemats.end(); ++it) {
 +              delete it->second;
 +      }
 +}
 +
 +void BCSample::add_bone_matrix(Bone *bone, Matrix &mat)
 +{
 +      BCMatrix *matrix;
 +      BCBoneMatrixMap::const_iterator it = bonemats.find(bone);
 +      if (it != bonemats.end()) {
 +              throw std::invalid_argument("bone " + std::string(bone->name) + " already defined before");
 +      }
 +      matrix = new BCMatrix(mat);
 +      bonemats[bone] = matrix;
 +}
 +
 +BCMatrix::BCMatrix(Matrix &mat)
 +{
 +      set_transform(mat);
 +}
 +
 +BCMatrix::BCMatrix(Object *ob)
 +{
 +      set_transform(ob);
 +}
 +
 +void BCMatrix::set_transform(Matrix &mat)
 +{
 +      copy_m4_m4(matrix, mat);
 +      mat4_decompose(this->loc, this->q, this->size, mat);
 +      quat_to_eul(this->rot, this->q);
 +}
 +
 +void BCMatrix::set_transform(Object *ob)
 +{
 +      Matrix lmat;
 +
 +      BKE_object_matrix_local_get(ob, lmat);
 +      copy_m4_m4(matrix, lmat);
 +
 +      mat4_decompose(this->loc, this->q, this->size, lmat);
 +      quat_to_compatible_eul(this->rot, ob->rot, this->q);
 +}
 +
 +const BCMatrix *BCSample::get_matrix(Bone *bone) const
 +{
 +      BCBoneMatrixMap::const_iterator it = bonemats.find(bone);
 +      if (it == bonemats.end()) {
 +              return NULL;
 +      }
 +      return it->second;
 +}
 +
 +const BCMatrix &BCSample::get_matrix() const
 +{
 +      return obmat;
 +}
 +
 +
 +/* Get channel value */
 +const bool BCSample::get_value(std::string channel_target, const int array_index, float *val) const
 +{
 +      if (channel_target == "location") {
 +              *val = obmat.location()[array_index];
 +      }
 +      else if (channel_target == "scale") {
 +              *val = obmat.scale()[array_index];
 +      }
 +      else if (
 +              channel_target == "rotation" ||
 +              channel_target == "rotation_euler") {
 +              *val = obmat.rotation()[array_index];
 +      }
 +      else if (channel_target == "rotation_quat") {
 +              *val = obmat.quat()[array_index];
 +      }
 +      else {
 +              *val = 0;
 +              return false;
 +      }
 +
 +      return true;
 +}
 +
 +void BCMatrix::copy(Matrix &out, Matrix &in)
 +{
 +      /* destination comes first: */
 +      memcpy(out, in, sizeof(Matrix));
 +}
 +
 +void BCMatrix::transpose(Matrix &mat)
 +{
 +      transpose_m4(mat);
 +}
 +
 +void BCMatrix::sanitize(Matrix &mat, int precision)
 +{
 +      bc_sanitize_mat(mat, precision);
 +}
 +
 +void BCMatrix::unit()
 +{
 +      unit_m4(matrix);
 +}
 +
 +/*
 +We need double here because the OpenCollada API needs it.
 +precision = -1 indicates to not limit the precision
 +*/
 +void BCMatrix::get_matrix(double(&mat)[4][4], const bool transposed, const int precision) const
 +{
 +      for (int i = 0; i < 4; i++)
 +              for (int j = 0; j < 4; j++) {
 +                      float val = (transposed) ? matrix[j][i] : matrix[i][j];
 +                      if (precision >= 0)
 +                              val = floor((val * pow(10, precision) + 0.5)) / pow(10, precision);
 +                      mat[i][j] = val;
 +              }
 +}
 +
 +const bool BCMatrix::in_range(const BCMatrix &other, float distance) const
 +{
 +      for (int i = 0; i < 4; i++) {
 +              for (int j = 0; j < 4; j++) {
 +                      if (fabs(other.matrix[i][j] - matrix[i][j]) > distance) {
 +                              return false;
 +                      }
 +              }
 +      }
 +      return true;
 +}
 +
 +float(&BCMatrix::location() const)[3]
 +{
 +      return loc;
 +}
 +
 +float(&BCMatrix::rotation() const)[3]
 +{
 +      return rot;
 +}
 +
 +float(&BCMatrix::scale() const)[3]
 +{
 +      return size;
 +}
 +
 +float(&BCMatrix::quat() const)[4]
 +{
 +      return q;
 +}
@@@ -94,22 -84,12 +94,22 @@@ bool ErrorHandler::handleError(const CO
                 * Accept non critical errors as warnings (i.e. texture not found)
                 * This makes the importer more graceful, so it now imports what makes sense.
                 */
 -              isWarning = (saxFWLError->getSeverity() == COLLADASaxFWL::IError::SEVERITY_ERROR_NONCRITICAL);
 -              std::cout << "Sax FWL Error: " << saxFWLError->getErrorMessage() << std::endl;
 +
 +              isError = (saxFWLError->getSeverity() != COLLADASaxFWL::IError::SEVERITY_ERROR_NONCRITICAL);
-               
++
        }
        else {
 -              std::cout << "opencollada error: " << error->getFullErrorMessage() << std::endl;
 +              error_context = "OpenCollada";
 +              error_message = error->getFullErrorMessage();
 +              isError = true;
        }
  
 -      return isWarning;
 +      std::string severity = (isError) ? "Error" : "Warning";
 +      std::cout << error_context << " (" << severity << "): " << error_message << std::endl;
 +      if (isError) {
 +              std::cout << "The Collada import has been forced to stop." << std::endl;
 +              std::cout << "Please fix the reported error and then try again.";
 +              mError = true;
 +      }
 +      return isError;
  }
@@@ -84,32 -85,9 +84,32 @@@ void SceneExporter::exportHierarchy(
        }
  }
  
-          in a good way. 
 +void SceneExporter::writeNodeList(std::vector<Object *> &child_objects, Object *parent)
 +{
 +      /* TODO: Handle the case where a parent is not exported
 +         Actually i am not even sure if this can be done at all
++         in a good way.
 +         I really prefer to enforce the export of hidden
 +         elements in an object hierarchy. When the children of
 +         the hidden elements are exported as well.
 +       */
 +      for (int i = 0; i < child_objects.size(); ++i) {
 +              Object *child = child_objects[i];
 +              if (bc_is_marked(child)) {
 +                      bc_remove_mark(child);
 +                      writeNodes(child);
 +              }
 +      }
 +}
  
 -void SceneExporter::writeNodes(bContext *C, Object *ob, Scene *sce)
 +void SceneExporter::writeNodes(Object *ob)
  {
 +      ViewLayer *view_layer = blender_context.get_view_layer();
 +
 +      std::vector<Object *> child_objects;
 +      bc_get_children(child_objects, ob, view_layer);
 +      bool can_export = bc_is_in_Export_set(this->export_settings->export_set, ob, view_layer);
 +
        // Add associated armature first if available
        bool armature_exported = false;
        Object *ob_arm = bc_get_assigned_armature(ob);
                }
        }
  
 -      COLLADASW::Node colladaNode(mSW);
 -      colladaNode.setNodeId(translate_id(id_name(ob)));
 -      colladaNode.setNodeName(translate_id(id_name(ob)));
 -      colladaNode.setType(COLLADASW::Node::NODE);
 +      if (can_export) {
 +              COLLADASW::Node colladaNode(mSW);
 +              colladaNode.setNodeId(translate_id(id_name(ob)));
 +              colladaNode.setNodeName(encode_xml(id_name(ob)));
 +              colladaNode.setType(COLLADASW::Node::NODE);
  
 -      colladaNode.start();
 +              colladaNode.start();
  
 -      std::list<Object *> child_objects;
 -
 -      // list child objects
 -      LinkNode *node;
 -      for (node=this->export_settings->export_set; node; node=node->next) {
 -              // cob - child object
 -              Object *cob = (Object *)node->link;
 +              if (ob->type == OB_MESH && armature_exported) {
 +                      // for skinned mesh we write obmat in <bind_shape_matrix>
 +                      TransformWriter::add_node_transform_identity(colladaNode);
 +              }
 +              else {
 +                      TransformWriter::add_node_transform_ob(colladaNode, ob, this->export_settings->export_transformation_type);
 +              }
  
 -              if (cob->parent == ob) {
 -                      switch (cob->type) {
 -                              case OB_MESH:
 -                              case OB_CAMERA:
 -                              case OB_LAMP:
 -                              case OB_EMPTY:
 -                              case OB_ARMATURE:
 -                                      if (bc_is_marked(cob))
 -                                              child_objects.push_back(cob);
 -                                      break;
 +              // <instance_geometry>
 +              if (ob->type == OB_MESH) {
 +                      bool instance_controller_created = false;
 +                      if (armature_exported) {
 +                              instance_controller_created = arm_exporter->add_instance_controller(ob);
 +                      }
 +                      if (!instance_controller_created) {
 +                              COLLADASW::InstanceGeometry instGeom(mSW);
 +                              instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, this->export_settings->use_object_instantiation)));
 +                              instGeom.setName(encode_xml(id_name(ob)));
 +                              InstanceWriter::add_material_bindings(instGeom.getBindMaterial(),
 +                                      ob,
 +                                      this->export_settings->active_uv_only);
 +                              instGeom.add();
                        }
                }
 -      }
 -
 -      if (ob->type == OB_MESH && armature_exported)
 -              // for skinned mesh we write obmat in <bind_shape_matrix>
 -              TransformWriter::add_node_transform_identity(colladaNode);
 -      else {
 -              TransformWriter::add_node_transform_ob(colladaNode, ob, this->export_settings->export_transformation_type);
 -      }
  
 -      // <instance_geometry>
 -      if (ob->type == OB_MESH) {
 -              bool instance_controller_created = false;
 -              if (armature_exported) {
 -                      instance_controller_created = arm_exporter->add_instance_controller(ob);
 +              // <instance_controller>
 +              else if (ob->type == OB_ARMATURE) {
 +                      arm_exporter->add_armature_bones(ob, view_layer, this, child_objects);
                }
 -              if (!instance_controller_created) {
 -                      COLLADASW::InstanceGeometry instGeom(mSW);
 -                      instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob, this->export_settings->use_object_instantiation)));
 -                      instGeom.setName(translate_id(id_name(ob)));
 -                      InstanceWriter::add_material_bindings(instGeom.getBindMaterial(),
 -                                  ob,
 -                                      this->export_settings->active_uv_only,
 -                                      this->export_settings->export_texture_type);
 -
 -                      instGeom.add();
 -              }
 -      }
 -
 -      // <instance_controller>
 -      else if (ob->type == OB_ARMATURE) {
 -              arm_exporter->add_armature_bones(C, ob, sce, this, child_objects);
 -      }
 -
 -      // <instance_camera>
 -      else if (ob->type == OB_CAMERA) {
 -              COLLADASW::InstanceCamera instCam(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_camera_id(ob)));
 -              instCam.add();
 -      }
  
 -      // <instance_light>
 -      else if (ob->type == OB_LAMP) {
 -              COLLADASW::InstanceLight instLa(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_light_id(ob)));
 -              instLa.add();
 -      }
 -
 -      // empty object
 -      else if (ob->type == OB_EMPTY) { // TODO: handle groups (OB_DUPLIGROUP
 -              if ((ob->transflag & OB_DUPLIGROUP) == OB_DUPLIGROUP && ob->dup_group) {
 -                      GroupObject *go = NULL;
 -                      Group *gr = ob->dup_group;
 -                      /* printf("group detected '%s'\n", gr->id.name + 2); */
 -                      for (go = (GroupObject *)(gr->gobject.first); go; go = go->next) {
 -                              printf("\t%s\n", go->ob->id.name);
 -                      }
 +              // <instance_camera>
 +              else if (ob->type == OB_CAMERA) {
 +                      COLLADASW::InstanceCamera instCam(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_camera_id(ob)));
 +                      instCam.add();
                }
 -      }
  
 -      if (ob->type == OB_ARMATURE) {
 -              colladaNode.end();
 -      }
 +              // <instance_light>
 +              else if (ob->type == OB_LAMP) {
 +                      COLLADASW::InstanceLight instLa(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_light_id(ob)));
 +                      instLa.add();
 +              }
  
 -      if (BLI_listbase_is_empty(&ob->constraints) == false) {
 -              bConstraint *con = (bConstraint *) ob->constraints.first;
 -              while (con) {
 -                      std::string con_name(translate_id(con->name));
 -                      std::string con_tag = con_name + "_constraint";
 -                      printf("%s\n", con_name.c_str());
 -                      printf("%s\n\n", con_tag.c_str());
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"type",con->type);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"enforce",con->enforce);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"flag",con->flag);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"headtail",con->headtail);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"lin_error",con->lin_error);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"own_space",con->ownspace);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"rot_error",con->rot_error);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"tar_space",con->tarspace);
 -                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"lin_error",con->lin_error);
 -
 -                      //not ideal: add the target object name as another parameter.
 -                      //No real mapping in the .dae
 -                      //Need support for multiple target objects also.
 -                      const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
 -                      ListBase targets = {NULL, NULL};
 -                      if (cti && cti->get_constraint_targets) {
 -
 -                              bConstraintTarget *ct;
 -                              Object *obtar;
 -
 -                              cti->get_constraint_targets(con, &targets);
 -
 -                              for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
 -                                      obtar = ct->tar;
 -                                      std::string tar_id((obtar) ? id_name(obtar) : "");
 -                                      colladaNode.addExtraTechniqueChildParameter("blender",con_tag,"target_id",tar_id);
 +              // empty object
 +              else if (ob->type == OB_EMPTY) { // TODO: handle groups (OB_DUPLICOLLECTION
 +                      if ((ob->transflag & OB_DUPLICOLLECTION) == OB_DUPLICOLLECTION && ob->dup_group) {
 +                              Collection *collection = ob->dup_group;
 +                              /* printf("group detected '%s'\n", group->id.name + 2); */
 +                              FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(collection, object)
 +                              {
 +                                      printf("\t%s\n", object->id.name);
                                }
 -
 -                              if (cti->flush_constraint_targets)
 -                                      cti->flush_constraint_targets(con, &targets, 1);
 -
 +                              FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
                        }
  
 -                      con = con->next;
 -              }
 -      }
 -
 -      for (std::list<Object *>::iterator i = child_objects.begin(); i != child_objects.end(); ++i) {
 -              if (bc_is_marked(*i)) {
 -                      bc_remove_mark(*i);
 -                      writeNodes(C, *i, sce);
 +                      if (BLI_listbase_is_empty(&ob->constraints) == false) {
 +                              bConstraint *con = (bConstraint *)ob->constraints.first;
 +                              while (con) {
 +                                      std::string con_name(encode_xml(con->name));
 +                                      std::string con_tag = con_name + "_constraint";
 +                                      printf("%s\n", con_name.c_str());
 +                                      printf("%s\n\n", con_tag.c_str());
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "type", con->type);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "enforce", con->enforce);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "flag", con->flag);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "headtail", con->headtail);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "lin_error", con->lin_error);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "own_space", con->ownspace);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "rot_error", con->rot_error);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "tar_space", con->tarspace);
 +                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "lin_error", con->lin_error);
 +
-                                       //not ideal: add the target object name as another parameter. 
++                                      //not ideal: add the target object name as another parameter.
 +                                      //No real mapping in the .dae
 +                                      //Need support for multiple target objects also.
 +                                      const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
 +                                      ListBase targets = { NULL, NULL };
 +                                      if (cti && cti->get_constraint_targets) {
 +
 +                                              bConstraintTarget *ct;
 +                                              Object *obtar;
 +
 +                                              cti->get_constraint_targets(con, &targets);
 +
 +                                              for (ct = (bConstraintTarget *)targets.first; ct; ct = ct->next) {
 +                                                      obtar = ct->tar;
 +                                                      std::string tar_id((obtar) ? id_name(obtar) : "");
 +                                                      colladaNode.addExtraTechniqueChildParameter("blender", con_tag, "target_id", tar_id);
 +                                              }
 +
 +                                              if (cti->flush_constraint_targets)
 +                                                      cti->flush_constraint_targets(con, &targets, 1);
 +
 +                                      }
 +
 +                                      con = con->next;
 +                              }
 +                      }
                }
 -      }
 -
 -      if (ob->type != OB_ARMATURE)
 +              writeNodeList(child_objects, ob);
                colladaNode.end();
 +      }
  }
@@@ -256,17 -159,13 +256,17 @@@ Object *bc_add_object(Main *bmain, Scen
  }
  
  Mesh *bc_get_mesh_copy(
-     BlenderContext &blender_context, 
 -        Main *bmain, Scene *scene, Object *ob, BC_export_mesh_type export_mesh_type, bool apply_modifiers, bool triangulate)
++    BlenderContext &blender_context,
 +      Object *ob,
 +      BC_export_mesh_type export_mesh_type,
 +      bool apply_modifiers,
 +      bool triangulate)
  {
 -      Mesh *tmpmesh;
        CustomDataMask mask = CD_MASK_MESH;
        Mesh *mesh = (Mesh *)ob->data;
 -      DerivedMesh *dm = NULL;
 +      Mesh *tmpmesh = NULL;
        if (apply_modifiers) {
 +#if 0  /* Not supported by new system currently... */
                switch (export_mesh_type) {
                        case BC_MESH_TYPE_VIEW:
                        {
@@@ -345,25 -235,9 +345,25 @@@ bool bc_is_base_node(LinkNode *export_s
        return (root == ob);
  }
  
 -bool bc_is_in_Export_set(LinkNode *export_set, Object *ob)
 +bool bc_is_in_Export_set(LinkNode *export_set, Object *ob, ViewLayer *view_layer)
  {
 -      return (BLI_linklist_index(export_set, ob) != -1);
 +      bool to_export = (BLI_linklist_index(export_set, ob) != -1);
 +
 +      if (!to_export)
 +      {
-               /* Mark this object as to_export even if it is not in the 
++              /* Mark this object as to_export even if it is not in the
 +              export list, but it contains children to export */
 +
 +              std::vector<Object *> children;
 +              bc_get_children(children, ob, view_layer);
 +              for (int i = 0; i < children.size(); i++) {
 +                      if (bc_is_in_Export_set(export_set, children[i], view_layer)) {
 +                              to_export = true;
 +                              break;
 +                      }
 +              }
 +      }
 +      return to_export;
  }
  
  bool bc_has_object_type(LinkNode *export_set, short obtype)
@@@ -1221,193 -941,122 +1221,193 @@@ std::string bc_get_uvlayer_name(Mesh *m
        return "";
  }
  
 -/**********************************************************************
 -*
 -* Return the list of Mesh objects with assigned UVtextures and Images
 -* Note: We need to create artificaial materials for each of them
 -*
 -***********************************************************************/
 -std::set<Object *> bc_getUVTexturedObjects(Scene *sce, bool all_uv_layers)
 -{
 -      std::set <Object *> UVObjects;
 -      Base *base = (Base *)sce->base.first;
 -
 -      while (base) {
 -              Object *ob = base->object;
 -              bool has_uvimage = false;
 -              if (ob->type == OB_MESH) {
 -                      Mesh *me = (Mesh *)ob->data;
 -                      int active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);
 -
 -                      for (int i = 0; i < me->pdata.totlayer && !has_uvimage; i++) {
 -                              if (all_uv_layers || active_uv_layer == i)
 -                              {
 -                                      if (me->pdata.layers[i].type == CD_MTEXPOLY) {
 -                                              MTexPoly *txface = (MTexPoly *)me->pdata.layers[i].data;
 -                                              MPoly *mpoly = me->mpoly;
 -                                              for (int j = 0; j < me->totpoly; j++, mpoly++, txface++) {
 -
 -                                                      Image *ima = txface->tpage;
 -                                                      if (ima != NULL) {
 -                                                              has_uvimage = true;
 -                                                              break;
 -                                                      }
 -                                              }
 -                                      }
 -                              }
 -                      }
 +std::string bc_find_bonename_in_path(std::string path, std::string probe)
 +{
 +      std::string result;
 +      char *boneName = BLI_str_quoted_substrN(path.c_str(), probe.c_str());
 +      if (boneName) {
 +              result = std::string(boneName);
 +              MEM_freeN(boneName);
 +      }
 +      return result;
 +}
  
 -                      if (has_uvimage) {
 -                              UVObjects.insert(ob);
 -                      }
 -              }
 -              base = base->next;
 +static bNodeTree *prepare_material_nodetree(Material *ma)
 +{
 +      if (ma->nodetree == NULL) {
 +              ma->nodetree = ntreeAddTree(NULL, "Shader Nodetree", "ShaderNodeTree");
 +              ma->use_nodes = true;
        }
 -      return UVObjects;
 -}
 -
 -/**********************************************************************
 -*
 -* Return the list of UV Texture images from all exported Mesh Items
 -* Note: We need to create one artificial material for each Image.
 -*
 -***********************************************************************/
 -std::set<Image *> bc_getUVImages(Scene *sce, bool all_uv_layers)
 -{
 -      std::set <Image *> UVImages;
 -      Base *base = (Base *)sce->base.first;
 -
 -      while (base) {
 -              Object *ob = base->object;
 -              bool has_uvimage = false;
 -              if (ob->type == OB_MESH) {
 -                      Mesh *me = (Mesh *)ob->data;
 -                      int active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);
 -
 -                      for (int i = 0; i < me->pdata.totlayer && !has_uvimage; i++) {
 -                              if (all_uv_layers || active_uv_layer == i)
 -                              {
 -                                      if (me->pdata.layers[i].type == CD_MTEXPOLY) {
 -                                              MTexPoly *txface = (MTexPoly *)me->pdata.layers[i].data;
 -                                              MPoly *mpoly = me->mpoly;
 -                                              for (int j = 0; j < me->totpoly; j++, mpoly++, txface++) {
 -
 -                                                      Image *ima = txface->tpage;
 -                                                      if (ima != NULL) {
 -                                                              if (UVImages.find(ima) == UVImages.end())
 -                                                                      UVImages.insert(ima);
 -                                                      }
 -                                              }
 -                                      }
 -                              }
 -                      }
 +      return ma->nodetree;
 +}
 +
 +bNode *bc_add_node(bContext *C, bNodeTree *ntree, int node_type, int locx, int locy, std::string label)
 +{
 +      bNode *node = nodeAddStaticNode(C, ntree, node_type);
 +      if (node) {
 +              if (label.length() > 0) {
 +                      strcpy(node->label, label.c_str());
                }
 -              base = base->next;
 +              node->locx = locx;
 +              node->locy = locy;
 +              node->flag |= NODE_SELECT;
        }
 -      return UVImages;
 +      return node;
  }
  
 -/**********************************************************************
 -*
 -* Return the list of UV Texture images for the given Object
 -* Note: We need to create one artificial material for each Image.
 -*
 -***********************************************************************/
 -std::set<Image *> bc_getUVImages(Object *ob, bool all_uv_layers)
 +
 +bNode *bc_add_node(bContext *C, bNodeTree *ntree, int node_type, int locx, int locy)
  {
 -      std::set <Image *> UVImages;
 +      return bc_add_node(C, ntree, node_type, locx, locy, "");
 +}
  
 -      bool has_uvimage = false;
 -      if (ob->type == OB_MESH) {
 -              Mesh *me = (Mesh *)ob->data;
 -              int active_uv_layer = CustomData_get_active_layer_index(&me->pdata, CD_MTEXPOLY);
 +#if 0
 +// experimental, probably not used
 +static bNodeSocket *bc_group_add_input_socket(bNodeTree *ntree, bNode *to_node, int to_index, std::string label)
 +{
 +      bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index);
-       
 -              for (int i = 0; i < me->pdata.totlayer && !has_uvimage; i++) {
 -                      if (all_uv_layers || active_uv_layer == i)
 -                      {
 -                              if (me->pdata.layers[i].type == CD_MTEXPOLY) {
 -                                      MTexPoly *txface = (MTexPoly *)me->pdata.layers[i].data;
 -                                      MPoly *mpoly = me->mpoly;
 -                                      for (int j = 0; j < me->totpoly; j++, mpoly++, txface++) {
 -
 -                                              Image *ima = txface->tpage;
 -                                              if (ima != NULL) {
 -                                                      if (UVImages.find(ima) == UVImages.end())
 -                                                              UVImages.insert(ima);
 -                                              }
 -                                      }
 -                              }
 +      //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
 +      //return socket;
 +
 +      bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
 +      bNode *inputGroup = ntreeFindType(ntree, NODE_GROUP_INPUT);
 +      node_group_input_verify(ntree, inputGroup, (ID *)ntree);
 +      bNodeSocket *newsock = node_group_input_find_socket(inputGroup, gsock->identifier);
 +      nodeAddLink(ntree, inputGroup, newsock, to_node, to_socket);
 +      strcpy(newsock->name, label.c_str());
 +      return newsock;
 +}
 +
 +static bNodeSocket *bc_group_add_output_socket(bNodeTree *ntree, bNode *from_node, int from_index, std::string label)
 +{
 +      bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index);
 +
 +      //bNodeSocket *socket = ntreeAddSocketInterfaceFromSocket(ntree, to_node, to_socket);
 +      //return socket;
 +
 +      bNodeSocket *gsock = ntreeAddSocketInterfaceFromSocket(ntree, from_node, from_socket);
 +      bNode *outputGroup = ntreeFindType(ntree, NODE_GROUP_OUTPUT);
 +      node_group_output_verify(ntree, outputGroup, (ID *)ntree);
 +      bNodeSocket *newsock = node_group_output_find_socket(outputGroup, gsock->identifier);
 +      nodeAddLink(ntree, from_node, from_socket, outputGroup, newsock);
 +      strcpy(newsock->name, label.c_str());
 +      return newsock;
 +}
 +
 +
 +void bc_make_group(bContext *C, bNodeTree *ntree, std::map<std::string, bNode *> nmap)
 +{
 +      bNode * gnode = node_group_make_from_selected(C, ntree, "ShaderNodeGroup", "ShaderNodeTree");
 +      bNodeTree *gtree = (bNodeTree *)gnode->id;
 +
 +      bc_group_add_input_socket(gtree, nmap["main"], 0, "Diffuse");
 +      bc_group_add_input_socket(gtree, nmap["emission"], 0, "Emission");
 +      bc_group_add_input_socket(gtree, nmap["mix"], 0, "Transparency");
 +      bc_group_add_input_socket(gtree, nmap["emission"], 1, "Emission");
 +      bc_group_add_input_socket(gtree, nmap["main"], 4, "Metallic");
 +      bc_group_add_input_socket(gtree, nmap["main"], 5, "Specular");
 +
 +      bc_group_add_output_socket(gtree, nmap["mix"], 0, "Shader");
 +}
 +#endif
 +
 +static void bc_node_add_link(bNodeTree *ntree, bNode *from_node, int from_index, bNode *to_node, int to_index)
 +{
 +      bNodeSocket *from_socket = (bNodeSocket *)BLI_findlink(&from_node->outputs, from_index);
 +      bNodeSocket *to_socket = (bNodeSocket *)BLI_findlink(&to_node->inputs, to_index);
 +
 +      nodeAddLink(ntree, from_node, from_socket, to_node, to_socket);
 +}
 +
 +void bc_add_default_shader(bContext *C, Material *ma)
 +{
 +      bNodeTree *ntree = prepare_material_nodetree(ma);
 +      std::map<std::string, bNode *> nmap;
 +#if 0
 +      nmap["main"] = bc_add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED, -300, 300);
 +      nmap["emission"] = bc_add_node(C, ntree, SH_NODE_EMISSION, -300, 500, "emission");
 +      nmap["add"] = bc_add_node(C, ntree, SH_NODE_ADD_SHADER, 100, 400);
 +      nmap["transparent"] = bc_add_node(C, ntree, SH_NODE_BSDF_TRANSPARENT, 100, 200);
 +      nmap["mix"] = bc_add_node(C, ntree, SH_NODE_MIX_SHADER, 400, 300, "transparency");
 +      nmap["out"] = bc_add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 600, 300);
 +      nmap["out"]->flag &= ~NODE_SELECT;
 +
 +      bc_node_add_link(ntree, nmap["emission"], 0, nmap["add"], 0);
 +      bc_node_add_link(ntree, nmap["main"], 0, nmap["add"], 1);
 +      bc_node_add_link(ntree, nmap["add"], 0, nmap["mix"], 1);
 +      bc_node_add_link(ntree, nmap["transparent"], 0, nmap["mix"], 2);
 +
 +      bc_node_add_link(ntree, nmap["mix"], 0, nmap["out"], 0);
 +      // experimental, probably not used.
 +      bc_make_group(C, ntree, nmap);
 +#else
 +nmap["main"] = bc_add_node(C, ntree, SH_NODE_BSDF_PRINCIPLED,  0, 300);
 +nmap["out"] = bc_add_node(C, ntree, SH_NODE_OUTPUT_MATERIAL, 300, 300);
 +bc_node_add_link(ntree, nmap["main"], 0, nmap["out"], 0);
 +#endif
 +}
 +
 +COLLADASW::ColorOrTexture bc_get_base_color(Material *ma)
 +{
 +      bNode *master_shader = bc_get_master_shader(ma);
 +      if (master_shader) {
 +              return bc_get_base_color(master_shader);
 +      }
 +      else {
 +              return bc_get_cot(ma->r, ma->g, ma->b, ma->alpha);
 +      }
 +}
 +
 +COLLADASW::ColorOrTexture bc_get_specular_color(Material *ma, bool use_fallback)
 +{
 +      bNode *master_shader = bc_get_master_shader(ma);
 +      if (master_shader) {
 +              return bc_get_specular_color(master_shader);
 +      }
 +      else if (use_fallback) {
 +              return bc_get_cot(ma->specr * ma->spec, ma->specg * ma->spec, ma->specb * ma->spec, 1.0f);
 +      }
 +      else {
 +              return bc_get_cot(0.0, 0.0, 0.0, 1.0); // no specular
 +      }
 +}
 +
 +COLLADASW::ColorOrTexture bc_get_base_color(bNode *shader)
 +{
 +      bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, "Base Color");
 +      if (socket)
 +      {
 +              bNodeSocketValueRGBA *dcol = (bNodeSocketValueRGBA *)socket->default_value;
 +              float* col = dcol->value;
 +              return bc_get_cot(col[0], col[1], col[2], col[3]);
 +      }
 +      else {
 +              return bc_get_cot(0.8, 0.8, 0.8, 1.0); //default white
 +      }
 +}
 +
 +COLLADASW::ColorOrTexture bc_get_specular_color(bNode *shader)
 +{
 +      bNodeSocket *socket = nodeFindSocket(shader, SOCK_IN, "Specular");
 +      if (socket)
 +      {
 +              bNodeSocketValueRGBA *dcol = (bNodeSocketValueRGBA *)socket->default_value;
 +              float* col = dcol->value;
 +              return bc_get_cot(col[0], col[1], col[2], col[3]);
 +      }
 +      else {
 +              return bc_get_cot(0.8, 0.8, 0.8, 1.0); //default white
 +      }
 +}
 +
 +bNode *bc_get_master_shader(Material *ma)
 +{
 +      bNodeTree *nodetree = ma->nodetree;
 +      if (nodetree) {
 +              for (bNode *node = (bNode *)nodetree->nodes.first; node; node = node->next) {
 +                      if (node->typeinfo->type == SH_NODE_BSDF_PRINCIPLED) {
 +                              return node;
                        }
                }
        }
@@@ -160,32 -90,8 +160,32 @@@ extern void bc_bubble_sort_by_Object_na
  extern bool bc_is_root_bone(Bone *aBone, bool deform_bones_only);
  extern int  bc_get_active_UVLayer(Object *ob);
  
-       if (starting.size() > value.size()) 
 +std::string bc_find_bonename_in_path(std::string path, std::string probe);
 +
 +inline std::string bc_string_after(const std::string& s, const char c)
 +{
 +      size_t i = s.rfind(c, s.length());
 +      if (i != std::string::npos) {
 +              return(s.substr(i + 1, s.length() - i));
 +      }
 +      return(s);
 +}
 +
 +inline bool bc_startswith(std::string const & value, std::string const & starting)
 +{
- extern std::string bc_replace_string(std::string data, const std::string& pattern, const std::string& replacement); 
- extern std::string bc_url_encode(std::string data); 
++      if (starting.size() > value.size())
 +              return false;
 +      return (value.substr(0, starting.size()) == starting);
 +}
 +
 +inline bool bc_endswith(std::string const & value, std::string const & ending)
 +{
 +      if (ending.size() > value.size()) return false;
 +      return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
 +}
 +
+ extern std::string bc_replace_string(std::string data, const std::string& pattern, const std::string& replacement);
+ extern std::string bc_url_encode(std::string data);
  extern void bc_match_scale(Object *ob, UnitConverter &bc_unit, bool scale_to_scene);
  extern void bc_match_scale(std::vector<Object *> *objects_done, UnitConverter &unit_converter, bool scale_to_scene);
  
index b41b443,0000000..841cb73
mode 100644,000000..100644
--- /dev/null
@@@ -1,257 -1,0 +1,257 @@@
- }
 +/*
 + * ***** BEGIN GPL LICENSE BLOCK *****
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version 2
 + * of the License, or (at your option) any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software Foundation,
 + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 + *
 + * The Original Code is Copyright (C) 2017 by Blender Foundation.
 + * All rights reserved.
 + *
 + * ***** END GPL LICENSE BLOCK *****
 + */
 +
 +/** \file draw_cache_impl_metaball.c
 + *  \ingroup draw
 + *
 + * \brief MetaBall API for render engines
 + */
 +
 +#include "MEM_guardedalloc.h"
 +
 +#include "BLI_utildefines.h"
 +
 +#include "DNA_meta_types.h"
 +#include "DNA_object_types.h"
 +
 +#include "BKE_curve.h"
 +#include "BKE_mball.h"
 +
 +#include "GPU_batch.h"
 +
 +#include "DRW_render.h"
 +
 +#include "draw_cache_impl.h"  /* own include */
 +
 +
 +static void metaball_batch_cache_clear(MetaBall *mb);
 +
 +/* ---------------------------------------------------------------------- */
 +/* MetaBall GPUBatch Cache */
 +
 +typedef struct MetaBallBatchCache {
 +      GPUBatch *batch;
 +      GPUBatch **shaded_triangles;
 +      int mat_len;
 +
 +      /* Shared */
 +      GPUVertBuf *pos_nor_in_order;
 +
 +      /* Wireframe */
 +      struct {
 +              GPUVertBuf *elem_vbo;
 +              GPUTexture *elem_tx;
 +              GPUTexture *verts_tx;
 +              int tri_count;
 +      } face_wire;
 +
 +      /* settings to determine if cache is invalid */
 +      bool is_dirty;
 +} MetaBallBatchCache;
 +
 +/* GPUBatch cache management. */
 +
 +static bool metaball_batch_cache_valid(MetaBall *mb)
 +{
 +      MetaBallBatchCache *cache = mb->batch_cache;
 +
 +      if (cache == NULL) {
 +              return false;
 +      }
 +
 +      return cache->is_dirty == false;
 +}
 +
 +static void metaball_batch_cache_init(MetaBall *mb)
 +{
 +      MetaBallBatchCache *cache = mb->batch_cache;
 +
 +      if (!cache) {
 +              cache = mb->batch_cache = MEM_mallocN(sizeof(*cache), __func__);
 +      }
 +      cache->batch = NULL;
 +      cache->mat_len = 0;
 +      cache->shaded_triangles = NULL;
 +      cache->is_dirty = false;
 +      cache->pos_nor_in_order = NULL;
 +      cache->face_wire.elem_vbo = NULL;
 +      cache->face_wire.elem_tx = NULL;
 +      cache->face_wire.verts_tx = NULL;
 +      cache->face_wire.tri_count = 0;
 +}
 +
 +static MetaBallBatchCache *metaball_batch_cache_get(MetaBall *mb)
 +{
 +      if (!metaball_batch_cache_valid(mb)) {
 +              metaball_batch_cache_clear(mb);
 +              metaball_batch_cache_init(mb);
 +      }
 +      return mb->batch_cache;
 +}
 +
 +void DRW_mball_batch_cache_dirty_tag(MetaBall *mb, int mode)
 +{
 +      MetaBallBatchCache *cache = mb->batch_cache;
 +      if (cache == NULL) {
 +              return;
 +      }
 +      switch (mode) {
 +              case BKE_MBALL_BATCH_DIRTY_ALL:
 +                      cache->is_dirty = true;
 +                      break;
 +              default:
 +                      BLI_assert(0);
 +      }
 +}
 +
 +static void metaball_batch_cache_clear(MetaBall *mb)
 +{
 +      MetaBallBatchCache *cache = mb->batch_cache;
 +      if (!cache) {
 +              return;
 +      }
 +
 +      GPU_VERTBUF_DISCARD_SAFE(cache->face_wire.elem_vbo);
 +      DRW_TEXTURE_FREE_SAFE(cache->face_wire.elem_tx);
 +      DRW_TEXTURE_FREE_SAFE(cache->face_wire.verts_tx);
 +
 +      GPU_BATCH_DISCARD_SAFE(cache->batch);
 +      GPU_VERTBUF_DISCARD_SAFE(cache->pos_nor_in_order);
 +      /* Note: shaded_triangles[0] is already freed by cache->batch */
 +      MEM_SAFE_FREE(cache->shaded_triangles);
 +      cache->mat_len = 0;
 +}
 +
 +void DRW_mball_batch_cache_free(MetaBall *mb)
 +{
 +      metaball_batch_cache_clear(mb);
 +      MEM_SAFE_FREE(mb->batch_cache);
 +}
 +
 +static GPUVertBuf *mball_batch_cache_get_pos_and_normals(Object *ob, MetaBallBatchCache *cache)
 +{
 +      if (cache->pos_nor_in_order == NULL) {
 +              ListBase *lb = &ob->runtime.curve_cache->disp;
 +              cache->pos_nor_in_order = DRW_displist_vertbuf_calc_pos_with_normals(lb);
 +      }
 +      return cache->pos_nor_in_order;
 +}
 +
 +static GPUTexture *mball_batch_cache_get_edges_overlay_texture_buf(Object *ob, MetaBallBatchCache *cache)
 +{
 +      if (cache->face_wire.elem_tx != NULL) {
 +              return cache->face_wire.elem_tx;
 +      }
 +
 +      ListBase *lb = &ob->runtime.curve_cache->disp;
 +
 +      /* We need a special index buffer. */
 +      GPUVertBuf *vbo = cache->face_wire.elem_vbo = DRW_displist_create_edges_overlay_texture_buf(lb);
 +
 +      /* Upload data early because we need to create the texture for it. */
 +      GPU_vertbuf_use(vbo);
 +      cache->face_wire.elem_tx = GPU_texture_create_from_vertbuf(vbo);
 +      cache->face_wire.tri_count = vbo->vertex_alloc / 3;
 +
 +      return cache->face_wire.elem_tx;
 +}
 +
 +static GPUTexture *mball_batch_cache_get_vert_pos_and_nor_in_order_buf(Object *ob, MetaBallBatchCache *cache)
 +{
 +      if (cache->face_wire.verts_tx == NULL) {
 +              GPUVertBuf *vbo = mball_batch_cache_get_pos_and_normals(ob, cache);
 +              GPU_vertbuf_use(vbo); /* Upload early for buffer texture creation. */
 +              cache->face_wire.verts_tx = GPU_texture_create_buffer(GPU_R32F, vbo->vbo_id);
 +      }
 +
 +      return cache->face_wire.verts_tx;
 +}
 +
 +/* -------------------------------------------------------------------- */
 +
 +/** \name Public Object/MetaBall API
 + * \{ */
 +
 +GPUBatch *DRW_metaball_batch_cache_get_triangles_with_normals(Object *ob)
 +{
 +      if (!BKE_mball_is_basis(ob)) {
 +              return NULL;
 +      }
 +
 +      MetaBall *mb = ob->data;
 +      MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
 +
 +      if (cache->batch == NULL) {
 +              ListBase *lb = &ob->runtime.curve_cache->disp;
 +              cache->batch = GPU_batch_create_ex(
 +                      GPU_PRIM_TRIS,
 +                      mball_batch_cache_get_pos_and_normals(ob, cache),
 +                      DRW_displist_indexbuf_calc_triangles_in_order(lb),
 +                      GPU_BATCH_OWNS_INDEX);
 +      }
 +
 +      return cache->batch;
 +}
 +
 +GPUBatch **DRW_metaball_batch_cache_get_surface_shaded(Object *ob, MetaBall *mb, struct GPUMaterial **UNUSED(gpumat_array), uint gpumat_array_len)
 +{
 +      if (!BKE_mball_is_basis(ob)) {
 +              return NULL;
 +      }
 +
 +      MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
 +      if (cache->shaded_triangles == NULL) {
 +              cache->mat_len = gpumat_array_len;
 +              cache->shaded_triangles = MEM_callocN(sizeof(*cache->shaded_triangles) * cache->mat_len, __func__);
 +              cache->shaded_triangles[0] = DRW_metaball_batch_cache_get_triangles_with_normals(ob);
 +              for (int i = 1; i < cache->mat_len; ++i) {
 +                      cache->shaded_triangles[i] = NULL;
 +              }
 +      }
 +      return cache->shaded_triangles;
 +
 +}
 +
 +void DRW_metaball_batch_cache_get_wireframes_face_texbuf(
 +        Object *ob, struct GPUTexture **verts_data, struct GPUTexture **face_indices, int *tri_count)
 +{
 +      if (!BKE_mball_is_basis(ob)) {
 +              *verts_data = NULL;
 +              *face_indices = NULL;
 +              *tri_count = 0;
 +              return;
 +      }
 +
 +      MetaBall *mb = ob->data;
 +      MetaBallBatchCache *cache = metaball_batch_cache_get(mb);
 +
 +      if (cache->face_wire.verts_tx == NULL) {
 +              *verts_data = mball_batch_cache_get_vert_pos_and_nor_in_order_buf(ob, cache);
 +              *face_indices = mball_batch_cache_get_edges_overlay_texture_buf(ob, cache);
 +      }
 +      else {
 +              *verts_data = cache->face_wire.verts_tx;
 +              *face_indices = cache->face_wire.elem_tx;
 +      }
 +      *tri_count = cache->face_wire.tri_count;
++}
@@@ -256,134 -231,96 +256,134 @@@ static void uiCollada_exportSettings(ui
  {
        uiLayout *box, *row, *col, *split;
        bool include_animations = RNA_boolean_get(imfptr, "include_animations");
-       
 +      int ui_section = RNA_enum_get(imfptr, "prop_bc_export_ui_section");
++
 +      BC_export_animation_type animation_type = RNA_enum_get(imfptr, "export_animation_type_selection");
 +      BC_export_transformation_type transformation_type = RNA_enum_get(imfptr, "export_transformation_type_selection");
 +
 +      bool sampling = animation_type == BC_ANIMATION_EXPORT_SAMPLES;
  
        /* Export Options: */
        box = uiLayoutBox(layout);
-       
 -      row = uiLayoutRow(box, false);
 -      uiItemL(row, IFACE_("Export Data Options:"), ICON_MESH_DATA);
        row = uiLayoutRow(box, false);
 -      split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT);
 -      col   = uiLayoutColumn(split, false);
 -      uiItemR(col, imfptr, "apply_modifiers", 0, NULL, ICON_NONE);
 -      col   = uiLayoutColumn(split, false);
 -      uiItemR(col, imfptr, "export_mesh_type_selection", 0, "", ICON_NONE);
 -      uiLayoutSetEnabled(col, RNA_boolean_get(imfptr, "apply_modifiers"));
 +      uiItemR(row, imfptr, "prop_bc_export_ui_section", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE);
 +      if (ui_section == BC_UI_SECTION_MAIN) {
 +              /* =================== */
 +              /* Export Data options */
 +              /* =================== */
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "include_children", 0, NULL, ICON_NONE);
 -      uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "selected", 0, NULL, ICON_NONE);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "include_armatures", 0, NULL, ICON_NONE);
 -      uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "include_children", 0, NULL, ICON_NONE);
 +              uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "include_shapekeys", 0, NULL, ICON_NONE);
 -      uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "include_armatures", 0, NULL, ICON_NONE);
 +              uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "include_animations", 0, NULL, ICON_NONE);
 -      row = uiLayoutRow(box, false);
 -      if (include_animations) {
 -              uiItemR(row, imfptr, "sample_animations", 0, NULL, ICON_NONE);
 -              row = uiLayoutColumn(box, false);
 -              uiItemR(row, imfptr, "sampling_rate", 0, NULL, ICON_NONE);
 -              uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "sample_animations"));
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "include_shapekeys", 0, NULL, ICON_NONE);
 +              uiLayoutSetEnabled(row, RNA_boolean_get(imfptr, "selected"));
 +
 +              /* Texture options */
 +              box = uiLayoutBox(layout);
 +              row = uiLayoutRow(box, false);
 +              uiItemL(row, IFACE_("Texture Options:"), ICON_TEXTURE_DATA);
 +
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "active_uv_only", 0, NULL, ICON_NONE);
 +
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "use_texture_copies", 1, NULL, ICON_NONE);
 +
 +      }
 +      else if (ui_section == BC_UI_SECTION_GEOMETRY) {
 +              row = uiLayoutRow(box, false);
 +              uiItemL(row, IFACE_("Export Data Options:"), ICON_MESH_DATA);
 +
 +              row = uiLayoutRow(box, false);
 +              split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT);
 +              col = uiLayoutColumn(split, false);
 +              uiItemR(col, imfptr, "apply_modifiers", 0, NULL, ICON_NONE);
 +              col = uiLayoutColumn(split, false);
 +              uiItemR(col, imfptr, "export_mesh_type_selection", 0, "", ICON_NONE);
 +              uiLayoutSetEnabled(col, RNA_boolean_get(imfptr, "apply_modifiers"));
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "triangulate", 1, NULL, ICON_NONE);
        }
 +      else if (ui_section == BC_UI_SECTION_ARMATURE) {
 +              /* Armature options */
 +              box = uiLayoutBox(layout);
 +              row = uiLayoutRow(box, false);
 +              uiItemL(row, IFACE_("Armature Options:"), ICON_ARMATURE_DATA);
  
 -      /* Texture options */
 -      box = uiLayoutBox(layout);
 -      row = uiLayoutRow(box, false);
 -      uiItemL(row, IFACE_("Texture Options:"), ICON_TEXTURE_DATA);
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "deform_bones_only", 0, NULL, ICON_NONE);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "active_uv_only", 0, NULL, ICON_NONE);
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "open_sim", 0, NULL, ICON_NONE);
 +      }
 +      else if (ui_section == BC_UI_SECTION_ANIMATION) {
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "export_texture_type_selection", 0, "", ICON_NONE);
 +              /* ====================== */
 +              /* Animation Data options */
 +              /* ====================== */
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "use_texture_copies", 1, NULL, ICON_NONE);
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "include_animations", 0, NULL, ICON_NONE);
  
  
 -      /* Armature options */
 -      box = uiLayoutBox(layout);
 -      row = uiLayoutRow(box, false);
 -      uiItemL(row, IFACE_("Armature Options:"), ICON_ARMATURE_DATA);
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "export_animation_type_selection", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "deform_bones_only", 0, NULL, ICON_NONE);
 +              row = uiLayoutRow(box, false);
 +              split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT);
 +              uiItemL(split, IFACE_("Transformation Type"), ICON_NONE);
 +              uiItemR(split, imfptr, "export_transformation_type_selection", 0, "", ICON_NONE);
 +              uiLayoutSetEnabled(row, animation_type == BC_ANIMATION_EXPORT_SAMPLES);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "open_sim", 0, NULL, ICON_NONE);
 +              row = uiLayoutColumn(box, false);
 +              uiItemR(row, imfptr, "keep_smooth_curves", 0, NULL, ICON_NONE);
-               uiLayoutSetEnabled(row, include_animations && 
++              uiLayoutSetEnabled(row, include_animations &&
 +                      (transformation_type == BC_TRANSFORMATION_TYPE_TRANSROTLOC || animation_type == BC_ANIMATION_EXPORT_KEYS));
  
 -      /* Collada options: */
 -      box = uiLayoutBox(layout);
 -      row = uiLayoutRow(box, false);
 -      uiItemL(row, IFACE_("Collada Options:"), ICON_MODIFIER);
 +              row = uiLayoutColumn(box, false);
 +              uiItemR(row, imfptr, "sampling_rate", 0, NULL, ICON_NONE);
 +              uiLayoutSetEnabled(row, sampling && include_animations);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "triangulate", 1, NULL, ICON_NONE);
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "use_object_instantiation", 1, NULL, ICON_NONE);
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "use_blender_profile", 1, NULL, ICON_NONE);
 +              row = uiLayoutColumn(box, false);
 +              uiItemR(row, imfptr, "keep_keyframes", 0, NULL, ICON_NONE);
 +              uiLayoutSetEnabled(row, sampling && include_animations);
  
 -      row = uiLayoutRow(box, false);
 -      split = uiLayoutSplit(row, 0.6f, UI_LAYOUT_ALIGN_RIGHT);
 -      uiItemL(split, IFACE_("Transformation Type"), ICON_NONE);
 -      uiItemR(split, imfptr, "export_transformation_type_selection", 0, "", ICON_NONE);
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "sort_by_name", 0, NULL, ICON_NONE);
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "include_all_actions", 0, NULL, ICON_NONE);
 +              uiLayoutSetEnabled(row, include_animations);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "keep_bind_info", 0, NULL, ICON_NONE);
 +      }
 +      else if (ui_section == BC_UI_SECTION_COLLADA) {
 +              /* Collada options: */
 +              box = uiLayoutBox(layout);
 +              row = uiLayoutRow(box, false);
 +              uiItemL(row, IFACE_("Collada Options:"), ICON_MODIFIER);
  
 -      row = uiLayoutRow(box, false);
 -      uiItemR(row, imfptr, "limit_precision", 0, NULL, ICON_NONE);
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "use_object_instantiation", 1, NULL, ICON_NONE);
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "use_blender_profile", 1, NULL, ICON_NONE);
 +
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "sort_by_name", 0, NULL, ICON_NONE);
  
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "keep_bind_info", 0, NULL, ICON_NONE);
 +
 +              row = uiLayoutRow(box, false);
 +              uiItemR(row, imfptr, "limit_precision", 0, NULL, ICON_NONE);
 +      }
  }
  
  static void wm_collada_export_draw(bContext *UNUSED(C), wmOperator *op)