Merge of the COLLADA GSoC branch into trunk.
authorArystanbek Dyussenov <arystan.d@gmail.com>
Fri, 30 Oct 2009 15:35:50 +0000 (15:35 +0000)
committerArystanbek Dyussenov <arystan.d@gmail.com>
Fri, 30 Oct 2009 15:35:50 +0000 (15:35 +0000)
COLLADA code is disabled by default (it has dependencies requiring manual install).

SCons and CMake builds are supported on Windows and Linux, no Mac building yet. More on building COLLADA code: http://wiki.blender.org/index.php/User:Kazanbas/Building_Collada_Branch.

The detailed command log of the merge (can be useful for educational purposes):

branch=https://svn.blender.org/svnroot/bf-blender/branches/soc-2009-chingachgook
# collada code
svn copy $branch/source/blender/collada source/blender/collada
# operator
svn merge -c 20401,20955,21077,24077,24079 $branch/source/blender/windowmanager/intern/wm_operators.c source/blender/windowmanager/intern/wm_operators.c
# menu
svn merge -c 24079 $branch/release/scripts/ui/space_info.py release/scripts/ui/space_info.py
# scons
svn merge -c 20398 $branch/source/blender/SConscript source/blender/SConscript
svn merge -c 20398,20691,20955,22726 $branch/tools/btools.py tools/btools.py
svn merge -c 20691,20955,22726 $branch/tools/Blender.py tools/Blender.py
svn merge -c 20398,20692,20955 $branch/config/linux2-config.py config/linux2-config.py
svn merge -c 22726 $branch/config/win64-vc-config.py config/win64-vc-config.py
svn merge -c 22726 $branch/config/win32-vc-config.py config/win32-vc-config.py
svn merge -c 24077 $branch/source/blender/windowmanager/SConscript source/blender/windowmanager/SConscript
# cmake
svn merge -c 23319,23905,24077,24158 $branch/CMakeLists.txt CMakeLists.txt
svn merge -c 23319 $branch/source/blender/CMakeLists.txt source/blender/CMakeLists.txt
svn merge -c 23319 $branch/source/creator/CMakeLists.txt source/creator/CMakeLists.txt
svn merge -c 23319 $branch/CMake/macros.cmake CMake/macros.cmake
svn merge -c 24077 $branch/source/blender/windowmanager/CMakeLists.txt source/blender/windowmanager/CMakeLists.txt

23 files changed:
CMake/macros.cmake
CMakeLists.txt
config/linux2-config.py
config/win32-vc-config.py
config/win64-vc-config.py
release/scripts/ui/space_info.py
source/blender/CMakeLists.txt
source/blender/SConscript
source/blender/collada/CMakeLists.txt [new file with mode: 0644]
source/blender/collada/DocumentExporter.cpp [new file with mode: 0644]
source/blender/collada/DocumentExporter.h [new file with mode: 0644]
source/blender/collada/DocumentImporter.cpp [new file with mode: 0644]
source/blender/collada/DocumentImporter.h [new file with mode: 0644]
source/blender/collada/SConscript [new file with mode: 0644]
source/blender/collada/collada.cpp [new file with mode: 0644]
source/blender/collada/collada.h [new file with mode: 0644]
source/blender/collada/collada_internal.h [new file with mode: 0644]
source/blender/windowmanager/CMakeLists.txt
source/blender/windowmanager/SConscript
source/blender/windowmanager/intern/wm_operators.c
source/creator/CMakeLists.txt
tools/Blender.py
tools/btools.py

index 150bd55bfd7e06a2f10ef5d3cc8d62c526d4739b..258cbdf57b688a6ea0563084196ef4e76567688f 100644 (file)
@@ -76,6 +76,11 @@ MACRO(SETUP_LIBDIRS)
   IF(WITH_FFTW3)
     LINK_DIRECTORIES(${FFTW3_LIBPATH})
   ENDIF(WITH_FFTW3)
+  IF(WITH_OPENCOLLADA)
+    LINK_DIRECTORIES(${OPENCOLLADA_LIBPATH})
+    LINK_DIRECTORIES(${PCRE_LIBPATH})
+    LINK_DIRECTORIES(${EXPAT_LIBPATH})
+  ENDIF(WITH_OPENCOLLADA)
 
   IF(WIN32)
     LINK_DIRECTORIES(${PTHREADS_LIBPATH})
@@ -135,6 +140,11 @@ MACRO(SETUP_LIBLINKS
   IF(WITH_FFMPEG)
     TARGET_LINK_LIBRARIES(${target} ${FFMPEG_LIB})
   ENDIF(WITH_FFMPEG)
+  IF(WITH_OPENCOLLADA)
+    TARGET_LINK_LIBRARIES(${target} ${OPENCOLLADA_LIB})
+    TARGET_LINK_LIBRARIES(${target} ${PCRE_LIB})
+    TARGET_LINK_LIBRARIES(${target} ${EXPAT_LIB})
+  ENDIF(WITH_OPENCOLLADA)
   IF(WIN32)
     TARGET_LINK_LIBRARIES(${target} ${PTHREADS_LIB})
   ENDIF(WIN32)
index 963601dd94ac2a2722dcaf0807ee84eaf81d108c..4126bc860daaa4f9779e2ef194459efd5fa5734e 100644 (file)
@@ -80,6 +80,7 @@ OPTION(WITH_LZMA          "Enable best LZMA compression, used for pointcache" ON
 OPTION(WITH_CXX_GUARDEDALLOC "Enable GuardedAlloc for C++ memory allocation" OFF)
 OPTION(WITH_BUILDINFO     "Include extra build details" ON)
 OPTION(WITH_INSTALL       "Install accompanying scripts and language files needed to run blender" ON)
+OPTION(WITH_OPENCOLLADA                "Enable OpenCollada Support (http://www.opencollada.org/)"      OFF)
 
 IF (APPLE)
 OPTION(WITH_COCOA        "Use Cocoa framework instead of deprecated Carbon" ON)
@@ -90,6 +91,19 @@ IF(NOT WITH_GAMEENGINE AND WITH_PLAYER)
        MESSAGE("WARNING: WITH_PLAYER needs WITH_GAMEENGINE")
 ENDIF(NOT WITH_GAMEENGINE AND WITH_PLAYER)
 
+IF (WITH_OPENCOLLADA)
+SET(OPENCOLLADA /usr/local/opencollada CACHE FILEPATH "OpenCollada Directory")
+SET(OPENCOLLADA_LIBPATH ${OPENCOLLADA})
+SET(OPENCOLLADA_LIB OpenCollada)
+SET(PCRE /usr CACHE FILEPATH "PCRE Directory")
+SET(PCRE_LIBPATH ${PCRE}/lib)
+SET(PCRE_LIB pcre)
+SET(EXPAT /usr CACHE FILEPATH "Expat Directory")
+SET(EXPAT_LIBPATH ${EXPAT}/lib)
+SET(EXPAT_LIB expat)
+
+ENDIF (WITH_OPENCOLLADA)
+
 # For alternate Python locations the commandline can be used to override detected/default cache settings, e.g:
 # On Unix: 
 #   cmake -D PYTHON_LIB=/usr/local/lib/python2.3/config/libpython2.3.so -D PYTHON_INC=/usr/local/include/python2.3 -D PYTHON_BINARY=/usr/local/bin/python2.3 -G "Unix Makefiles" ../blender
index 026d0a200a5eb32ea8fab6daa0dd75a05af46b1f..dffc861fca8632069cfe9bb254b57c9ec506cf93 100644 (file)
@@ -151,6 +151,20 @@ BF_OPENGL_LIB = 'GL GLU X11 Xi'
 BF_OPENGL_LIBPATH = '/usr/X11R6/lib'
 BF_OPENGL_LIB_STATIC = '${BF_OPENGL_LIBPATH}/libGL.a ${BF_OPENGL_LIBPATH}/libGLU.a ${BF_OPENGL_LIBPATH}/libXxf86vm.a ${BF_OPENGL_LIBPATH}/libX11.a ${BF_OPENGL_LIBPATH}/libXi.a ${BF_OPENGL_LIBPATH}/libXext.a ${BF_OPENGL_LIBPATH}/libXxf86vm.a'
 
+WITH_BF_COLLADA = False
+BF_COLLADA = '#source/blender/collada'
+BF_COLLADA_INC = '${BF_COLLADA}'
+BF_COLLADA_LIB = 'bf_collada'
+BF_OPENCOLLADA = ''
+BF_OPENCOLLADA_LIB = 'OpenCollada'
+BF_OPENCOLLADA_LIBPATH = '/usr/lib'
+BF_PCRE = ''
+BF_PCRE_LIB = 'pcre'
+BF_PCRE_LIBPATH = '/usr/lib'
+BF_EXPAT = '/usr'
+BF_EXPAT_LIB = 'expat'
+BF_EXPAT_LIBPATH = '/usr/lib'
+
 ##
 CC = 'gcc'
 CXX = 'g++'
index 291aa023ec86410037f8e20043b55a909f5e93dd..2c5781df75a706c0bd63f85e8eacb278f1708bbb 100644 (file)
@@ -138,6 +138,16 @@ BF_FFTW3_LIBPATH = '${BF_FFTW3}/lib'
 WITH_BF_REDCODE = False  
 BF_REDCODE_INC = '#extern'
 
+WITH_BF_COLLADA = False
+BF_COLLADA = '#source/blender/collada'
+BF_COLLADA_INC = '${BF_COLLADA}'
+BF_COLLADA_LIB = 'bf_collada'
+
+BF_OPENCOLLADA = LIBDIR + '/opencollada'
+BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include'
+BF_OPENCOLLADA_LIB = 'opencollada'
+BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib'
+
 WITH_BF_STATICOPENGL = False
 BF_OPENGL_INC = '${BF_OPENGL}/include'
 BF_OPENGL_LIBINC = '${BF_OPENGL}/lib'
index 5f088489b34bb89e64500b4be1d06038731f3d96..429f8b0b6d6aae4dd5ebdbe7fcfe767436c6e0e0 100644 (file)
@@ -151,6 +151,16 @@ BF_FFTW3_LIBPATH = '${BF_FFTW3}/lib'
 WITH_BF_REDCODE = False  
 BF_REDCODE_INC = '#extern'
 
+WITH_BF_COLLADA = False
+BF_COLLADA = '#source/blender/collada'
+BF_COLLADA_INC = '${BF_COLLADA}'
+BF_COLLADA_LIB = 'bf_collada'
+
+BF_OPENCOLLADA = LIBDIR + '/opencollada'
+BF_OPENCOLLADA_INC = '${BF_OPENCOLLADA}/include'
+BF_OPENCOLLADA_LIB = 'opencollada'
+BF_OPENCOLLADA_LIBPATH = '${BF_OPENCOLLADA}/lib'
+
 WITH_BF_STATICOPENGL = False
 BF_OPENGL_INC = '${BF_OPENGL}/include'
 BF_OPENGL_LIBINC = '${BF_OPENGL}/lib'
index 083b6f07fbc6bd97bae67d57b1a270e65d3c7ba7..d4fcbbcdd4d498b5a9c0806f1468243733f0e920 100644 (file)
@@ -99,13 +99,13 @@ class INFO_MT_file_import(dynamic_menu.DynMenu):
        __label__ = "Import"
 
        def draw(self, context):
-               pass # dynamic menu
+               self.layout.itemO("WM_OT_collada_import", text="COLLADA (.dae)...")
 
 class INFO_MT_file_export(dynamic_menu.DynMenu):
        __label__ = "Export"
 
        def draw(self, context):
-               pass # dynamic menu
+               self.layout.itemO("WM_OT_collada_export", text="COLLADA (.dae)...")
 
 class INFO_MT_file_external_data(bpy.types.Menu):
        __label__ = "External Data"
index 99297714fd2eb60ba4df80b3e4fdce12987270cd..045ee15246f90be03b0efe91c56130f20b138cee 100644 (file)
@@ -58,3 +58,7 @@ IF(WITH_PYTHON)
        ADD_SUBDIRECTORY(python)
 ENDIF(WITH_PYTHON)
 
+IF(WITH_OPENCOLLADA)
+  ADD_SUBDIRECTORY(collada)
+ENDIF(WITH_OPENCOLLADA)
+
index 3625678f6102603782d91d632e37fc2fc18d190f..9910db1902f0eb3934d4d58d0dc5325ef5a56b04 100644 (file)
@@ -33,3 +33,6 @@ if env['WITH_BF_OPENEXR']:
 
 if env['WITH_BF_QUICKTIME']:
     SConscript (['quicktime/SConscript'])
+
+if env['WITH_BF_COLLADA']:
+    SConscript (['collada/SConscript'])
diff --git a/source/blender/collada/CMakeLists.txt b/source/blender/collada/CMakeLists.txt
new file mode 100644 (file)
index 0000000..5a8c08a
--- /dev/null
@@ -0,0 +1,44 @@
+# $Id: CMakeLists.txt 21789 2009-07-22 05:35:12Z kazanbas $
+# ***** 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+#
+# The Original Code is Copyright (C) 2006, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): Jacques Beaurain.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+FILE(GLOB SRC *.cpp)
+
+SET(INC
+  .
+  ../blenlib
+  ../blenkernel
+  ../windowmanager
+  ../makesdna
+  ../makesrna
+  ../editors/include
+  ../../../intern/guardedalloc
+  ${OPENCOLLADA}/COLLADAStreamWriter/include
+  ${OPENCOLLADA}/COLLADABaseUtils/include
+  ${OPENCOLLADA}/COLLADAFramework/include
+  ${OPENCOLLADA}/COLLADASaxFrameworkLoader/include 
+)
+
+BLENDERLIB(bf_collada "${SRC}" "${INC}")
diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp
new file mode 100644 (file)
index 0000000..09db4ba
--- /dev/null
@@ -0,0 +1,2137 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_image_types.h"
+#include "DNA_material_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_anim_types.h"
+#include "DNA_action_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_modifier_types.h"
+
+extern "C" 
+{
+#include "BKE_DerivedMesh.h"
+#include "BKE_fcurve.h"
+#include "BLI_util.h"
+#include "BLI_fileops.h"
+#include "ED_keyframing.h"
+}
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_scene.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_action.h" // pose functions
+#include "BKE_armature.h"
+#include "BKE_image.h"
+#include "BKE_utildefines.h"
+
+#include "BLI_arithb.h"
+#include "BLI_string.h"
+#include "BLI_listbase.h"
+
+#include "COLLADASWAsset.h"
+#include "COLLADASWLibraryVisualScenes.h"
+#include "COLLADASWNode.h"
+#include "COLLADASWLibraryGeometries.h"
+#include "COLLADASWSource.h"
+#include "COLLADASWInstanceGeometry.h"
+#include "COLLADASWInputList.h"
+#include "COLLADASWPrimitves.h"
+#include "COLLADASWVertices.h"
+#include "COLLADASWLibraryAnimations.h"
+#include "COLLADASWLibraryImages.h"
+#include "COLLADASWLibraryEffects.h"
+#include "COLLADASWImage.h"
+#include "COLLADASWEffectProfile.h"
+#include "COLLADASWColorOrTexture.h"
+#include "COLLADASWParamTemplate.h"
+#include "COLLADASWParamBase.h"
+#include "COLLADASWSurfaceInitOption.h"
+#include "COLLADASWSampler.h"
+#include "COLLADASWScene.h"
+//#include "COLLADASWSurface.h"
+#include "COLLADASWTechnique.h"
+#include "COLLADASWTexture.h"
+#include "COLLADASWLibraryMaterials.h"
+#include "COLLADASWBindMaterial.h"
+#include "COLLADASWLibraryCameras.h"
+#include "COLLADASWLibraryLights.h"
+#include "COLLADASWInstanceCamera.h"
+#include "COLLADASWInstanceLight.h"
+#include "COLLADASWCameraOptic.h"
+#include "COLLADASWConstants.h"
+#include "COLLADASWLibraryControllers.h"
+#include "COLLADASWInstanceController.h"
+#include "COLLADASWBaseInputElement.h"
+
+#include "collada_internal.h"
+#include "DocumentExporter.h"
+
+#include <vector>
+#include <algorithm> // std::find
+
+// arithb.c now has QuatToAxisAngle too
+#if 0
+// This function assumes that quat is normalized.
+// The following document was used as reference:
+// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+void QuatToAxisAngle(float *q, float *axis, float *angle)
+{
+       // quat to axis angle
+       *angle = 2 * acos(q[0]);
+       float divisor = sqrt(1 - q[0] * q[0]);
+
+       // test to avoid divide by zero, divisor is always positive
+       if (divisor < 0.001f ) {
+               axis[0] = 1.0f;
+               axis[1] = 0.0f;
+               axis[2] = 0.0f;
+       }
+       else {
+               axis[0] = q[1] / divisor;
+               axis[1] = q[2] / divisor;
+               axis[2] = q[3] / divisor;
+       }
+}
+#endif
+
+char *CustomData_get_layer_name(const struct CustomData *data, int type, int n)
+{
+       int layer_index = CustomData_get_layer_index(data, type);
+       if(layer_index < 0) return NULL;
+
+       return data->layers[layer_index+n].name;
+}
+
+char *CustomData_get_active_layer_name(const CustomData *data, int type)
+{
+       /* get the layer index of the active layer of type */
+       int layer_index = CustomData_get_active_layer_index(data, type);
+       if(layer_index < 0) return NULL;
+
+       return data->layers[layer_index].name;
+}
+
+static std::string id_name(void *id)
+{
+       return ((ID*)id)->name + 2;
+}
+
+static std::string get_geometry_id(Object *ob)
+{
+       return id_name(ob) + "-mesh";
+}
+
+static std::string get_light_id(Object *ob)
+{
+       return id_name(ob) + "-light";
+}
+
+static std::string get_camera_id(Object *ob)
+{
+       return id_name(ob) + "-camera";
+}
+
+static void replace_chars(char *str, char chars[], char with)
+{
+       char *ch, *p;
+
+       for (ch = chars; *ch; ch++) {
+               while ((p = strchr(str, *ch))) {
+                       *p = with;
+               }
+       }
+}
+
+/*
+  Utilities to avoid code duplication.
+  Definition can take some time to understand, but they should be useful.
+*/
+
+// f should have
+// void operator()(Object* ob)
+template<class Functor>
+void forEachMeshObjectInScene(Scene *sce, Functor &f)
+{
+       
+       Base *base= (Base*) sce->base.first;
+       while(base) {
+               Object *ob = base->object;
+               
+               if (ob->type == OB_MESH && ob->data) {
+                       f(ob);
+               }
+               base= base->next;
+               
+       }
+}
+
+template<class Functor>
+void forEachObjectInScene(Scene *sce, Functor &f)
+{
+       Base *base= (Base*) sce->base.first;
+       while(base) {
+               Object *ob = base->object;
+                       
+               f(ob);
+
+               base= base->next;
+       }
+}
+
+template<class Functor>
+void forEachCameraObjectInScene(Scene *sce, Functor &f)
+{
+       Base *base= (Base*) sce->base.first;
+       while(base) {
+               Object *ob = base->object;
+                       
+               if (ob->type == OB_CAMERA && ob->data) {
+                       f(ob, sce);
+               }
+               base= base->next;
+       }
+}
+
+template<class Functor>
+void forEachLampObjectInScene(Scene *sce, Functor &f)
+{
+       Base *base= (Base*) sce->base.first;
+       while(base) {
+               Object *ob = base->object;
+                       
+               if (ob->type == OB_LAMP && ob->data) {
+                       f(ob);
+               }
+               base= base->next;
+       }
+}
+
+// used in forEachMaterialInScene
+template <class MaterialFunctor>
+class ForEachMaterialFunctor
+{
+       std::vector<std::string> mMat; // contains list of material names, to avoid duplicate calling of f
+       MaterialFunctor *f;
+public:
+       ForEachMaterialFunctor(MaterialFunctor *f) : f(f) { }
+       void operator ()(Object *ob)
+       {
+               int a;
+               for(a = 0; a < ob->totcol; a++) {
+
+                       Material *ma = give_current_material(ob, a+1);
+
+                       if (!ma) continue;
+
+                       if (find(mMat.begin(), mMat.end(), id_name(ma)) == mMat.end()) {
+                               (*this->f)(ma, ob);
+
+                               mMat.push_back(id_name(ma));
+                       }
+               }
+       }
+};
+
+// calls f for each unique material linked to each object in sce
+// f should have
+// void operator()(Material* ma)
+template<class Functor>
+void forEachMaterialInScene(Scene *sce, Functor &f)
+{
+       ForEachMaterialFunctor<Functor> matfunc(&f);
+       forEachMeshObjectInScene(sce, matfunc);
+}
+
+// OB_MESH is assumed
+std::string getActiveUVLayerName(Object *ob)
+{
+       Mesh *me = (Mesh*)ob->data;
+
+       int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+       if (num_layers)
+               return std::string(CustomData_get_active_layer_name(&me->fdata, CD_MTFACE));
+               
+       return "";
+}
+
+// TODO: optimize UV sets by making indexed list with duplicates removed
+class GeometryExporter : COLLADASW::LibraryGeometries
+{
+       Scene *mScene;
+public:
+       GeometryExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryGeometries(sw) {}
+
+       void exportGeom(Scene *sce)
+       {
+               openLibrary();
+
+               mScene = sce;
+               forEachMeshObjectInScene(sce, *this);
+
+               closeLibrary();
+       }
+
+       void operator()(Object *ob)
+       {
+               // XXX don't use DerivedMesh, Mesh instead?
+
+#if 0          
+               DerivedMesh *dm = mesh_get_derived_final(mScene, ob, CD_MASK_BAREMESH);
+#endif
+               Mesh *me = (Mesh*)ob->data;
+               std::string geom_id = get_geometry_id(ob);
+               
+               // openMesh(geoId, geoName, meshId)
+               openMesh(geom_id);
+               
+               // writes <source> for vertex coords
+               createVertsSource(geom_id, me);
+               
+               // writes <source> for normal coords
+               createNormalsSource(geom_id, me);
+
+               int has_uvs = CustomData_has_layer(&me->fdata, CD_MTFACE);
+               
+               // writes <source> for uv coords if mesh has uv coords
+               if (has_uvs) {
+                       createTexcoordsSource(geom_id, (Mesh*)ob->data);
+               }
+               // <vertices>
+               COLLADASW::Vertices verts(mSW);
+               verts.setId(getIdBySemantics(geom_id, COLLADASW::VERTEX));
+               COLLADASW::InputList &input_list = verts.getInputList();
+               COLLADASW::Input input(COLLADASW::POSITION, getUrlBySemantics(geom_id, COLLADASW::POSITION));
+               input_list.push_back(input);
+               verts.add();
+
+               // XXX slow             
+               if (ob->totcol) {
+                       for(int a = 0; a < ob->totcol; a++)     {
+                               // account for NULL materials, this should not normally happen?
+                               Material *ma = give_current_material(ob, a + 1);
+                               createPolylist(ma != NULL, a, has_uvs, ob, geom_id);
+                       }
+               }
+               else {
+                       createPolylist(false, 0, has_uvs, ob, geom_id);
+               }
+               
+               closeMesh();
+               closeGeometry();
+               
+#if 0
+               dm->release(dm);
+#endif
+       }
+
+       // powerful because it handles both cases when there is material and when there's not
+       void createPolylist(bool has_material,
+                                               int material_index,
+                                               bool has_uvs,
+                                               Object *ob,
+                                               std::string& geom_id)
+       {
+#if 0
+               MFace *mfaces = dm->getFaceArray(dm);
+               int totfaces = dm->getNumFaces(dm);
+#endif
+               Mesh *me = (Mesh*)ob->data;
+               MFace *mfaces = me->mface;
+               int totfaces = me->totface;
+
+               // <vcount>
+               int i;
+               int faces_in_polylist = 0;
+               std::vector<unsigned long> vcount_list;
+
+               // count faces with this material
+               for (i = 0; i < totfaces; i++) {
+                       MFace *f = &mfaces[i];
+                       
+                       if ((has_material && f->mat_nr == material_index) || !has_material) {
+                               faces_in_polylist++;
+                               if (f->v4 == 0) {
+                                       vcount_list.push_back(3);
+                               }
+                               else {
+                                       vcount_list.push_back(4);
+                               }
+                       }
+               }
+
+               // no faces using this material
+               if (faces_in_polylist == 0) {
+                       return;
+               }
+                       
+               Material *ma = has_material ? give_current_material(ob, material_index + 1) : NULL;
+               COLLADASW::Polylist polylist(mSW);
+                       
+               // sets count attribute in <polylist>
+               polylist.setCount(faces_in_polylist);
+                       
+               // sets material name
+               if (has_material)
+                       polylist.setMaterial(id_name(ma));
+                               
+               COLLADASW::InputList &til = polylist.getInputList();
+                       
+               // creates <input> in <polylist> for vertices 
+               COLLADASW::Input input1(COLLADASW::VERTEX, getUrlBySemantics
+                                                               (geom_id, COLLADASW::VERTEX), 0);
+                       
+               // creates <input> in <polylist> for normals
+               COLLADASW::Input input2(COLLADASW::NORMAL, getUrlBySemantics
+                                                               (geom_id, COLLADASW::NORMAL), 0);
+                       
+               til.push_back(input1);
+               til.push_back(input2);
+                       
+               // if mesh has uv coords writes <input> for TEXCOORD
+               int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+
+               for (i = 0; i < num_layers; i++) {
+                       char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i);
+                       COLLADASW::Input input3(COLLADASW::TEXCOORD,
+                                                                       makeUrl(makeTexcoordSourceId(geom_id, i)),
+                                                                       1, // offset always 1, this is only until we have optimized UV sets
+                                                                       i  // set number equals UV layer index
+                                                                       );
+                       til.push_back(input3);
+               }
+                       
+               // sets <vcount>
+               polylist.setVCountList(vcount_list);
+                       
+               // performs the actual writing
+               polylist.prepareToAppendValues();
+                       
+               // <p>
+               int texindex = 0;
+               for (i = 0; i < totfaces; i++) {
+                       MFace *f = &mfaces[i];
+
+                       if ((has_material && f->mat_nr == material_index) || !has_material) {
+
+                               unsigned int *v = &f->v1;
+                               for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) {
+                                       polylist.appendValues(v[j]);
+
+                                       if (has_uvs)
+                                               polylist.appendValues(texindex + j);
+                               }
+                       }
+
+                       texindex += 3;
+                       if (f->v4 != 0)
+                               texindex++;
+               }
+                       
+               polylist.finish();
+       }
+       
+       // creates <source> for positions
+       void createVertsSource(std::string geom_id, Mesh *me)
+       {
+#if 0
+               int totverts = dm->getNumVerts(dm);
+               MVert *verts = dm->getVertArray(dm);
+#endif
+               int totverts = me->totvert;
+               MVert *verts = me->mvert;
+               
+               COLLADASW::FloatSourceF source(mSW);
+               source.setId(getIdBySemantics(geom_id, COLLADASW::POSITION));
+               source.setArrayId(getIdBySemantics(geom_id, COLLADASW::POSITION) +
+                                                 ARRAY_ID_SUFFIX);
+               source.setAccessorCount(totverts);
+               source.setAccessorStride(3);
+               COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+               param.push_back("X");
+               param.push_back("Y");
+               param.push_back("Z");
+               /*main function, it creates <source id = "">, <float_array id = ""
+                 count = ""> */
+               source.prepareToAppendValues();
+               //appends data to <float_array>
+               int i = 0;
+               for (i = 0; i < totverts; i++) {
+                       source.appendValues(verts[i].co[0], verts[i].co[1], verts[i].co[2]);                    
+               }
+               
+               source.finish();
+       
+       }
+
+       std::string makeTexcoordSourceId(std::string& geom_id, int layer_index)
+       {
+               char suffix[20];
+               sprintf(suffix, "-%d", layer_index);
+               return getIdBySemantics(geom_id, COLLADASW::TEXCOORD) + suffix;
+       }
+
+       //creates <source> for texcoords
+       void createTexcoordsSource(std::string geom_id, Mesh *me)
+       {
+#if 0
+               int totfaces = dm->getNumFaces(dm);
+               MFace *mfaces = dm->getFaceArray(dm);
+#endif
+               int totfaces = me->totface;
+               MFace *mfaces = me->mface;
+
+               int totuv = 0;
+               int i;
+
+               // count totuv
+               for (i = 0; i < totfaces; i++) {
+                       MFace *f = &mfaces[i];
+                       if (f->v4 == 0) {
+                               totuv+=3;
+                       }
+                       else {
+                               totuv+=4;
+                       }
+               }
+
+               int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+
+               // write <source> for each layer
+               // each <source> will get id like meshName + "map-channel-1"
+               for (int a = 0; a < num_layers; a++) {
+                       MTFace *tface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, a);
+                       char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, a);
+                       
+                       COLLADASW::FloatSourceF source(mSW);
+                       std::string layer_id = makeTexcoordSourceId(geom_id, a);
+                       source.setId(layer_id);
+                       source.setArrayId(layer_id + ARRAY_ID_SUFFIX);
+                       
+                       source.setAccessorCount(totuv);
+                       source.setAccessorStride(2);
+                       COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+                       param.push_back("X");
+                       param.push_back("Y");
+                       
+                       source.prepareToAppendValues();
+                       
+                       for (i = 0; i < totfaces; i++) {
+                               MFace *f = &mfaces[i];
+                               
+                               for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) {
+                                       source.appendValues(tface[i].uv[j][0],
+                                                                               tface[i].uv[j][1]);
+                               }
+                       }
+                       
+                       source.finish();
+               }
+       }
+
+
+       //creates <source> for normals
+       void createNormalsSource(std::string geom_id, Mesh *me)
+       {
+#if 0
+               int totverts = dm->getNumVerts(dm);
+               MVert *verts = dm->getVertArray(dm);
+#endif
+
+               int totverts = me->totvert;
+               MVert *verts = me->mvert;
+
+               COLLADASW::FloatSourceF source(mSW);
+               source.setId(getIdBySemantics(geom_id, COLLADASW::NORMAL));
+               source.setArrayId(getIdBySemantics(geom_id, COLLADASW::NORMAL) +
+                                                 ARRAY_ID_SUFFIX);
+               source.setAccessorCount(totverts);
+               source.setAccessorStride(3);
+               COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+               param.push_back("X");
+               param.push_back("Y");
+               param.push_back("Z");
+               
+               source.prepareToAppendValues();
+               
+               int i = 0;
+               
+               for( i = 0; i < totverts; ++i ){
+                       
+                       source.appendValues(float(verts[i].no[0]/32767.0),
+                                                               float(verts[i].no[1]/32767.0),
+                                                               float(verts[i].no[2]/32767.0));
+                               
+               }
+               source.finish();
+       }
+       
+       std::string getIdBySemantics(std::string geom_id, COLLADASW::Semantics type, std::string other_suffix = "") {
+               return geom_id + getSuffixBySemantic(type) + other_suffix;
+       }
+       
+       
+       COLLADASW::URI getUrlBySemantics(std::string geom_id, COLLADASW::Semantics type, std::string other_suffix = "") {
+               
+               std::string id(getIdBySemantics(geom_id, type, other_suffix));
+               return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
+               
+       }
+
+       COLLADASW::URI makeUrl(std::string id)
+       {
+               return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
+       }
+       
+
+       /*      int getTriCount(MFace *faces, int totface) {
+               int i;
+               int tris = 0;
+               for (i = 0; i < totface; i++) {
+                       // if quad
+                       if (faces[i].v4 != 0)
+                               tris += 2;
+                       else
+                               tris++;
+               }
+
+               return tris;
+               }*/
+};
+
+class TransformWriter : protected TransformBase
+{
+protected:
+       void add_node_transform(COLLADASW::Node& node, float mat[][4], float parent_mat[][4])
+       {
+               float loc[3], rot[3], size[3];
+               float local[4][4];
+
+               if (parent_mat) {
+                       float invpar[4][4];
+                       Mat4Invert(invpar, parent_mat);
+                       Mat4MulMat4(local, mat, invpar);
+               }
+               else {
+                       Mat4CpyMat4(local, mat);
+               }
+
+               TransformBase::decompose(local, loc, rot, size);
+               
+               /*
+               // this code used to create a single <rotate> representing object rotation
+               float quat[4];
+               float axis[3];
+               float angle;
+               double angle_deg;
+               EulToQuat(rot, quat);
+               NormalQuat(quat);
+               QuatToAxisAngle(quat, axis, &angle);
+               angle_deg = angle * 180.0f / M_PI;
+               node.addRotate(axis[0], axis[1], axis[2], angle_deg);
+               */
+               node.addTranslate("location", loc[0], loc[1], loc[2]);
+
+               node.addRotateZ("rotationZ", COLLADABU::Math::Utils::radToDegF(rot[2]));
+               node.addRotateY("rotationY", COLLADABU::Math::Utils::radToDegF(rot[1]));
+               node.addRotateX("rotationX", COLLADABU::Math::Utils::radToDegF(rot[0]));
+
+               node.addScale("scale", size[0], size[1], size[2]);
+       }
+};
+
+class InstanceWriter
+{
+protected:
+       void add_material_bindings(COLLADASW::BindMaterial& bind_material, Object *ob)
+       {
+               for(int a = 0; a < ob->totcol; a++)     {
+                       Material *ma = give_current_material(ob, a+1);
+                               
+                       COLLADASW::InstanceMaterialList& iml = bind_material.getInstanceMaterialList();
+
+                       if (ma) {
+                               std::string matid(id_name(ma));
+                               COLLADASW::InstanceMaterial im(matid, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid));
+                               
+                               // create <bind_vertex_input> for each uv layer
+                               Mesh *me = (Mesh*)ob->data;
+                               int totlayer = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+                               
+                               for (int b = 0; b < totlayer; b++) {
+                                       char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, b);
+                                       im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", b));
+                               }
+                               
+                               iml.push_back(im);
+                       }
+               }
+       }
+};
+
+// XXX exporter writes wrong data for shared armatures.  A separate
+// controller should be written for each armature-mesh binding how do
+// we make controller ids then?
+class ArmatureExporter: public COLLADASW::LibraryControllers, protected TransformWriter, protected InstanceWriter
+{
+private:
+       Scene *scene;
+
+public:
+       ArmatureExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryControllers(sw) {}
+
+       // write bone nodes
+       void add_armature_bones(Object *ob_arm, Scene *sce)
+       {
+               // write bone nodes
+               bArmature *arm = (bArmature*)ob_arm->data;
+               for (Bone *bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) {
+                       // start from root bones
+                       if (!bone->parent)
+                               add_bone_node(bone, ob_arm);
+               }
+       }
+
+       bool is_skinned_mesh(Object *ob)
+       {
+               return get_assigned_armature(ob) != NULL;
+       }
+
+       void add_instance_controller(Object *ob)
+       {
+               Object *ob_arm = get_assigned_armature(ob);
+               bArmature *arm = (bArmature*)ob_arm->data;
+
+               const std::string& controller_id = get_controller_id(ob_arm);
+
+               COLLADASW::InstanceController ins(mSW);
+               ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
+
+               // write root bone URLs
+               Bone *bone;
+               for (bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) {
+                       if (!bone->parent)
+                               ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_joint_id(bone, ob_arm)));
+               }
+
+               InstanceWriter::add_material_bindings(ins.getBindMaterial(), ob);
+                       
+               ins.add();
+       }
+
+       void export_controllers(Scene *sce)
+       {
+               scene = sce;
+
+               openLibrary();
+
+               forEachMeshObjectInScene(sce, *this);
+
+               closeLibrary();
+       }
+
+       void operator()(Object *ob)
+       {
+               Object *ob_arm = get_assigned_armature(ob);
+
+               if (ob_arm /*&& !already_written(ob_arm)*/)
+                       export_controller(ob, ob_arm);
+       }
+
+private:
+
+       UnitConverter converter;
+
+#if 0
+       std::vector<Object*> written_armatures;
+
+       bool already_written(Object *ob_arm)
+       {
+               return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != written_armatures.end();
+       }
+
+       void wrote(Object *ob_arm)
+       {
+               written_armatures.push_back(ob_arm);
+       }
+
+       void find_objects_using_armature(Object *ob_arm, std::vector<Object *>& objects, Scene *sce)
+       {
+               objects.clear();
+
+               Base *base= (Base*) sce->base.first;
+               while(base) {
+                       Object *ob = base->object;
+                       
+                       if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) {
+                               objects.push_back(ob);
+                       }
+
+                       base= base->next;
+               }
+       }
+#endif
+
+       Object *get_assigned_armature(Object *ob)
+       {
+               Object *ob_arm = NULL;
+
+               if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) {
+                       ob_arm = ob->parent;
+               }
+               else {
+                       ModifierData *mod = (ModifierData*)ob->modifiers.first;
+                       while (mod) {
+                               if (mod->type == eModifierType_Armature) {
+                                       ob_arm = ((ArmatureModifierData*)mod)->object;
+                               }
+
+                               mod = mod->next;
+                       }
+               }
+
+               return ob_arm;
+       }
+
+       std::string get_joint_id(Bone *bone, Object *ob_arm)
+       {
+               return id_name(ob_arm) + "_" + bone->name;
+       }
+
+       std::string get_joint_sid(Bone *bone)
+       {
+               char name[100];
+               BLI_strncpy(name, bone->name, sizeof(name));
+
+               // these chars have special meaning in SID
+               replace_chars(name, ".()", '_');
+
+               return name;
+       }
+
+       // parent_mat is armature-space
+       void add_bone_node(Bone *bone, Object *ob_arm)
+       {
+               std::string node_id = get_joint_id(bone, ob_arm);
+               std::string node_name = std::string(bone->name);
+               std::string node_sid = get_joint_sid(bone);
+
+               COLLADASW::Node node(mSW);
+
+               node.setType(COLLADASW::Node::JOINT);
+               node.setNodeId(node_id);
+               node.setNodeName(node_name);
+               node.setNodeSid(node_sid);
+
+               node.start();
+
+               add_bone_transform(ob_arm, bone, node);
+
+               for (Bone *child = (Bone*)bone->childbase.first; child; child = child->next) {
+                       add_bone_node(child, ob_arm);
+               }
+
+               node.end();
+       }
+
+       void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node)
+       {
+               bPose *pose = ob_arm->pose;
+
+               bPoseChannel *pchan = get_pose_channel(ob_arm->pose, bone->name);
+
+               float mat[4][4];
+
+               if (bone->parent) {
+                       // get bone-space matrix from armature-space
+                       bPoseChannel *parchan = get_pose_channel(ob_arm->pose, bone->parent->name);
+
+                       float invpar[4][4];
+                       Mat4Invert(invpar, parchan->pose_mat);
+                       Mat4MulMat4(mat, pchan->pose_mat, invpar);
+               }
+               else {
+                       // get world-space from armature-space
+                       Mat4MulMat4(mat, pchan->pose_mat, ob_arm->obmat);
+               }
+
+               TransformWriter::add_node_transform(node, mat, NULL);
+       }
+
+       std::string get_controller_id(Object *ob_arm)
+       {
+               return id_name(ob_arm) + SKIN_CONTROLLER_ID_SUFFIX;
+       }
+
+       // ob should be of type OB_MESH
+       // both args are required
+       void export_controller(Object* ob, Object *ob_arm)
+       {
+               // joint names
+               // joint inverse bind matrices
+               // vertex weights
+
+               // input:
+               // joint names: ob -> vertex group names
+               // vertex group weights: me->dvert -> groups -> index, weight
+
+               /*
+               me->dvert:
+
+               typedef struct MDeformVert {
+                       struct MDeformWeight *dw;
+                       int totweight;
+                       int flag;       // flag only in use for weightpaint now
+               } MDeformVert;
+
+               typedef struct MDeformWeight {
+                       int                             def_nr;
+                       float                   weight;
+               } MDeformWeight;
+               */
+
+               Mesh *me = (Mesh*)ob->data;
+               if (!me->dvert) return;
+
+               std::string controller_name = id_name(ob_arm);
+               std::string controller_id = get_controller_id(ob_arm);
+
+               openSkin(controller_id, controller_name,
+                                COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob)));
+
+               add_bind_shape_mat(ob);
+
+               std::string joints_source_id = add_joints_source(ob_arm, &ob->defbase, controller_id);
+               std::string inv_bind_mat_source_id = add_inv_bind_mats_source(ob_arm, &ob->defbase, controller_id);
+               std::string weights_source_id = add_weights_source(me, controller_id);
+
+               add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id);
+               add_vertex_weights_element(weights_source_id, joints_source_id, me, ob_arm, &ob->defbase);
+
+               closeSkin();
+               closeController();
+       }
+
+       void add_joints_element(ListBase *defbase,
+                                                       const std::string& joints_source_id, const std::string& inv_bind_mat_source_id)
+       {
+               COLLADASW::JointsElement joints(mSW);
+               COLLADASW::InputList &input = joints.getInputList();
+
+               int offset = 0;
+               input.push_back(COLLADASW::Input(COLLADASW::JOINT, // constant declared in COLLADASWInputList.h
+                                                                                COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id)));
+        input.push_back(COLLADASW::Input(COLLADASW::BINDMATRIX,
+                                                                                COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, inv_bind_mat_source_id)));
+               joints.add();
+       }
+
+       void add_bind_shape_mat(Object *ob)
+       {
+               double bind_mat[4][4];
+
+               converter.mat4_to_dae_double(bind_mat, ob->obmat);
+
+               addBindShapeTransform(bind_mat);
+       }
+
+       std::string add_joints_source(Object *ob_arm, ListBase *defbase, const std::string& controller_id)
+       {
+               std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX;
+
+               int totjoint = 0;
+               bDeformGroup *def;
+               for (def = (bDeformGroup*)defbase->first; def; def = def->next) {
+                       if (is_bone_defgroup(ob_arm, def))
+                               totjoint++;
+               }
+
+               COLLADASW::NameSource source(mSW);
+               source.setId(source_id);
+               source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+               source.setAccessorCount(totjoint);
+               source.setAccessorStride(1);
+               
+               COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+               param.push_back("JOINT");
+
+               source.prepareToAppendValues();
+
+               for (def = (bDeformGroup*)defbase->first; def; def = def->next) {
+                       Bone *bone = get_bone_from_defgroup(ob_arm, def);
+                       if (bone)
+                               source.appendValues(get_joint_sid(bone));
+               }
+
+               source.finish();
+
+               return source_id;
+       }
+
+       std::string add_inv_bind_mats_source(Object *ob_arm, ListBase *defbase, const std::string& controller_id)
+       {
+               std::string source_id = controller_id + BIND_POSES_SOURCE_ID_SUFFIX;
+
+               COLLADASW::FloatSourceF source(mSW);
+               source.setId(source_id);
+               source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+               source.setAccessorCount(BLI_countlist(defbase));
+               source.setAccessorStride(16);
+               
+               source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4);
+               COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+               param.push_back("TRANSFORM");
+
+               source.prepareToAppendValues();
+
+               bPose *pose = ob_arm->pose;
+               bArmature *arm = (bArmature*)ob_arm->data;
+
+               int flag = arm->flag;
+
+               // put armature in rest position
+               if (!(arm->flag & ARM_RESTPOS)) {
+                       arm->flag |= ARM_RESTPOS;
+                       where_is_pose(scene, ob_arm);
+               }
+
+               for (bDeformGroup *def = (bDeformGroup*)defbase->first; def; def = def->next) {
+                       if (is_bone_defgroup(ob_arm, def)) {
+
+                               bPoseChannel *pchan = get_pose_channel(pose, def->name);
+
+                               float mat[4][4];
+                               float world[4][4];
+                               float inv_bind_mat[4][4];
+
+                               // make world-space matrix, pose_mat is armature-space
+                               Mat4MulMat4(world, pchan->pose_mat, ob_arm->obmat);
+                               
+                               Mat4Invert(mat, world);
+                               converter.mat4_to_dae(inv_bind_mat, mat);
+
+                               source.appendValues(inv_bind_mat);
+                       }
+               }
+
+               // back from rest positon
+               if (!(flag & ARM_RESTPOS)) {
+                       arm->flag = flag;
+                       where_is_pose(scene, ob_arm);
+               }
+
+               source.finish();
+
+               return source_id;
+       }
+
+       Bone *get_bone_from_defgroup(Object *ob_arm, bDeformGroup* def)
+       {
+               bPoseChannel *pchan = get_pose_channel(ob_arm->pose, def->name);
+               return pchan ? pchan->bone : NULL;
+       }
+
+       bool is_bone_defgroup(Object *ob_arm, bDeformGroup* def)
+       {
+               return get_bone_from_defgroup(ob_arm, def) != NULL;
+       }
+
+       std::string add_weights_source(Mesh *me, const std::string& controller_id)
+       {
+               std::string source_id = controller_id + WEIGHTS_SOURCE_ID_SUFFIX;
+
+               int i;
+               int totweight = 0;
+
+               for (i = 0; i < me->totvert; i++) {
+                       totweight += me->dvert[i].totweight;
+               }
+
+               COLLADASW::FloatSourceF source(mSW);
+               source.setId(source_id);
+               source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+               source.setAccessorCount(totweight);
+               source.setAccessorStride(1);
+               
+               COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+               param.push_back("WEIGHT");
+
+               source.prepareToAppendValues();
+
+               // NOTE: COLLADA spec says weights should be normalized
+
+               for (i = 0; i < me->totvert; i++) {
+                       MDeformVert *vert = &me->dvert[i];
+                       for (int j = 0; j < vert->totweight; j++) {
+                               source.appendValues(vert->dw[j].weight);
+                       }
+               }
+
+               source.finish();
+
+               return source_id;
+       }
+
+       void add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id, Mesh *me,
+                                                                       Object *ob_arm, ListBase *defbase)
+       {
+               COLLADASW::VertexWeightsElement weights(mSW);
+               COLLADASW::InputList &input = weights.getInputList();
+
+               int offset = 0;
+               input.push_back(COLLADASW::Input(COLLADASW::JOINT, // constant declared in COLLADASWInputList.h
+                                                                                COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id), offset++));
+        input.push_back(COLLADASW::Input(COLLADASW::WEIGHT,
+                                                                                COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id), offset++));
+
+               weights.setCount(me->totvert);
+
+               // write number of deformers per vertex
+               COLLADASW::PrimitivesBase::VCountList vcount;
+               int i;
+               for (i = 0; i < me->totvert; i++) {
+                       vcount.push_back(me->dvert[i].totweight);
+               }
+
+               weights.prepareToAppendVCountValues();
+               weights.appendVertexCount(vcount);
+
+               // def group index -> joint index
+               std::map<int, int> joint_index_by_def_index;
+               bDeformGroup *def;
+               int j;
+               for (def = (bDeformGroup*)defbase->first, i = 0, j = 0; def; def = def->next, i++) {
+                       if (is_bone_defgroup(ob_arm, def))
+                               joint_index_by_def_index[i] = j++;
+                       else
+                               joint_index_by_def_index[i] = -1;
+               }
+
+               weights.CloseVCountAndOpenVElement();
+
+               // write deformer index - weight index pairs
+               int weight_index = 0;
+               for (i = 0; i < me->totvert; i++) {
+                       MDeformVert *dvert = &me->dvert[i];
+                       for (int j = 0; j < dvert->totweight; j++) {
+                               weights.appendValues(joint_index_by_def_index[dvert->dw[j].def_nr]);
+                               weights.appendValues(weight_index++);
+                       }
+               }
+
+               weights.finish();
+       }
+};
+
+class SceneExporter: COLLADASW::LibraryVisualScenes, protected TransformWriter, protected InstanceWriter
+{
+       ArmatureExporter *arm_exporter;
+public:
+       SceneExporter(COLLADASW::StreamWriter *sw, ArmatureExporter *arm) : COLLADASW::LibraryVisualScenes(sw),
+                                                                                                                                               arm_exporter(arm) {}
+       
+       void exportScene(Scene *sce) {
+               // <library_visual_scenes> <visual_scene>
+               openVisualScene(id_name(sce));
+
+               // write <node>s
+               //forEachMeshObjectInScene(sce, *this);
+               //forEachCameraObjectInScene(sce, *this);
+               //forEachLampObjectInScene(sce, *this);
+               exportHierarchy(sce);
+
+               // </visual_scene> </library_visual_scenes>
+               closeVisualScene();
+
+               closeLibrary();
+       }
+
+       void exportHierarchy(Scene *sce)
+       {
+               Base *base= (Base*) sce->base.first;
+               while(base) {
+                       Object *ob = base->object;
+
+                       if (!ob->parent) {
+                               switch(ob->type) {
+                               case OB_MESH:
+                               case OB_CAMERA:
+                               case OB_LAMP:
+                               case OB_EMPTY:
+                               case OB_ARMATURE:
+                                       // write nodes....
+                                       writeNodes(ob, sce);
+                                       break;
+                               }
+                       }
+
+                       base= base->next;
+               }
+       }
+
+
+       // called for each object
+       //void operator()(Object *ob) {
+       void writeNodes(Object *ob, Scene *sce)
+       {
+               COLLADASW::Node node(mSW);
+               node.setNodeId(id_name(ob));
+               node.setType(COLLADASW::Node::NODE);
+
+               node.start();
+
+               bool is_skinned_mesh = arm_exporter->is_skinned_mesh(ob);
+
+               float mat[4][4];
+               
+               if (ob->type == OB_MESH && is_skinned_mesh)
+                       // for skinned mesh we write obmat in <bind_shape_matrix>
+                       Mat4One(mat);
+               else
+                       Mat4CpyMat4(mat, ob->obmat);
+
+               TransformWriter::add_node_transform(node, mat, ob->parent ? ob->parent->obmat : NULL);
+               
+               // <instance_geometry>
+               if (ob->type == OB_MESH) {
+                       if (is_skinned_mesh) {
+                               arm_exporter->add_instance_controller(ob);
+                       }
+                       else {
+                               COLLADASW::InstanceGeometry instGeom(mSW);
+                               instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob)));
+
+                               InstanceWriter::add_material_bindings(instGeom.getBindMaterial(), ob);
+                       
+                               instGeom.add();
+                       }
+               }
+
+               // <instance_controller>
+               else if (ob->type == OB_ARMATURE) {
+                       arm_exporter->add_armature_bones(ob, sce);
+
+                       // XXX this looks unstable...
+                       node.end();
+               }
+               
+               // <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) {
+               }
+
+               // write nodes for child objects
+               Base *b = (Base*) sce->base.first;
+               while(b) {
+                       // cob - child object
+                       Object *cob = b->object;
+
+                       if (cob->parent == ob) {
+                               switch(cob->type) {
+                               case OB_MESH:
+                               case OB_CAMERA:
+                               case OB_LAMP:
+                               case OB_EMPTY:
+                               case OB_ARMATURE:
+                                       // write node...
+                                       writeNodes(cob, sce);
+                                       break;
+                               }
+                       }
+
+                       b = b->next;
+               }
+
+               if (ob->type != OB_ARMATURE)
+                       node.end();
+       }
+};
+
+class ImagesExporter: COLLADASW::LibraryImages
+{
+       const char *mfilename;
+       std::vector<std::string> mImages; // contains list of written images, to avoid duplicates
+public:
+       ImagesExporter(COLLADASW::StreamWriter *sw, const char* filename) : COLLADASW::LibraryImages(sw), mfilename(filename)
+       {}
+       
+       void exportImages(Scene *sce)
+       {
+               openLibrary();
+
+               forEachMaterialInScene(sce, *this);
+
+               closeLibrary();
+       }
+
+       void operator()(Material *ma, Object *ob)
+       {
+               int a;
+               for (a = 0; a < MAX_MTEX; a++) {
+                       MTex *mtex = ma->mtex[a];
+                       if (mtex && mtex->tex && mtex->tex->ima) {
+
+                               Image *image = mtex->tex->ima;
+                               std::string name(id_name(image));
+                               char rel[FILE_MAX];
+                               char abs[FILE_MAX];
+                               char src[FILE_MAX];
+                               char dir[FILE_MAX];
+                               
+                               BLI_split_dirfile_basic(mfilename, dir, NULL);
+
+                               BKE_get_image_export_path(image, dir, abs, sizeof(abs), rel, sizeof(rel));
+
+                               if (strlen(abs)) {
+
+                                       // make absolute source path
+                                       BLI_strncpy(src, image->name, sizeof(src));
+                                       BLI_convertstringcode(src, G.sce);
+
+                                       // make dest directory if it doesn't exist
+                                       BLI_make_existing_file(abs);
+                               
+                                       if (BLI_copy_fileops(src, abs) != 0) {
+                                               fprintf(stderr, "Cannot copy image to file's directory. \n");
+                                       }
+                               } 
+                               
+                               if (find(mImages.begin(), mImages.end(), name) == mImages.end()) {
+                                       COLLADASW::Image img(COLLADABU::URI(COLLADABU::URI::nativePathToUri(rel)), name);
+                                       img.add(mSW);
+
+                                       mImages.push_back(name);
+                               }
+                       }
+               }
+       }
+};
+
+
+class EffectsExporter: COLLADASW::LibraryEffects
+{
+public:
+       EffectsExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryEffects(sw){}
+       void exportEffects(Scene *sce)
+       {
+               openLibrary();
+
+               forEachMaterialInScene(sce, *this);
+
+               closeLibrary();
+       }
+
+       void operator()(Material *ma, Object *ob)
+       {
+               // create a list of indices to textures of type TEX_IMAGE
+               std::vector<int> tex_indices;
+               createTextureIndices(ma, tex_indices);
+
+               openEffect(id_name(ma) + "-effect");
+               
+               COLLADASW::EffectProfile ep(mSW);
+               ep.setProfileType(COLLADASW::EffectProfile::COMMON);
+               ep.openProfile();
+               // set shader type - one of three blinn, phong or lambert
+               if (ma->spec_shader == MA_SPEC_BLINN) {
+                       ep.setShaderType(COLLADASW::EffectProfile::BLINN);
+                       // shininess
+                       ep.setShininess(ma->spec);
+               }
+               else if (ma->spec_shader == MA_SPEC_PHONG) {
+                       ep.setShaderType(COLLADASW::EffectProfile::PHONG);
+                       // shininess
+                       // XXX not sure, stolen this from previous Collada plugin
+                       ep.setShininess(ma->har / 4);
+               }
+               else {
+                       // XXX write warning "Current shader type is not supported" 
+                       ep.setShaderType(COLLADASW::EffectProfile::LAMBERT);
+               }
+               // index of refraction
+               if (ma->mode & MA_RAYTRANSP) {
+                       ep.setIndexOfRefraction(ma->ang);
+               }
+               else {
+                       ep.setIndexOfRefraction(1.0f);
+               }
+               // transparency
+               ep.setTransparency(ma->alpha);
+               // emission
+               COLLADASW::ColorOrTexture cot = getcol(0.0f, 0.0f, 0.0f, 1.0f);
+               ep.setEmission(cot);
+               ep.setTransparent(cot);
+               // diffuse 
+               cot = getcol(ma->r, ma->g, ma->b, 1.0f);
+               ep.setDiffuse(cot);
+               // ambient
+               cot = getcol(ma->ambr, ma->ambg, ma->ambb, 1.0f);
+               ep.setAmbient(cot);
+               // reflective, reflectivity
+               if (ma->mode & MA_RAYMIRROR) {
+                       cot = getcol(ma->mirr, ma->mirg, ma->mirb, 1.0f);
+                       ep.setReflective(cot);
+                       ep.setReflectivity(ma->ray_mirror);
+               }
+               else {
+                       cot = getcol(0.0f, 0.0f, 0.0f, 1.0f);
+                       ep.setReflective(cot);
+                       ep.setReflectivity(0.0f);
+               }
+               // specular
+               if (ep.getShaderType() != COLLADASW::EffectProfile::LAMBERT) {
+                       cot = getcol(ma->specr, ma->specg, ma->specb, 1.0f);
+                       ep.setSpecular(cot);
+               }
+
+               // XXX make this more readable if possible
+
+               // create <sampler> and <surface> for each image
+               COLLADASW::Sampler samplers[MAX_MTEX];
+               //COLLADASW::Surface surfaces[MAX_MTEX];
+               //void *samp_surf[MAX_MTEX][2];
+               void *samp_surf[MAX_MTEX][1];
+               
+               // image to index to samp_surf map
+               // samp_surf[index] stores 2 pointers, sampler and surface
+               std::map<std::string, int> im_samp_map;
+
+               unsigned int a, b;
+               for (a = 0, b = 0; a < tex_indices.size(); a++) {
+                       MTex *t = ma->mtex[tex_indices[a]];
+                       Image *ima = t->tex->ima;
+                       
+                       std::string key(id_name(ima));
+
+                       // create only one <sampler>/<surface> pair for each unique image
+                       if (im_samp_map.find(key) == im_samp_map.end()) {
+                               //<newparam> <surface> <init_from>
+                       //      COLLADASW::Surface surface(COLLADASW::Surface::SURFACE_TYPE_2D,
+//                                                                                key + COLLADASW::Surface::SURFACE_SID_SUFFIX);
+//                             COLLADASW::SurfaceInitOption sio(COLLADASW::SurfaceInitOption::INIT_FROM);
+//                             sio.setImageReference(key);
+//                             surface.setInitOption(sio);
+                               
+                               //<newparam> <sampler> <source>
+                               COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
+                                                                                  key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
+                                                                                  key + COLLADASW::Sampler::SURFACE_SID_SUFFIX);
+                               sampler.setImageId(key);
+                               // copy values to arrays since they will live longer
+                               samplers[a] = sampler;
+                               //surfaces[a] = surface;
+                               
+                               // store pointers so they can be used later when we create <texture>s
+                               samp_surf[b][0] = &samplers[a];
+                               //samp_surf[b][1] = &surfaces[a];
+                               
+                               im_samp_map[key] = b;
+                               b++;
+                       }
+               }
+
+               // used as fallback when MTex->uvname is "" (this is pretty common)
+               // it is indeed the correct value to use in that case
+               std::string active_uv(getActiveUVLayerName(ob));
+
+               // write textures
+               // XXX very slow
+               for (a = 0; a < tex_indices.size(); a++) {
+                       MTex *t = ma->mtex[tex_indices[a]];
+                       Image *ima = t->tex->ima;
+
+                       // we assume map input is always TEXCO_UV
+
+                       std::string key(id_name(ima));
+                       int i = im_samp_map[key];
+                       COLLADASW::Sampler *sampler = (COLLADASW::Sampler*)samp_surf[i][0];
+                       //COLLADASW::Surface *surface = (COLLADASW::Surface*)samp_surf[i][1];
+
+                       std::string uvname = strlen(t->uvname) ? t->uvname : active_uv;
+
+                       // color
+                       if (t->mapto & MAP_COL) {
+                               ep.setDiffuse(createTexture(ima, uvname, sampler));
+                       }
+                       // ambient
+                       if (t->mapto & MAP_AMB) {
+                               ep.setAmbient(createTexture(ima, uvname, sampler));
+                       }
+                       // specular
+                       if (t->mapto & MAP_SPEC) {
+                               ep.setSpecular(createTexture(ima, uvname, sampler));
+                       }
+                       // emission
+                       if (t->mapto & MAP_EMIT) {
+                               ep.setEmission(createTexture(ima, uvname, sampler));
+                       }
+                       // reflective
+                       if (t->mapto & MAP_REF) {
+                               ep.setReflective(createTexture(ima, uvname, sampler));
+                       }
+                       if (t->mapto & MAP_ALPHA) {
+                               ep.setTransparent(createTexture(ima, uvname, sampler));
+                       }
+               }
+               // performs the actual writing
+               ep.addProfileElements();
+               ep.closeProfile();
+               closeEffect();  
+       }
+       
+       COLLADASW::ColorOrTexture createTexture(Image *ima,
+                                                                                       std::string& uv_layer_name,
+                                                                                       COLLADASW::Sampler *sampler
+                                                                                       /*COLLADASW::Surface *surface*/)
+       {
+               
+               COLLADASW::Texture texture(id_name(ima));
+               texture.setTexcoord(uv_layer_name);
+               //texture.setSurface(*surface);
+               texture.setSampler(*sampler);
+               
+               COLLADASW::ColorOrTexture cot(texture);
+               return cot;
+       }
+       
+       COLLADASW::ColorOrTexture getcol(float r, float g, float b, float a)
+       {
+               COLLADASW::Color color(r,g,b,a);
+               COLLADASW::ColorOrTexture cot(color);
+               return cot;
+       }
+       
+       //returns the array of mtex indices which have image 
+       //need this for exporting textures
+       void createTextureIndices(Material *ma, std::vector<int> &indices)
+       {
+               indices.clear();
+
+               for (int a = 0; a < MAX_MTEX; a++) {
+                       if (ma->mtex[a] &&
+                               ma->mtex[a]->tex->type == TEX_IMAGE &&
+                               ma->mtex[a]->texco == TEXCO_UV){
+                               indices.push_back(a);
+                       }
+               }
+       }
+};
+
+class MaterialsExporter: COLLADASW::LibraryMaterials
+{
+public:
+       MaterialsExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryMaterials(sw){}
+       void exportMaterials(Scene *sce)
+       {
+               openLibrary();
+
+               forEachMaterialInScene(sce, *this);
+
+               closeLibrary();
+       }
+
+       void operator()(Material *ma, Object *ob)
+       {
+               std::string name(id_name(ma));
+
+               openMaterial(name);
+
+               std::string efid = name + "-effect";
+               addInstanceEffect(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, efid));
+
+               closeMaterial();
+       }
+};
+
+class CamerasExporter: COLLADASW::LibraryCameras
+{
+public:
+       CamerasExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryCameras(sw){}
+       void exportCameras(Scene *sce)
+       {
+               openLibrary();
+               
+               forEachCameraObjectInScene(sce, *this);
+               
+               closeLibrary();
+       }
+       void operator()(Object *ob, Scene *sce)
+       {
+               // XXX add other params later
+               Camera *cam = (Camera*)ob->data;
+               std::string cam_id(get_camera_id(ob));
+               std::string cam_name(id_name(cam));
+               
+               if (cam->type == CAM_PERSP) {
+                       COLLADASW::PerspectiveOptic persp(mSW);
+                       persp.setXFov(1.0);
+                       persp.setAspectRatio(0.1);
+                       persp.setZFar(cam->clipend);
+                       persp.setZNear(cam->clipsta);
+                       COLLADASW::Camera ccam(mSW, &persp, cam_id, cam_name);
+                       addCamera(ccam);
+               }
+               else {
+                       COLLADASW::OrthographicOptic ortho(mSW);
+                       ortho.setXMag(1.0);
+                       ortho.setAspectRatio(0.1);
+                       ortho.setZFar(cam->clipend);
+                       ortho.setZNear(cam->clipsta);
+                       COLLADASW::Camera ccam(mSW, &ortho, cam_id, cam_name);
+                       addCamera(ccam);
+               }
+       }       
+};
+
+class LightsExporter: COLLADASW::LibraryLights
+{
+public:
+       LightsExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryLights(sw){}
+       void exportLights(Scene *sce)
+       {
+               openLibrary();
+               
+               forEachLampObjectInScene(sce, *this);
+               
+               closeLibrary();
+       }
+       void operator()(Object *ob)
+       {
+               Lamp *la = (Lamp*)ob->data;
+               std::string la_id(get_light_id(ob));
+               std::string la_name(id_name(la));
+               COLLADASW::Color col(la->r, la->g, la->b);
+               float e = la->energy;
+               
+               // sun
+               if (la->type == LA_SUN) {
+                       COLLADASW::DirectionalLight cla(mSW, la_id, la_name, e);
+                       cla.setColor(col);
+                       addLight(cla);
+               }
+               // hemi
+               else if (la->type == LA_HEMI) {
+                       COLLADASW::AmbientLight cla(mSW, la_id, la_name, e);
+                       cla.setColor(col);
+                       addLight(cla);
+               }
+               // spot
+               else if (la->type == LA_SPOT) {
+                       COLLADASW::SpotLight cla(mSW, la_id, la_name, e);
+                       cla.setColor(col);
+                       cla.setFallOffAngle(la->spotsize);
+                       cla.setFallOffExponent(la->spotblend);
+                       cla.setLinearAttenuation(la->att1);
+                       cla.setQuadraticAttenuation(la->att2);
+                       addLight(cla);
+               }
+               // lamp
+               else if (la->type == LA_LOCAL) {
+                       COLLADASW::PointLight cla(mSW, la_id, la_name, e);
+                       cla.setColor(col);
+                       cla.setLinearAttenuation(la->att1);
+                       cla.setQuadraticAttenuation(la->att2);
+                       addLight(cla);
+               }
+               // area lamp is not supported
+               // it will be exported as a local lamp
+               else {
+                       COLLADASW::PointLight cla(mSW, la_id, la_name, e);
+                       cla.setColor(col);
+                       cla.setLinearAttenuation(la->att1);
+                       cla.setQuadraticAttenuation(la->att2);
+                       addLight(cla);
+               }
+       }
+};
+
+// TODO: it would be better to instantiate animations rather than create a new one per object
+// COLLADA allows this through multiple <channel>s in <animation>.
+// For this to work, we need to know objects that use a certain action.
+class AnimationExporter: COLLADASW::LibraryAnimations
+{
+       Scene *scene;
+       std::map<bActionGroup*, std::vector<FCurve*> > fcurves_actionGroup_map;
+       std::map<bActionGroup*, std::vector<FCurve*> > rotfcurves_actionGroup_map;
+public:
+       AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) {}
+
+       void exportAnimations(Scene *sce)
+       {
+               this->scene = sce;
+
+               openLibrary();
+               
+               forEachObjectInScene(sce, *this);
+               
+               closeLibrary();
+       }
+
+       // create <animation> for each transform axis
+
+       float convert_time(float frame) {
+               return FRA2TIME(frame);
+       }
+
+       float convert_angle(float angle) {
+               return COLLADABU::Math::Utils::radToDegF(angle);
+       }
+
+       std::string get_semantic_suffix(Sampler::Semantic semantic) {
+               switch(semantic) {
+               case Sampler::INPUT:
+                       return INPUT_SOURCE_ID_SUFFIX;
+               case Sampler::OUTPUT:
+                       return OUTPUT_SOURCE_ID_SUFFIX;
+               case Sampler::INTERPOLATION:
+                       return INTERPOLATION_SOURCE_ID_SUFFIX;
+               case Sampler::IN_TANGENT:
+                       return INTANGENT_SOURCE_ID_SUFFIX;
+               case Sampler::OUT_TANGENT:
+                       return OUTTANGENT_SOURCE_ID_SUFFIX;
+               }
+               return "";
+       }
+
+       void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param,
+                                                          Sampler::Semantic semantic, bool rotation, const char *axis) {
+               switch(semantic) {
+               case Sampler::INPUT:
+                       param.push_back("TIME");
+                       break;
+               case Sampler::OUTPUT:
+                       if (rotation) {
+                               param.push_back("ANGLE");
+                       }
+                       else {
+                               param.push_back(axis);
+                       }
+                       break;
+               case Sampler::IN_TANGENT:
+               case Sampler::OUT_TANGENT:
+                       param.push_back("X");
+                       param.push_back("Y");
+                       break;
+               }
+       }
+
+       void get_source_values(BezTriple *bezt, Sampler::Semantic semantic, bool rotation, float *values, int *length)
+       {
+               switch (semantic) {
+               case Sampler::INPUT:
+                       *length = 1;
+                       values[0] = convert_time(bezt->vec[1][0]);
+                       break;
+               case Sampler::OUTPUT:
+                       *length = 1;
+                       if (rotation) {
+                               values[0] = convert_angle(bezt->vec[1][1]);
+                       }
+                       else {
+                               values[0] = bezt->vec[1][1];
+                       }
+                       break;
+               case Sampler::IN_TANGENT:
+               case Sampler::OUT_TANGENT:
+                       // XXX
+                       *length = 2;
+                       break;
+               }
+       }
+
+       std::string create_source(Sampler::Semantic semantic, FCurve *fcu, std::string& anim_id, const char *axis_name)
+       {
+               std::string source_id = anim_id + get_semantic_suffix(semantic);
+
+               //bool is_rotation = !strcmp(fcu->rna_path, "rotation");
+               bool is_rotation = false;
+               
+               if (strstr(fcu->rna_path, "rotation")) is_rotation = true;
+               
+               COLLADASW::FloatSourceF source(mSW);
+               source.setId(source_id);
+               source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+               source.setAccessorCount(fcu->totvert);
+               source.setAccessorStride(1);
+               
+               COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+               add_source_parameters(param, semantic, is_rotation, axis_name);
+
+               source.prepareToAppendValues();
+
+               for (int i = 0; i < fcu->totvert; i++) {
+                       float values[3]; // be careful!
+                       int length;
+
+                       get_source_values(&fcu->bezt[i], semantic, is_rotation, values, &length);
+                       for (int j = 0; j < length; j++)
+                               source.appendValues(values[j]);
+               }
+
+               source.finish();
+
+               return source_id;
+       }
+
+       std::string create_interpolation_source(FCurve *fcu, std::string& anim_id, const char *axis_name)
+       {
+               std::string source_id = anim_id + get_semantic_suffix(Sampler::INTERPOLATION);
+
+               //bool is_rotation = !strcmp(fcu->rna_path, "rotation");
+
+               COLLADASW::NameSource source(mSW);
+               source.setId(source_id);
+               source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+               source.setAccessorCount(fcu->totvert);
+               source.setAccessorStride(1);
+               
+               COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+               param.push_back("INTERPOLATION");
+
+               source.prepareToAppendValues();
+
+               for (int i = 0; i < fcu->totvert; i++) {
+                       // XXX
+                       source.appendValues(LINEAR_NAME);
+               }
+
+               source.finish();
+
+               return source_id;
+       }
+
+       std::string get_transform_sid(char *rna_path, const char *axis_name)
+       {
+               // if (!strcmp(rna_path, "rotation"))
+//                     return std::string(rna_path) + axis_name;
+
+//             return std::string(rna_path) + "." + axis_name;
+               std::string new_rna_path;
+               
+               if (strstr(rna_path, "rotation")) {
+                       new_rna_path = strstr(rna_path, "rotation");
+                       return new_rna_path + axis_name;
+               }
+               else if (strstr(rna_path, "location")) {
+                       new_rna_path = strstr(rna_path, "location");
+                       return new_rna_path + "." + axis_name;
+               }
+               else if (strstr(rna_path, "scale")) {
+                       new_rna_path = strstr(rna_path, "scale");
+                       return new_rna_path + "." + axis_name;
+               }
+               return NULL;
+       }
+
+       void add_animation(FCurve *fcu, std::string ob_name)
+       {
+               const char *axis_names[] = {"X", "Y", "Z"};
+               const char *axis_name = NULL;
+               char c_anim_id[100]; // careful!
+               
+               if (fcu->array_index < 3)
+                       axis_name = axis_names[fcu->array_index];
+
+               BLI_snprintf(c_anim_id, sizeof(c_anim_id), "%s.%s.%s", (char*)ob_name.c_str(), fcu->rna_path, axis_names[fcu->array_index]);
+               std::string anim_id(c_anim_id);
+
+               // check rna_path is one of: rotation, scale, location
+
+               openAnimation(anim_id);
+
+               // create input source
+               std::string input_id = create_source(Sampler::INPUT, fcu, anim_id, axis_name);
+
+               // create output source
+               std::string output_id = create_source(Sampler::OUTPUT, fcu, anim_id, axis_name);
+
+               // create interpolations source
+               std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name);
+
+               std::string sampler_id = anim_id + SAMPLER_ID_SUFFIX;
+               COLLADASW::LibraryAnimations::Sampler sampler(sampler_id);
+               std::string empty;
+               sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
+               sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
+
+               // this input is required
+               sampler.addInput(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
+
+               addSampler(sampler);
+
+               std::string target = ob_name + "/" + get_transform_sid(fcu->rna_path, axis_name);
+               addChannel(COLLADABU::URI(empty, sampler_id), target);
+
+               closeAnimation();
+       }
+       
+       void add_bone_animation(FCurve *fcu, std::string ob_name, std::string bone_name)
+       {
+               const char *axis_names[] = {"X", "Y", "Z"};
+               const char *axis_name = NULL;
+               char c_anim_id[100]; // careful!
+
+               if (fcu->array_index < 3)
+                       axis_name = axis_names[fcu->array_index];
+               
+               std::string transform_sid = get_transform_sid(fcu->rna_path, axis_name);
+               
+               BLI_snprintf(c_anim_id, sizeof(c_anim_id), "%s.%s.%s", (char*)ob_name.c_str(), (char*)bone_name.c_str(), (char*)transform_sid.c_str());
+               std::string anim_id(c_anim_id);
+
+               // check rna_path is one of: rotation, scale, location
+
+               openAnimation(anim_id);
+
+               // create input source
+               std::string input_id = create_source(Sampler::INPUT, fcu, anim_id, axis_name);
+
+               // create output source
+               std::string output_id = create_source(Sampler::OUTPUT, fcu, anim_id, axis_name);
+
+               // create interpolations source
+               std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name);
+
+               std::string sampler_id = anim_id + SAMPLER_ID_SUFFIX;
+               COLLADASW::LibraryAnimations::Sampler sampler(sampler_id);
+               std::string empty;
+               sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
+               sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
+
+               // this input is required
+               sampler.addInput(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
+
+               addSampler(sampler);
+
+               std::string target = ob_name + "_" + bone_name + "/" + transform_sid;
+               addChannel(COLLADABU::URI(empty, sampler_id), target);
+
+               closeAnimation();
+       }
+       
+       FCurve *create_fcurve(int array_index, 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 create_bezt(FCurve *fcu, float frame, float output)
+       {
+               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, 0);
+               calchandles_fcurve(fcu);
+       }
+       
+       void change_quat_to_eul(Object *ob, bActionGroup *grp, char *grpname)
+       {
+               std::vector<FCurve*> &rot_fcurves = rotfcurves_actionGroup_map[grp];
+               
+               FCurve *quatcu[4] = {NULL, NULL, NULL, NULL};
+               int i;
+               
+               for (i = 0; i < rot_fcurves.size(); i++)
+                       quatcu[rot_fcurves[i]->array_index] = rot_fcurves[i];
+               
+               char *rna_path = rot_fcurves[0]->rna_path;
+               
+               FCurve *eulcu[3] = {
+                       create_fcurve(0, rna_path),
+                       create_fcurve(1, rna_path),
+                       create_fcurve(2, rna_path)
+               };
+               
+               for (i = 0; i < 4; i++) {
+                       
+                       FCurve *cu = quatcu[i];
+                       
+                       if (!cu) continue;
+                       
+                       for (int j = 0; j < cu->totvert; j++) {
+                               float frame = cu->bezt[j].vec[1][0];
+                               
+                               float quat[4] = {
+                                       quatcu[0] ? evaluate_fcurve(quatcu[0], frame) : 0.0f,
+                                       quatcu[1] ? evaluate_fcurve(quatcu[1], frame) : 0.0f,
+                                       quatcu[2] ? evaluate_fcurve(quatcu[2], frame) : 0.0f,
+                                       quatcu[3] ? evaluate_fcurve(quatcu[3], frame) : 0.0f
+                               };
+                               
+                               float eul[3];
+                               
+                               QuatToEul(quat, eul);
+                               
+                               for (int k = 0; k < 3; k++)
+                                       create_bezt(eulcu[k], frame, eul[k]);
+                       }
+               }
+               
+               for (i = 0; i < 3; i++) {
+                       add_bone_animation(eulcu[i], id_name(ob), std::string(grpname));
+                       free_fcurve(eulcu[i]);
+               }
+       }
+
+       // called for each exported object
+       void operator() (Object *ob) 
+       {
+               if (!ob->adt || !ob->adt->action) return;
+               
+               FCurve *fcu = (FCurve*)ob->adt->action->curves.first;
+               
+               if (ob->type == OB_ARMATURE) {
+                       
+                       while (fcu) {
+                               
+                               if (strstr(fcu->rna_path, ".rotation")) 
+                                       rotfcurves_actionGroup_map[fcu->grp].push_back(fcu);
+                               else fcurves_actionGroup_map[fcu->grp].push_back(fcu);
+                               
+                               fcu = fcu->next;
+                       }
+                       
+                       for (bPoseChannel *pchan = (bPoseChannel*)ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+                               int i;
+                               char *grpname = pchan->name;
+                               bActionGroup *grp = action_groups_find_named(ob->adt->action, grpname);
+                               
+                               if (!grp) continue;
+                               
+                               // write animation for location & scaling
+                               if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end()) continue;
+                               
+                               std::vector<FCurve*> &fcurves = fcurves_actionGroup_map[grp];
+                               for (i = 0; i < fcurves.size(); i++)
+                                       add_bone_animation(fcurves[i], id_name(ob), std::string(grpname));
+                               
+                               // ... for rotation
+                               if (rotfcurves_actionGroup_map.find(grp) == rotfcurves_actionGroup_map.end())
+                                       continue;
+                               
+                               // if rotation mode is euler - no need to convert it
+                               if (pchan->rotmode == ROT_MODE_EUL) {
+                                       
+                                       std::vector<FCurve*> &rotfcurves = rotfcurves_actionGroup_map[grp];
+                                       
+                                       for (i = 0; i < rotfcurves.size(); i++) 
+                                               add_bone_animation(rotfcurves[i], id_name(ob), std::string(grpname));
+                               }
+                               
+                               // convert rotation to euler & write animation
+                               else change_quat_to_eul(ob, grp, grpname);
+                       }
+               }
+               else {
+                       while (fcu) {
+                               
+                               if (!strcmp(fcu->rna_path, "location") ||
+                                       !strcmp(fcu->rna_path, "scale") ||
+                                       !strcmp(fcu->rna_path, "rotation")) {
+                                       
+                                       add_animation(fcu, id_name(ob));
+                               }
+                               
+                               fcu = fcu->next;
+                       }
+               }
+       }
+};
+
+void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename)
+{
+       COLLADABU::NativeString native_filename =
+               COLLADABU::NativeString(std::string(filename));
+       COLLADASW::StreamWriter sw(native_filename);
+
+       // open <Collada>
+       sw.startDocument();
+
+       // <asset>
+       COLLADASW::Asset asset(&sw);
+       // XXX ask blender devs about this?
+       asset.setUnit("decimetre", 0.1);
+       asset.setUpAxisType(COLLADASW::Asset::Z_UP);
+       asset.add();
+       
+       // <library_cameras>
+       CamerasExporter ce(&sw);
+       ce.exportCameras(sce);
+       
+       // <library_lights>
+       LightsExporter le(&sw);
+       le.exportLights(sce);
+
+       // <library_images>
+       ImagesExporter ie(&sw, filename);
+       ie.exportImages(sce);
+       
+       // <library_effects>
+       EffectsExporter ee(&sw);
+       ee.exportEffects(sce);
+       
+       // <library_materials>
+       MaterialsExporter me(&sw);
+       me.exportMaterials(sce);
+
+       // <library_geometries>
+       GeometryExporter ge(&sw);
+       ge.exportGeom(sce);
+
+       // <library_animations>
+       AnimationExporter ae(&sw);
+       ae.exportAnimations(sce);
+
+       // <library_controllers>
+       ArmatureExporter arm_exporter(&sw);
+       arm_exporter.export_controllers(sce);
+
+       // <library_visual_scenes>
+       SceneExporter se(&sw, &arm_exporter);
+       se.exportScene(sce);
+       
+       // <scene>
+       std::string scene_name(id_name(sce));
+       COLLADASW::Scene scene(&sw, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING,
+                                                                                          scene_name));
+       scene.add();
+       
+       // close <Collada>
+       sw.endDocument();
+
+}
+
+void DocumentExporter::exportScenes(const char* filename)
+{
+}
diff --git a/source/blender/collada/DocumentExporter.h b/source/blender/collada/DocumentExporter.h
new file mode 100644 (file)
index 0000000..1a0c292
--- /dev/null
@@ -0,0 +1,8 @@
+struct Scene;
+
+class DocumentExporter
+{
+ public:
+       void exportCurrentScene(Scene *sce, const char* filename);
+       void exportScenes(const char* filename);
+};
diff --git a/source/blender/collada/DocumentImporter.cpp b/source/blender/collada/DocumentImporter.cpp
new file mode 100644 (file)
index 0000000..f31ac7d
--- /dev/null
@@ -0,0 +1,2847 @@
+#include "COLLADAFWRoot.h"
+#include "COLLADAFWIWriter.h"
+#include "COLLADAFWStableHeaders.h"
+#include "COLLADAFWAnimationCurve.h"
+#include "COLLADAFWAnimationList.h"
+#include "COLLADAFWCamera.h"
+#include "COLLADAFWColorOrTexture.h"
+#include "COLLADAFWEffect.h"
+#include "COLLADAFWFloatOrDoubleArray.h"
+#include "COLLADAFWGeometry.h"
+#include "COLLADAFWImage.h"
+#include "COLLADAFWIndexList.h"
+#include "COLLADAFWInstanceGeometry.h"
+#include "COLLADAFWLight.h"
+#include "COLLADAFWMaterial.h"
+#include "COLLADAFWMesh.h"
+#include "COLLADAFWMeshPrimitiveWithFaceVertexCount.h"
+#include "COLLADAFWNode.h"
+#include "COLLADAFWPolygons.h"
+#include "COLLADAFWSampler.h"
+#include "COLLADAFWSkinController.h"
+#include "COLLADAFWSkinControllerData.h"
+#include "COLLADAFWTransformation.h"
+#include "COLLADAFWTranslate.h"
+#include "COLLADAFWRotate.h"
+#include "COLLADAFWScale.h"
+#include "COLLADAFWMatrix.h"
+#include "COLLADAFWTypes.h"
+#include "COLLADAFWVisualScene.h"
+#include "COLLADAFWFileInfo.h"
+#include "COLLADAFWArrayPrimitiveType.h"
+
+#include "COLLADASaxFWLLoader.h"
+
+// TODO move "extern C" into header files
+extern "C" 
+{
+#include "ED_keyframing.h"
+#include "ED_armature.h"
+#include "ED_mesh.h" // ED_vgroup_vert_add, ...
+#include "ED_anim_api.h"
+#include "WM_types.h"
+#include "WM_api.h"
+
+#include "BKE_main.h"
+#include "BKE_customdata.h"
+#include "BKE_library.h"
+#include "BKE_texture.h"
+#include "BKE_fcurve.h"
+#include "BKE_depsgraph.h"
+#include "BLI_util.h"
+#include "BKE_displist.h"
+#include "BLI_arithb.h"
+}
+#include "BKE_armature.h"
+#include "BKE_mesh.h"
+#include "BKE_global.h"
+#include "BKE_context.h"
+#include "BKE_object.h"
+#include "BKE_image.h"
+#include "BKE_material.h"
+#include "BKE_utildefines.h"
+#include "BKE_action.h"
+
+#include "BLI_arithb.h"
+#include "BLI_listbase.h"
+#include "BLI_string.h"
+
+#include "DNA_lamp_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_anim_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_material_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "DocumentImporter.h"
+#include "collada_internal.h"
+
+#include <string>
+#include <map>
+
+#include <math.h>
+#include <float.h>
+
+// #define COLLADA_DEBUG
+
+char *CustomData_get_layer_name(const struct CustomData *data, int type, int n);
+
+// armature module internal func, it's not good to use it here? (Arystan)
+struct EditBone *addEditBone(struct bArmature *arm, char *name);
+
+const char *primTypeToStr(COLLADAFW::MeshPrimitive::PrimitiveType type)
+{
+       using namespace COLLADAFW;
+       
+       switch (type) {
+       case MeshPrimitive::LINES:
+               return "LINES";
+       case MeshPrimitive::LINE_STRIPS:
+               return "LINESTRIPS";
+       case MeshPrimitive::POLYGONS:
+               return "POLYGONS";
+       case MeshPrimitive::POLYLIST:
+               return "POLYLIST";
+       case MeshPrimitive::TRIANGLES:
+               return "TRIANGLES";
+       case MeshPrimitive::TRIANGLE_FANS:
+               return "TRIANGLE_FANS";
+       case MeshPrimitive::TRIANGLE_STRIPS:
+               return "TRIANGLE_FANS";
+       case MeshPrimitive::POINTS:
+               return "POINTS";
+       case MeshPrimitive::UNDEFINED_PRIMITIVE_TYPE:
+               return "UNDEFINED_PRIMITIVE_TYPE";
+       }
+       return "UNKNOWN";
+}
+const char *geomTypeToStr(COLLADAFW::Geometry::GeometryType type)
+{
+       switch (type) {
+       case COLLADAFW::Geometry::GEO_TYPE_MESH:
+               return "MESH";
+       case COLLADAFW::Geometry::GEO_TYPE_SPLINE:
+               return "SPLINE";
+       case COLLADAFW::Geometry::GEO_TYPE_CONVEX_MESH:
+               return "CONVEX_MESH";
+       }
+       return "UNKNOWN";
+}
+
+// works for COLLADAFW::Node, COLLADAFW::Geometry
+template<class T>
+const char *get_dae_name(T *node)
+{
+       const std::string& name = node->getName();
+       return name.size() ? name.c_str() : node->getOriginalId().c_str();
+}
+
+// use this for retrieving bone names, since these must be unique
+template<class T>
+const char *get_joint_name(T *node)
+{
+       const std::string& id = node->getOriginalId();
+       return id.size() ? id.c_str() : node->getName().c_str();
+}
+
+float get_float_value(const COLLADAFW::FloatOrDoubleArray& array, int index)
+{
+       if (index >= array.getValuesCount())
+               return 0.0f;
+
+       if (array.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT)
+               return array.getFloatValues()->getData()[index];
+       else 
+               return array.getDoubleValues()->getData()[index];
+}
+
+typedef std::map<COLLADAFW::TextureMapId, std::vector<MTex*> > TexIndexTextureArrayMap;
+
+class TransformReader : public TransformBase
+{
+protected:
+
+       UnitConverter *unit_converter;
+
+       struct Animation {
+               Object *ob;
+               COLLADAFW::Node *node;
+               COLLADAFW::Transformation *tm; // which transform is animated by an AnimationList->id
+       };
+
+public:
+
+       TransformReader(UnitConverter* conv) : unit_converter(conv) {}
+
+       void get_node_mat(float mat[][4], COLLADAFW::Node *node, std::map<COLLADAFW::UniqueId, Animation> *animation_map,
+                                         Object *ob)
+       {
+               float cur[4][4];
+               float copy[4][4];
+
+               Mat4One(mat);
+               
+               for (int i = 0; i < node->getTransformations().getCount(); i++) {
+
+                       COLLADAFW::Transformation *tm = node->getTransformations()[i];
+                       COLLADAFW::Transformation::TransformationType type = tm->getTransformationType();
+
+                       switch(type) {
+                       case COLLADAFW::Transformation::TRANSLATE:
+                               {
+                                       COLLADAFW::Translate *tra = (COLLADAFW::Translate*)tm;
+                                       COLLADABU::Math::Vector3& t = tra->getTranslation();
+
+                                       Mat4One(cur);
+                                       cur[3][0] = (float)t[0];
+                                       cur[3][1] = (float)t[1];
+                                       cur[3][2] = (float)t[2];
+                               }
+                               break;
+                       case COLLADAFW::Transformation::ROTATE:
+                               {
+                                       COLLADAFW::Rotate *ro = (COLLADAFW::Rotate*)tm;
+                                       COLLADABU::Math::Vector3& raxis = ro->getRotationAxis();
+                                       float angle = (float)(ro->getRotationAngle() * M_PI / 180.0f);
+                                       float axis[] = {raxis[0], raxis[1], raxis[2]};
+                                       float quat[4];
+                                       float rot_copy[3][3];
+                                       float mat[3][3];
+                                       AxisAngleToQuat(quat, axis, angle);
+                                       
+                                       QuatToMat4(quat, cur);
+                               }
+                               break;
+                       case COLLADAFW::Transformation::SCALE:
+                               {
+                                       COLLADABU::Math::Vector3& s = ((COLLADAFW::Scale*)tm)->getScale();
+                                       float size[3] = {(float)s[0], (float)s[1], (float)s[2]};
+                                       SizeToMat4(size, cur);
+                               }
+                               break;
+                       case COLLADAFW::Transformation::MATRIX:
+                               {
+                                       unit_converter->mat4_from_dae(cur, ((COLLADAFW::Matrix*)tm)->getMatrix());
+                               }
+                               break;
+                       case COLLADAFW::Transformation::LOOKAT:
+                       case COLLADAFW::Transformation::SKEW:
+                               fprintf(stderr, "LOOKAT and SKEW transformations are not supported yet.\n");
+                               break;
+                       }
+
+                       Mat4CpyMat4(copy, mat);
+                       Mat4MulMat4(mat, cur, copy);
+
+                       if (animation_map) {
+                               // AnimationList that drives this Transformation
+                               const COLLADAFW::UniqueId& anim_list_id = tm->getAnimationList();
+                       
+                               // store this so later we can link animation data with ob
+                               Animation anim = {ob, node, tm};
+                               (*animation_map)[anim_list_id] = anim;
+                       }
+               }
+       }
+};
+
+// only for ArmatureImporter to "see" MeshImporter::get_object_by_geom_uid
+class MeshImporterBase
+{
+public:
+       virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid) = 0;
+};
+
+// ditto as above
+class AnimationImporterBase
+{
+public:
+       virtual void change_eul_to_quat(Object *ob, bAction *act) = 0;
+};
+
+class ArmatureImporter : private TransformReader
+{
+private:
+       Scene *scene;
+       UnitConverter *unit_converter;
+
+       // std::map<int, JointData> joint_index_to_joint_info_map;
+       // std::map<COLLADAFW::UniqueId, int> joint_id_to_joint_index_map;
+
+       struct LeafBone {
+               // COLLADAFW::Node *node;
+               EditBone *bone;
+               char name[32];
+               float mat[4][4]; // bone matrix, derived from inv_bind_mat
+       };
+       std::vector<LeafBone> leaf_bones;
+       // int bone_direction_row; // XXX not used
+       float leaf_bone_length;
+       int totbone;
+       // XXX not used
+       // float min_angle; // minimum angle between bone head-tail and a row of bone matrix
+
+#if 0
+       struct ArmatureJoints {
+               Object *ob_arm;
+               std::vector<COLLADAFW::Node*> root_joints;
+       };
+       std::vector<ArmatureJoints> armature_joints;
+#endif
+
+       Object *empty; // empty for leaf bones
+
+       std::map<COLLADAFW::UniqueId, COLLADAFW::UniqueId> geom_uid_by_controller_uid;
+       std::map<COLLADAFW::UniqueId, COLLADAFW::Node*> joint_by_uid; // contains all joints
+       std::vector<COLLADAFW::Node*> root_joints;
+
+       std::vector<Object*> armature_objects;
+
+       MeshImporterBase *mesh_importer;
+       AnimationImporterBase *anim_importer;
+
+       // This is used to store data passed in write_controller_data.
+       // Arrays from COLLADAFW::SkinControllerData lose ownership, so do this class members
+       // so that arrays don't get freed until we free them explicitly.
+       class SkinInfo
+       {
+       private:
+               // to build armature bones from inverse bind matrices
+               struct JointData {
+                       float inv_bind_mat[4][4]; // joint inverse bind matrix
+                       COLLADAFW::UniqueId joint_uid; // joint node UID
+                       // Object *ob_arm;                        // armature object
+               };
+
+               float bind_shape_matrix[4][4];
+
+               // data from COLLADAFW::SkinControllerData, each array should be freed
+               COLLADAFW::UIntValuesArray joints_per_vertex;
+               COLLADAFW::UIntValuesArray weight_indices;
+               COLLADAFW::IntValuesArray joint_indices;
+               // COLLADAFW::FloatOrDoubleArray weights;
+               std::vector<float> weights;
+
+               std::vector<JointData> joint_data; // index to this vector is joint index
+
+               UnitConverter *unit_converter;
+
+               Object *ob_arm;
+               COLLADAFW::UniqueId controller_uid;
+
+       public:
+
+               SkinInfo() {}
+
+               SkinInfo(const SkinInfo& skin) : weights(skin.weights),
+                                                                                joint_data(skin.joint_data),
+                                                                                unit_converter(skin.unit_converter),
+                                                                                ob_arm(skin.ob_arm),
+                                                                                controller_uid(skin.controller_uid)
+               {
+                       Mat4CpyMat4(bind_shape_matrix, (float (*)[4])skin.bind_shape_matrix);
+
+                       transfer_uint_array_data_const(skin.joints_per_vertex, joints_per_vertex);
+                       transfer_uint_array_data_const(skin.weight_indices, weight_indices);
+                       transfer_int_array_data_const(skin.joint_indices, joint_indices);
+               }
+
+               SkinInfo(UnitConverter *conv) : unit_converter(conv), ob_arm(NULL) {}
+
+               // nobody owns the data after this, so it should be freed manually with releaseMemory
+               template <class T>
+               void transfer_array_data(T& src, T& dest)
+               {
+                       dest.setData(src.getData(), src.getCount());
+                       src.yieldOwnerShip();
+                       dest.yieldOwnerShip();
+               }
+
+               // when src is const we cannot src.yieldOwnerShip, this is used by copy constructor
+               void transfer_int_array_data_const(const COLLADAFW::IntValuesArray& src, COLLADAFW::IntValuesArray& dest)
+               {
+                       dest.setData((int*)src.getData(), src.getCount());
+                       dest.yieldOwnerShip();
+               }
+
+               void transfer_uint_array_data_const(const COLLADAFW::UIntValuesArray& src, COLLADAFW::UIntValuesArray& dest)
+               {
+                       dest.setData((unsigned int*)src.getData(), src.getCount());
+                       dest.yieldOwnerShip();
+               }
+
+               void borrow_skin_controller_data(const COLLADAFW::SkinControllerData* skin)
+               {
+                       transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getJointsPerVertex(), joints_per_vertex);
+                       transfer_array_data((COLLADAFW::UIntValuesArray&)skin->getWeightIndices(), weight_indices);
+                       transfer_array_data((COLLADAFW::IntValuesArray&)skin->getJointIndices(), joint_indices);
+                       // transfer_array_data(skin->getWeights(), weights);
+
+                       // cannot transfer data for FloatOrDoubleArray, copy values manually
+                       const COLLADAFW::FloatOrDoubleArray& weight = skin->getWeights();
+                       for (int i = 0; i < weight.getValuesCount(); i++)
+                               weights.push_back(get_float_value(weight, i));
+
+                       unit_converter->mat4_from_dae(bind_shape_matrix, skin->getBindShapeMatrix());
+               }
+                       
+               void free()
+               {
+                       joints_per_vertex.releaseMemory();
+                       weight_indices.releaseMemory();
+                       joint_indices.releaseMemory();
+                       // weights.releaseMemory();
+               }
+
+               // using inverse bind matrices to construct armature
+               // it is safe to invert them to get the original matrices
+               // because if they are inverse matrices, they can be inverted
+               void add_joint(const COLLADABU::Math::Matrix4& matrix)
+               {
+                       JointData jd;
+                       unit_converter->mat4_from_dae(jd.inv_bind_mat, matrix);
+                       joint_data.push_back(jd);
+               }
+
+               // called from write_controller
+               Object *create_armature(const COLLADAFW::SkinController* co, Scene *scene)
+               {
+                       ob_arm = add_object(scene, OB_ARMATURE);
+
+                       controller_uid = co->getUniqueId();
+
+                       const COLLADAFW::UniqueIdArray& joint_uids = co->getJoints();
+                       for (int i = 0; i < joint_uids.getCount(); i++) {
+                               joint_data[i].joint_uid = joint_uids[i];
+
+                               // // store armature pointer
+                               // JointData& jd = joint_index_to_joint_info_map[i];
+                               // jd.ob_arm = ob_arm;
+
+                               // now we'll be able to get inv bind matrix from joint id
+                               // joint_id_to_joint_index_map[joint_ids[i]] = i;
+                       }
+
+                       return ob_arm;
+               }
+
+               bool get_joint_inv_bind_matrix(float inv_bind_mat[][4], COLLADAFW::Node *node)
+               {
+                       const COLLADAFW::UniqueId& uid = node->getUniqueId();
+                       std::vector<JointData>::iterator it;
+                       for (it = joint_data.begin(); it != joint_data.end(); it++) {
+                               if ((*it).joint_uid == uid) {
+                                       Mat4CpyMat4(inv_bind_mat, (*it).inv_bind_mat);
+                                       return true;
+                               }
+                       }
+
+                       return false;
+               }
+
+               Object *get_armature()
+               {
+                       return ob_arm;
+               }
+
+               const COLLADAFW::UniqueId& get_controller_uid()
+               {
+                       return controller_uid;
+               }
+
+               // some nodes may not be referenced by SkinController,
+               // in this case to determine if the node belongs to this armature,
+               // we need to search down the tree
+               bool uses_joint(COLLADAFW::Node *node)
+               {
+                       const COLLADAFW::UniqueId& uid = node->getUniqueId();
+                       std::vector<JointData>::iterator it;
+                       for (it = joint_data.begin(); it != joint_data.end(); it++) {
+                               if ((*it).joint_uid == uid)
+                                       return true;
+                       }
+
+                       COLLADAFW::NodePointerArray& children = node->getChildNodes();
+                       for (int i = 0; i < children.getCount(); i++) {
+                               if (this->uses_joint(children[i]))
+                                       return true;
+                       }
+
+                       return false;
+               }
+
+               void link_armature(bContext *C, Object *ob, std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>& joint_by_uid,
+                                                  TransformReader *tm)
+               {
+                       tm->decompose(bind_shape_matrix, ob->loc, ob->rot, ob->size);
+
+                       ob->parent = ob_arm;
+                       ob->partype = PARSKEL;
+                       ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA;
+
+                       ((bArmature*)ob_arm->data)->deformflag = ARM_DEF_VGROUP;
+
+                       // we need armature matrix here... where do we get it from I wonder...
+                       // root node/joint? or node with <instance_controller>?
+                       float parmat[4][4];
+                       Mat4One(parmat);
+                       Mat4Invert(ob->parentinv, parmat);
+
+                       // create all vertex groups
+                       std::vector<JointData>::iterator it;
+                       int joint_index;
+                       for (it = joint_data.begin(), joint_index = 0; it != joint_data.end(); it++, joint_index++) {
+                               const char *name = "Group";
+
+                               // name group by joint node name
+                               if (joint_by_uid.find((*it).joint_uid) != joint_by_uid.end()) {
+                                       name = get_joint_name(joint_by_uid[(*it).joint_uid]);
+                               }
+
+                               ED_vgroup_add_name(ob, (char*)name);
+                       }
+
+                       // <vcount> - number of joints per vertex - joints_per_vertex
+                       // <v> - [[bone index, weight index] * joints per vertex] * vertices - weight indices
+                       // ^ bone index can be -1 meaning weight toward bind shape, how to express this in Blender?
+
+                       // for each vertex in weight indices
+                       //   for each bone index in vertex
+                       //     add vertex to group at group index
+                       //     treat group index -1 specially
+
+                       // get def group by index with BLI_findlink
+
+                       for (int vertex = 0, weight = 0; vertex < joints_per_vertex.getCount(); vertex++) {
+
+                               int limit = weight + joints_per_vertex[vertex];
+                               for ( ; weight < limit; weight++) {
+                                       int joint = joint_indices[weight], joint_weight = weight_indices[weight];
+
+                                       // -1 means "weight towards the bind shape", we just don't assign it to any group
+                                       if (joint != -1) {
+                                               bDeformGroup *def = (bDeformGroup*)BLI_findlink(&ob->defbase, joint);
+
+                                               ED_vgroup_vert_add(ob, def, vertex, weights[joint_weight], WEIGHT_REPLACE);
+                                       }
+                               }
+                       }
+
+                       DAG_scene_sort(CTX_data_scene(C));
+                       ED_anim_dag_flush_update(C);
+                       WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, NULL);
+               }
+
+               bPoseChannel *get_pose_channel_from_node(COLLADAFW::Node *node)
+               {
+                       return get_pose_channel(ob_arm->pose, get_joint_name(node));
+               }
+       };
+
+       std::map<COLLADAFW::UniqueId, SkinInfo> skin_by_data_uid; // data UID = skin controller data UID
+#if 0
+       JointData *get_joint_data(COLLADAFW::Node *node)
+       {
+               const COLLADAFW::UniqueId& joint_id = node->getUniqueId();
+
+               if (joint_id_to_joint_index_map.find(joint_id) == joint_id_to_joint_index_map.end()) {
+                       fprintf(stderr, "Cannot find a joint index by joint id for %s.\n",
+                                       node->getOriginalId().c_str());
+                       return NULL;
+               }
+
+               int joint_index = joint_id_to_joint_index_map[joint_id];
+
+               return &joint_index_to_joint_info_map[joint_index];
+       }
+#endif
+
+       void create_bone(SkinInfo& skin, COLLADAFW::Node *node, EditBone *parent, int totchild,
+                                        float parent_mat[][4], bArmature *arm)
+       {
+               float joint_inv_bind_mat[4][4];
+
+               // JointData* jd = get_joint_data(node);
+
+               float mat[4][4];
+
+               if (skin.get_joint_inv_bind_matrix(joint_inv_bind_mat, node)) {
+                       // get original world-space matrix
+                       Mat4Invert(mat, joint_inv_bind_mat);
+               }
+               // create a bone even if there's no joint data for it (i.e. it has no influence)
+               else {
+                       float obmat[4][4];
+
+                       // object-space
+                       get_node_mat(obmat, node, NULL, NULL);
+
+                       // get world-space
+                       if (parent)
+                               Mat4MulMat4(mat, obmat, parent_mat);
+                       else
+                               Mat4CpyMat4(mat, obmat);
+               }
+
+               // TODO rename from Node "name" attrs later
+               EditBone *bone = addEditBone(arm, (char*)get_joint_name(node));
+               totbone++;
+
+               if (parent) bone->parent = parent;
+
+               // set head
+               VecCopyf(bone->head, mat[3]);
+
+               // set tail, don't set it to head because 0-length bones are not allowed
+               float vec[3] = {0.0f, 0.5f, 0.0f};
+               VecAddf(bone->tail, bone->head, vec);
+
+               // set parent tail
+               if (parent && totchild == 1) {
+                       VecCopyf(parent->tail, bone->head);
+
+                       // XXX increase this to prevent "very" small bones?
+                       const float epsilon = 0.000001f;
+
+                       // derive leaf bone length
+                       float length = VecLenf(parent->head, parent->tail);
+                       if ((length < leaf_bone_length || totbone == 0) && length > epsilon) {
+                               leaf_bone_length = length;
+                       }
+
+                       // treat zero-sized bone like a leaf bone
+                       if (length <= epsilon) {
+                               add_leaf_bone(parent_mat, parent);
+                       }
+
+                       /*
+#if 0
+                       // and which row in mat is bone direction
+                       float vec[3];
+                       VecSubf(vec, parent->tail, parent->head);
+#ifdef COLLADA_DEBUG
+                       printvecf("tail - head", vec);
+                       printmatrix4("matrix", parent_mat);
+#endif
+                       for (int i = 0; i < 3; i++) {
+#ifdef COLLADA_DEBUG
+                               char *axis_names[] = {"X", "Y", "Z"};
+                               printf("%s-axis length is %f\n", axis_names[i], VecLength(parent_mat[i]));
+#endif
+                               float angle = VecAngle2(vec, parent_mat[i]);
+                               if (angle < min_angle) {
+#ifdef COLLADA_DEBUG
+                                       printvecf("picking", parent_mat[i]);
+                                       printf("^ %s axis of %s's matrix\n", axis_names[i], get_dae_name(node));
+#endif
+                                       bone_direction_row = i;
+                                       min_angle = angle;
+                               }
+                       }
+#endif
+                       */
+               }
+
+               COLLADAFW::NodePointerArray& children = node->getChildNodes();
+               for (int i = 0; i < children.getCount(); i++) {
+                       create_bone(skin, children[i], bone, children.getCount(), mat, arm);
+               }
+
+               // in second case it's not a leaf bone, but we handle it the same way
+               if (!children.getCount() || children.getCount() > 1) {
+                       add_leaf_bone(mat, bone);
+               }
+       }
+
+       void add_leaf_bone(float mat[][4], EditBone *bone)
+       {
+               LeafBone leaf;
+
+               leaf.bone = bone;
+               Mat4CpyMat4(leaf.mat, mat);
+               BLI_strncpy(leaf.name, bone->name, sizeof(leaf.name));
+
+               leaf_bones.push_back(leaf);
+       }
+
+       void fix_leaf_bones()
+       {
+               // just setting tail for leaf bones here
+
+               std::vector<LeafBone>::iterator it;
+               for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) {
+                       LeafBone& leaf = *it;
+
+                       // pointing up
+                       float vec[3] = {0.0f, 0.0f, 1.0f};
+
+                       VecMulf(vec, leaf_bone_length);
+
+                       VecCopyf(leaf.bone->tail, leaf.bone->head);
+                       VecAddf(leaf.bone->tail, leaf.bone->head, vec);
+               }
+       }
+
+       void set_leaf_bone_shapes(Object *ob_arm)
+       {
+               bPose *pose = ob_arm->pose;
+
+               std::vector<LeafBone>::iterator it;
+               for (it = leaf_bones.begin(); it != leaf_bones.end(); it++) {
+                       LeafBone& leaf = *it;
+
+                       bPoseChannel *pchan = get_pose_channel(pose, leaf.name);
+                       if (pchan) {
+                               pchan->custom = get_empty_for_leaves();
+                       }
+                       else {
+                               fprintf(stderr, "Cannot find a pose channel for leaf bone %s\n", leaf.name);
+                       }
+               }
+       }
+
+       void set_euler_rotmode()
+       {
+               // just set rotmode = ROT_MODE_EUL on pose channel for each joint
+
+               std::map<COLLADAFW::UniqueId, COLLADAFW::Node*>::iterator it;
+
+               for (it = joint_by_uid.begin(); it != joint_by_uid.end(); it++) {
+
+                       COLLADAFW::Node *joint = it->second;
+
+                       std::map<COLLADAFW::UniqueId, SkinInfo>::iterator sit;
+                       
+                       for (sit = skin_by_data_uid.begin(); sit != skin_by_data_uid.end(); sit++) {
+                               SkinInfo& skin = sit->second;
+
+                               if (skin.uses_joint(joint)) {
+                                       bPoseChannel *pchan = skin.get_pose_channel_from_node(joint);
+
+                                       if (pchan) {
+                                               pchan->rotmode = ROT_MODE_EUL;
+                                       }
+                                       else {
+                                               fprintf(stderr, "Cannot find pose channel for %s.\n", get_joint_name(joint));
+                                       }
+
+                                       break;
+                               }
+                       }
+               }
+       }
+
+       Object *get_empty_for_leaves()
+       {
+               if (empty) return empty;
+               
+               empty = add_object(scene, OB_EMPTY);
+               empty->empty_drawtype = OB_EMPTY_SPHERE;
+
+               return empty;
+       }
+
+#if 0
+       Object *find_armature(COLLADAFW::Node *node)
+       {
+               JointData* jd = get_joint_data(node);
+               if (jd) return jd->ob_arm;
+
+               COLLADAFW::NodePointerArray& children = node->getChildNodes();
+               for (int i = 0; i < children.getCount(); i++) {
+                       Object *ob_arm = find_armature(children[i]);
+                       if (ob_arm) return ob_arm;
+               }
+
+               return NULL;
+       }
+
+       ArmatureJoints& get_armature_joints(Object *ob_arm)
+       {
+               // try finding it
+               std::vector<ArmatureJoints>::iterator it;
+               for (it = armature_joints.begin(); it != armature_joints.end(); it++) {
+                       if ((*it).ob_arm == ob_arm) return *it;
+               }
+
+               // not found, create one
+               ArmatureJoints aj;
+               aj.ob_arm = ob_arm;
+               armature_joints.push_back(aj);
+
+               return armature_joints.back();
+       }
+#endif
+
+       void create_armature_bones(SkinInfo& skin)
+       {
+               // just do like so:
+               // - get armature
+               // - enter editmode
+               // - add edit bones and head/tail properties using matrices and parent-child info
+               // - exit edit mode
+               // - set a sphere shape to leaf bones
+
+               Object *ob_arm = skin.get_armature();
+
+               // enter armature edit mode
+               ED_armature_to_edit(ob_arm);
+
+               leaf_bones.clear();
+               totbone = 0;
+               // bone_direction_row = 1; // TODO: don't default to Y but use asset and based on it decide on default row
+               leaf_bone_length = 0.1f;
+               // min_angle = 360.0f;          // minimum angle between bone head-tail and a row of bone matrix
+
+               // create bones
+
+               std::vector<COLLADAFW::Node*>::iterator it;
+               for (it = root_joints.begin(); it != root_joints.end(); it++) {
+                       // since root_joints may contain joints for multiple controllers, we need to filter
+                       if (skin.uses_joint(*it)) {
+                               create_bone(skin, *it, NULL, (*it)->getChildNodes().getCount(), NULL, (bArmature*)ob_arm->data);
+                       }
+               }
+
+               fix_leaf_bones();
+
+               // exit armature edit mode
+               ED_armature_from_edit(ob_arm);
+               ED_armature_edit_free(ob_arm);
+               DAG_id_flush_update(&ob_arm->id, OB_RECALC_OB|OB_RECALC_DATA);
+
+               set_leaf_bone_shapes(ob_arm);
+
+               set_euler_rotmode();
+       }
+       
+
+public:
+
+       ArmatureImporter(UnitConverter *conv, MeshImporterBase *mesh, AnimationImporterBase *anim, Scene *sce) :
+               TransformReader(conv), scene(sce), empty(NULL), mesh_importer(mesh), anim_importer(anim) {}
+
+       ~ArmatureImporter()
+       {
+               // free skin controller data if we forget to do this earlier
+               std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it;
+               for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) {
+                       it->second.free();
+               }
+       }
+
+       // root - if this joint is the top joint in hierarchy, if a joint
+       // is a child of a node (not joint), root should be true since
+       // this is where we build armature bones from
+       void add_joint(COLLADAFW::Node *node, bool root)
+       {
+               joint_by_uid[node->getUniqueId()] = node;
+               if (root) root_joints.push_back(node);
+       }
+
+#if 0
+       void add_root_joint(COLLADAFW::Node *node)
+       {
+               // root_joints.push_back(node);
+               Object *ob_arm = find_armature(node);
+               if (ob_arm)     {
+                       get_armature_joints(ob_arm).root_joints.push_back(node);
+               }
+#ifdef COLLADA_DEBUG
+               else {
+                       fprintf(stderr, "%s cannot be added to armature.\n", get_joint_name(node));
+               }
+#endif
+       }
+#endif
+
+       // here we add bones to armatures, having armatures previously created in write_controller
+       void make_armatures(bContext *C)
+       {
+               std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it;
+               for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) {
+
+                       SkinInfo& skin = it->second;
+
+                       create_armature_bones(skin);
+
+                       // link armature with an object
+                       Object *ob = mesh_importer->get_object_by_geom_uid(*get_geometry_uid(skin.get_controller_uid()));
+                       if (ob) {
+                               skin.link_armature(C, ob, joint_by_uid, this);
+                       }
+                       else {
+                               fprintf(stderr, "Cannot find object to link armature with.\n");
+                       }
+
+                       // free memory stolen from SkinControllerData
+                       skin.free();
+               }
+       }
+
+#if 0
+       // link with meshes, create vertex groups, assign weights
+       void link_armature(Object *ob_arm, const COLLADAFW::UniqueId& geom_id, const COLLADAFW::UniqueId& controller_data_id)
+       {
+               Object *ob = mesh_importer->get_object_by_geom_uid(geom_id);
+
+               if (!ob) {
+                       fprintf(stderr, "Cannot find object by geometry UID.\n");
+                       return;
+               }
+
+               if (skin_by_data_uid.find(controller_data_id) == skin_by_data_uid.end()) {
+                       fprintf(stderr, "Cannot find skin info by controller data UID.\n");
+                       return;
+               }
+
+               SkinInfo& skin = skin_by_data_uid[conroller_data_id];
+
+               // create vertex groups
+       }
+#endif
+
+       bool write_skin_controller_data(const COLLADAFW::SkinControllerData* data)
+       {
+               // at this stage we get vertex influence info that should go into me->verts and ob->defbase
+               // there's no info to which object this should be long so we associate it with skin controller data UID
+
+               // don't forget to call unique_vertexgroup_name before we copy
+
+               // controller data uid -> [armature] -> joint data, 
+               // [mesh object]
+               // 
+
+               SkinInfo skin(unit_converter);
+               skin.borrow_skin_controller_data(data);
+
+               // store join inv bind matrix to use it later in armature construction
+               const COLLADAFW::Matrix4Array& inv_bind_mats = data->getInverseBindMatrices();
+               for (int i = 0; i < data->getJointsCount(); i++) {
+                       skin.add_joint(inv_bind_mats[i]);
+               }
+
+               skin_by_data_uid[data->getUniqueId()] = skin;
+
+               return true;
+       }
+
+       bool write_controller(const COLLADAFW::Controller* controller)
+       {
+               // - create and store armature object
+
+               const COLLADAFW::UniqueId& skin_id = controller->getUniqueId();
+
+               if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_SKIN) {
+
+                       COLLADAFW::SkinController *co = (COLLADAFW::SkinController*)controller;
+
+                       // to find geom id by controller id
+                       geom_uid_by_controller_uid[skin_id] = co->getSource();
+
+                       const COLLADAFW::UniqueId& data_uid = co->getSkinControllerData();
+                       if (skin_by_data_uid.find(data_uid) == skin_by_data_uid.end()) {
+                               fprintf(stderr, "Cannot find skin by controller data UID.\n");
+                               return true;
+                       }
+
+                       Object *ob_arm = skin_by_data_uid[data_uid].create_armature(co, scene);
+
+                       armature_objects.push_back(ob_arm);
+               }
+               // morph controller
+               else {
+                       // shape keys? :)
+                       fprintf(stderr, "Morph controller is not supported yet.\n");
+               }
+
+               return true;
+       }
+
+       COLLADAFW::UniqueId *get_geometry_uid(const COLLADAFW::UniqueId& controller_uid)
+       {
+               if (geom_uid_by_controller_uid.find(controller_uid) == geom_uid_by_controller_uid.end())
+                       return NULL;
+
+               return &geom_uid_by_controller_uid[controller_uid];
+       }
+
+       Object *get_armature_for_joint(COLLADAFW::Node *node)
+       {
+               std::map<COLLADAFW::UniqueId, SkinInfo>::iterator it;
+               for (it = skin_by_data_uid.begin(); it != skin_by_data_uid.end(); it++) {
+                       SkinInfo& skin = it->second;
+
+                       if (skin.uses_joint(node))
+                               return skin.get_armature();
+               }
+
+               return NULL;
+       }
+
+       void get_rna_path_for_joint(COLLADAFW::Node *node, char *joint_path, size_t count)
+       {
+               BLI_snprintf(joint_path, count, "pose.pose_channels[\"%s\"]", get_joint_name(node));
+       }
+       
+       void fix_animation()
+       {
+               /* Change Euler rotation to Quaternion for bone animation */
+               std::vector<Object*>::iterator it;
+               for (it = armature_objects.begin(); it != armature_objects.end(); it++) {
+                       Object *ob = *it;
+                       if (!ob || !ob->adt || !ob->adt->action) continue;
+                       anim_importer->change_eul_to_quat(ob, ob->adt->action);
+               }
+       }
+};
+
+class MeshImporter : public MeshImporterBase
+{
+private:
+
+       Scene *scene;
+       ArmatureImporter *armature_importer;
+
+       std::map<COLLADAFW::UniqueId, Mesh*> uid_mesh_map; // geometry unique id-to-mesh map
+       std::map<COLLADAFW::UniqueId, Object*> uid_object_map; // geom uid-to-object
+       // this structure is used to assign material indices to faces
+       // it holds a portion of Mesh faces and corresponds to a DAE primitive list (<triangles>, <polylist>, etc.)
+       struct Primitive {
+               MFace *mface;
+               unsigned int totface;
+       };
+       typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive> > MaterialIdPrimitiveArrayMap;
+       std::map<COLLADAFW::UniqueId, MaterialIdPrimitiveArrayMap> geom_uid_mat_mapping_map; // crazy name!
+       
+       class UVDataWrapper
+       {
+               COLLADAFW::MeshVertexData *mVData;
+       public:
+               UVDataWrapper(COLLADAFW::MeshVertexData& vdata) : mVData(&vdata)
+               {}
+
+#ifdef COLLADA_DEBUG
+               void print()
+               {
+                       fprintf(stderr, "UVs:\n");
+                       switch(mVData->getType()) {
+                       case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT:
+                               {
+                                       COLLADAFW::ArrayPrimitiveType<float>* values = mVData->getFloatValues();
+                                       if (values->getCount()) {
+                                               for (int i = 0; i < values->getCount(); i += 2) {
+                                                       fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i+1]);
+                                               }
+                                       }
+                               }
+                               break;
+                       case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE:
+                               {
+                                       COLLADAFW::ArrayPrimitiveType<double>* values = mVData->getDoubleValues();
+                                       if (values->getCount()) {
+                                               for (int i = 0; i < values->getCount(); i += 2) {
+                                                       fprintf(stderr, "%.1f, %.1f\n", (float)(*values)[i], (float)(*values)[i+1]);
+                                               }
+                                       }
+                               }
+                               break;
+                       }
+                       fprintf(stderr, "\n");
+               }
+#endif
+
+               void getUV(int uv_set_index, int uv_index[2], float *uv)
+               {
+                       switch(mVData->getType()) {
+                       case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT:
+                               {
+                                       COLLADAFW::ArrayPrimitiveType<float>* values = mVData->getFloatValues();
+                                       if (values->empty()) return;
+                                       uv[0] = (*values)[uv_index[0]];
+                                       uv[1] = (*values)[uv_index[1]];
+                                       
+                               }
+                               break;
+                       case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE:
+                               {
+                                       COLLADAFW::ArrayPrimitiveType<double>* values = mVData->getDoubleValues();
+                                       if (values->empty()) return;
+                                       uv[0] = (float)(*values)[uv_index[0]];
+                                       uv[1] = (float)(*values)[uv_index[1]];
+                                       
+                               }
+                               break;
+                       }
+               }
+       };
+
+       void set_face_indices(MFace *mface, unsigned int *indices, bool quad)
+       {
+               mface->v1 = indices[0];
+               mface->v2 = indices[1];
+               mface->v3 = indices[2];
+               if (quad) mface->v4 = indices[3];
+               else mface->v4 = 0;
+#ifdef COLLADA_DEBUG
+               // fprintf(stderr, "%u, %u, %u \n", indices[0], indices[1], indices[2]);
+#endif
+       }
+
+       // change face indices order so that v4 is not 0
+       void rotate_face_indices(MFace *mface) {
+               mface->v4 = mface->v1;
+               mface->v1 = mface->v2;
+               mface->v2 = mface->v3;
+               mface->v3 = 0;
+       }
+       
+       void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, int uv_set_index,
+                                        COLLADAFW::IndexList& index_list, unsigned int *tris_indices)
+       {
+               int uv_indices[4][2];
+
+               // per face vertex indices, this means for quad we have 4 indices, not 8
+               COLLADAFW::UIntValuesArray& indices = index_list.getIndices();
+
+               // make indices into FloatOrDoubleArray
+               for (int i = 0; i < 3; i++) {
+                       int uv_index = indices[tris_indices[i]];
+                       uv_indices[i][0] = uv_index * 2;
+                       uv_indices[i][1] = uv_index * 2 + 1;
+               }
+
+               uvs.getUV(uv_set_index, uv_indices[0], mtface->uv[0]);
+               uvs.getUV(uv_set_index, uv_indices[1], mtface->uv[1]);
+               uvs.getUV(uv_set_index, uv_indices[2], mtface->uv[2]);
+       }
+
+       void set_face_uv(MTFace *mtface, UVDataWrapper &uvs, int uv_set_index,
+                                       COLLADAFW::IndexList& index_list, int index, bool quad)
+       {
+               int uv_indices[4][2];
+
+               // per face vertex indices, this means for quad we have 4 indices, not 8
+               COLLADAFW::UIntValuesArray& indices = index_list.getIndices();
+
+               // make indices into FloatOrDoubleArray
+               for (int i = 0; i < (quad ? 4 : 3); i++) {
+                       int uv_index = indices[index + i];
+                       uv_indices[i][0] = uv_index * 2;
+                       uv_indices[i][1] = uv_index * 2 + 1;
+               }
+
+               uvs.getUV(uv_set_index, uv_indices[0], mtface->uv[0]);
+               uvs.getUV(uv_set_index, uv_indices[1], mtface->uv[1]);
+               uvs.getUV(uv_set_index, uv_indices[2], mtface->uv[2]);
+
+               if (quad) uvs.getUV(uv_set_index, uv_indices[3], mtface->uv[3]);
+
+#ifdef COLLADA_DEBUG
+               /*if (quad) {
+                       fprintf(stderr, "face uv:\n"
+                                       "((%d, %d), (%d, %d), (%d, %d), (%d, %d))\n"
+                                       "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n",
+
+                                       uv_indices[0][0], uv_indices[0][1],
+                                       uv_indices[1][0], uv_indices[1][1],
+                                       uv_indices[2][0], uv_indices[2][1],
+                                       uv_indices[3][0], uv_indices[3][1],
+
+                                       mtface->uv[0][0], mtface->uv[0][1],
+                                       mtface->uv[1][0], mtface->uv[1][1],
+                                       mtface->uv[2][0], mtface->uv[2][1],
+                                       mtface->uv[3][0], mtface->uv[3][1]);
+               }
+               else {
+                       fprintf(stderr, "face uv:\n"
+                                       "((%d, %d), (%d, %d), (%d, %d))\n"
+                                       "((%.1f, %.1f), (%.1f, %.1f), (%.1f, %.1f))\n",
+
+                                       uv_indices[0][0], uv_indices[0][1],
+                                       uv_indices[1][0], uv_indices[1][1],
+                                       uv_indices[2][0], uv_indices[2][1],
+
+                                       mtface->uv[0][0], mtface->uv[0][1],
+                                       mtface->uv[1][0], mtface->uv[1][1],
+                                       mtface->uv[2][0], mtface->uv[2][1]);
+               }*/
+#endif
+       }
+
+#ifdef COLLADA_DEBUG
+       void print_index_list(COLLADAFW::IndexList& index_list)
+       {
+               fprintf(stderr, "Index list for \"%s\":\n", index_list.getName().c_str());
+               for (int i = 0; i < index_list.getIndicesCount(); i += 2) {
+                       fprintf(stderr, "%u, %u\n", index_list.getIndex(i), index_list.getIndex(i + 1));
+               }
+               fprintf(stderr, "\n");
+       }
+#endif
+
+       bool is_nice_mesh(COLLADAFW::Mesh *mesh)
+       {
+               COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives();
+               int i;
+
+               const char *name = get_dae_name(mesh);
+               
+               for (i = 0; i < prim_arr.getCount(); i++) {
+                       
+                       COLLADAFW::MeshPrimitive *mp = prim_arr[i];
+                       COLLADAFW::MeshPrimitive::PrimitiveType type = mp->getPrimitiveType();
+
+                       const char *type_str = primTypeToStr(type);
+                       
+                       // OpenCollada passes POLYGONS type for <polylist>
+                       if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) {
+
+                               COLLADAFW::Polygons *mpvc = (COLLADAFW::Polygons*)mp;
+                               COLLADAFW::Polygons::VertexCountArray& vca = mpvc->getGroupedVerticesVertexCountArray();
+                               
+                               for(int j = 0; j < vca.getCount(); j++){
+                                       int count = vca[j];
+                                       if (count < 3) {
+                                               fprintf(stderr, "Primitive %s in %s has at least one face with vertex count < 3\n",
+                                                               type_str, name);
+                                               return false;
+                                       }
+                               }
+                                       
+                       }
+                       else if(type != COLLADAFW::MeshPrimitive::TRIANGLES) {
+                               fprintf(stderr, "Primitive type %s is not supported.\n", type_str);
+                               return false;
+                       }
+               }
+               
+               if (mesh->getPositions().empty()) {
+                       fprintf(stderr, "Mesh %s has no vertices.\n", name);
+                       return false;
+               }
+
+               return true;
+       }
+
+       void read_vertices(COLLADAFW::Mesh *mesh, Mesh *me)
+       {
+               // vertices     
+               me->totvert = mesh->getPositions().getFloatValues()->getCount() / 3;
+               me->mvert = (MVert*)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, me->totvert);
+
+               const COLLADAFW::MeshVertexData& pos = mesh->getPositions();
+               MVert *mvert;
+               int i, j;
+
+               for (i = 0, mvert = me->mvert; i < me->totvert; i++, mvert++) {
+                       j = i * 3;
+
+                       if (pos.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT) {
+                               const float *array = pos.getFloatValues()->getData();
+                               mvert->co[0] = array[j];
+                               mvert->co[1] = array[j + 1];
+                               mvert->co[2] = array[j + 2];
+                       }
+                       else if (pos.getType() == COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE){
+                               const double *array = pos.getDoubleValues()->getData();
+                               mvert->co[0] = (float)array[j];
+                               mvert->co[1] = (float)array[j + 1];
+                               mvert->co[2] = (float)array[j + 2];
+                       }
+                       else {
+                               fprintf(stderr, "Cannot read vertex positions: unknown data type.\n");
+                               break;
+                       }
+               }
+       }
+       
+       int triangulate(int *indices, int vcount, MVert *verts, std::vector<unsigned int>& tri)
+       {
+               ListBase dispbase = {NULL, NULL};
+               DispList *dl;
+               float *vert;
+               int i = 0;
+               
+               dispbase.first = dispbase.last = NULL;
+               
+               dl = (DispList*)MEM_callocN(sizeof(DispList), "poly disp");
+               BLI_addtail(&dispbase, dl);
+               dl->type = DL_INDEX3;
+               dl->nr = vcount;
+               dl->type = DL_POLY;
+               dl->parts = 1;
+               dl->col = 0;
+               dl->verts = vert = (float*)MEM_callocN( sizeof(float) * 3 * vcount, "dl verts");
+               dl->index = (int*)MEM_callocN(sizeof(int) * 3 * vcount, "dl index");
+               
+               for (i = 0; i < vcount; ++i, vert += 3) {
+                       MVert *mvert = &verts[indices[i]];
+                       vert[0] = mvert->co[0];
+                       vert[1] = mvert->co[1];
+                       vert[2] = mvert->co[2];
+                       //fprintf(stderr, "%.1f %.1f %.1f \n", mvert->co[0], mvert->co[1], mvert->co[2]);
+               }
+               
+               filldisplist(&dispbase, &dispbase);
+
+               dl = (DispList*)dispbase.first;
+               int tottri = dl->parts;
+               int *index = dl->index;
+               
+               for (i = 0; i < tottri * 3; i++, index++) {
+                       tri.push_back(*index);
+               }
+
+               freedisplist(&dispbase);
+
+               return tottri;
+       }
+       
+       int count_new_tris(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris)
+       {
+               COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives();
+               int i, j, k;
+               
+               for (i = 0; i < prim_arr.getCount(); i++) {
+                       
+                       COLLADAFW::MeshPrimitive *mp = prim_arr[i];
+                       int type = mp->getPrimitiveType();
+                       size_t prim_totface = mp->getFaceCount();
+                       unsigned int *indices = mp->getPositionIndices().getData();
+                       
+                       if (type == COLLADAFW::MeshPrimitive::POLYLIST ||
+                               type == COLLADAFW::MeshPrimitive::POLYGONS) {
+                               
+                               COLLADAFW::Polygons *mpvc =     (COLLADAFW::Polygons*)mp;
+                               COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray();
+                               
+                               for (j = 0; j < prim_totface; j++) {
+                                       
+                                       int vcount = vcounta[j];
+                                       
+                                       if (vcount > 4) {
+                                               // create triangles using PolyFill
+                                               int *temp_indices = (int*)MEM_callocN(sizeof(int) * vcount, "face_index");
+                                               
+                                               for (k = 0; k < vcount; k++) {
+                                                       temp_indices[k] = indices[k];
+                                               }
+                                               
+                                               std::vector<unsigned int> tri;
+                                               
+                                               int totri = triangulate(temp_indices, vcount, me->mvert, tri);
+                                               new_tris += totri - 1;
+                                               MEM_freeN(temp_indices);
+                                               indices += vcount;
+                                       }
+                                       else if (vcount == 4 || vcount == 3) {
+                                               indices += vcount;
+                                       }
+                               }
+                       }
+               }
+               return new_tris;
+       }
+       
+       // TODO: import uv set names
+       void read_faces(COLLADAFW::Mesh *mesh, Mesh *me, int new_tris)
+       {
+               int i;
+               
+               // allocate faces
+               me->totface = mesh->getFacesCount() + new_tris;
+               me->mface = (MFace*)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, me->totface);
+               
+               // allocate UV layers
+               int totuvset = mesh->getUVCoords().getInputInfosArray().getCount();
+
+               for (i = 0; i < totuvset; i++) {
+                       CustomData_add_layer(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, me->totface);
+                       //this->set_layername_map[i] = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i);
+               }
+
+               // activate the first uv layer
+               if (totuvset) me->mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, 0);
+
+               UVDataWrapper uvs(mesh->getUVCoords());
+
+#ifdef COLLADA_DEBUG
+               // uvs.print();
+#endif
+
+               MFace *mface = me->mface;
+
+               MaterialIdPrimitiveArrayMap mat_prim_map;
+
+               int face_index = 0;
+
+               COLLADAFW::MeshPrimitiveArray& prim_arr = mesh->getMeshPrimitives();
+
+               for (i = 0; i < prim_arr.getCount(); i++) {
+                       
+                       COLLADAFW::MeshPrimitive *mp = prim_arr[i];
+
+                       // faces
+                       size_t prim_totface = mp->getFaceCount();
+                       unsigned int *indices = mp->getPositionIndices().getData();
+                       int j, k;
+                       int type = mp->getPrimitiveType();
+                       int index = 0;
+                       
+                       // since we cannot set mface->mat_nr here, we store a portion of me->mface in Primitive
+                       Primitive prim = {mface, 0};
+                       COLLADAFW::IndexListArray& index_list_array = mp->getUVCoordIndicesArray();
+
+#ifdef COLLADA_DEBUG
+                       /*
+                       fprintf(stderr, "Primitive %d:\n", i);
+                       for (int j = 0; j < totuvset; j++) {
+                               print_index_list(*index_list_array[j]);
+                       }
+                       */
+#endif
+                       
+                       if (type == COLLADAFW::MeshPrimitive::TRIANGLES) {
+                               for (j = 0; j < prim_totface; j++){
+                                       
+                                       set_face_indices(mface, indices, false);
+                                       indices += 3;
+
+                                       for (k = 0; k < totuvset; k++) {
+                                               // get mtface by face index and uv set index
+                                               MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k);
+                                               set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, false);
+                                       }
+                                       
+                                       index += 3;
+                                       mface++;
+                                       face_index++;
+                                       prim.totface++;
+                               }
+                       }
+                       else if (type == COLLADAFW::MeshPrimitive::POLYLIST || type == COLLADAFW::MeshPrimitive::POLYGONS) {
+                               COLLADAFW::Polygons *mpvc =     (COLLADAFW::Polygons*)mp;
+                               COLLADAFW::Polygons::VertexCountArray& vcounta = mpvc->getGroupedVerticesVertexCountArray();
+                               
+                               for (j = 0; j < prim_totface; j++) {
+                                       
+                                       // face
+                                       int vcount = vcounta[j];
+                                       if (vcount == 3 || vcount == 4) {
+                                               
+                                               set_face_indices(mface, indices, vcount == 4);
+                                               indices += vcount;
+                                               
+                                               // do the trick if needed
+                                               if (vcount == 4 && mface->v4 == 0)
+                                                       rotate_face_indices(mface);
+                                               
+                                               
+                                               // set mtface for each uv set
+                                               // it is assumed that all primitives have equal number of UV sets
+                                               
+                                               for (k = 0; k < totuvset; k++) {
+                                                       // get mtface by face index and uv set index
+                                                       MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, k);
+                                                       set_face_uv(&mtface[face_index], uvs, k, *index_list_array[k], index, mface->v4 != 0);
+                                               }
+                                               
+                                               index += mface->v4 ? 4 : 3;
+                                               mface++;
+                                               face_index++;
+                                               prim.totface++;
+                                               
+                                       }
+                                       else {
+                                               // create triangles using PolyFill
+                                               int *temp_indices = (int*)MEM_callocN(sizeof(int) *vcount, "face_index");
+                                               int *temp_uv_indices = (int*)MEM_callocN(sizeof(int) *vcount, "uv_index");
+                                               
+                                               for (k = 0; k < vcount; k++) {
+                                                       temp_indices[k] = indices[k];
+                                                       temp_uv_indices[k] = index + k;
+                                               }
+                                               
+                                               std::vector<unsigned int> tri;
+                                               
+                                               int totri = triangulate(temp_indices, vcount, me->mvert, tri);
+                                               
+                                               for (k = 0; k < tri.size() / 3; k++) {
+                                                       unsigned int tris_indices[3];
+                                                       unsigned int uv_indices[3];
+                                                       tris_indices[0] = temp_indices[tri[k * 3]];
+                                                       tris_indices[1] = temp_indices[tri[k * 3 + 1]];
+                                                       tris_indices[2] = temp_indices[tri[k * 3 + 2]];
+                                                       uv_indices[0] = temp_uv_indices[tri[k * 3]];
+                                                       uv_indices[1] = temp_uv_indices[tri[k * 3 + 1]];
+                                                       uv_indices[2] = temp_uv_indices[tri[k * 3 + 2]];
+                                                       //fprintf(stderr, "%u %u %u \n", tris_indices[0], tris_indices[1], tris_indices[2]);
+                                                       set_face_indices(mface, tris_indices, false);
+                                                       
+                                                       for (int l = 0; l < totuvset; l++) {
+                                                               // get mtface by face index and uv set index
+                                                               MTFace *mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, l);
+                                                               set_face_uv(&mtface[face_index], uvs, l, *index_list_array[l], uv_indices);
+                                                               
+                                                       }
+                                                       
+                                                       mface++;
+                                                       face_index++;
+                                                       prim.totface++;
+                                               }
+                                               
+                                               index += vcount;
+                                               indices += vcount;
+                                               MEM_freeN(temp_indices);
+                                               MEM_freeN(temp_uv_indices);
+                                       }
+                               }
+                       }
+                       
+                       mat_prim_map[mp->getMaterialId()].push_back(prim);
+               }
+
+               geom_uid_mat_mapping_map[mesh->getUniqueId()] = mat_prim_map;
+       }
+
+public:
+
+       MeshImporter(ArmatureImporter *arm, Scene *sce) : scene(sce), armature_importer(arm) {}
+
+       virtual Object *get_object_by_geom_uid(const COLLADAFW::UniqueId& geom_uid)
+       {
+               if (uid_object_map.find(geom_uid) != uid_object_map.end())
+                       return uid_object_map[geom_uid];
+               return NULL;
+       }
+       
+       MTex *assign_textures_to_uvlayer(COLLADAFW::InstanceGeometry::TextureCoordinateBinding &ctexture,
+                                                                        Mesh *me, TexIndexTextureArrayMap& texindex_texarray_map,
+                                                                        MTex *color_texture)
+       {
+               
+               COLLADAFW::TextureMapId texture_index = ctexture.textureMapId;
+               
+               char *uvname = CustomData_get_layer_name(&me->fdata, CD_MTFACE, ctexture.setIndex);
+               
+               if (texindex_texarray_map.find(texture_index) == texindex_texarray_map.end()) {
+                       
+                       fprintf(stderr, "Cannot find texture array by texture index.\n");
+                       return color_texture;
+               }
+               
+               std::vector<MTex*> textures = texindex_texarray_map[texture_index];
+               
+               std::vector<MTex*>::iterator it;
+               
+               for (it = textures.begin(); it != textures.end(); it++) {
+                       
+                       MTex *texture = *it;
+                       
+                       if (texture) {
+                               strcpy(texture->uvname, uvname);
+                               if (texture->mapto == MAP_COL) color_texture = texture;
+                       }
+               }
+               return color_texture;
+       }
+       
+       MTFace *assign_material_to_geom(COLLADAFW::InstanceGeometry::MaterialBinding cmaterial,
+                                                                       std::map<COLLADAFW::UniqueId, Material*>& uid_material_map,
+                                                                       Object *ob, const COLLADAFW::UniqueId *geom_uid, 
+                                                                       MTex **color_texture, char *layername, MTFace *texture_face,
+                                                                       std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map, int mat_index)
+       {
+               Mesh *me = (Mesh*)ob->data;
+               const COLLADAFW::UniqueId& ma_uid = cmaterial.getReferencedMaterial();
+               
+               // do we know this material?
+               if (uid_material_map.find(ma_uid) == uid_material_map.end()) {
+                       
+                       fprintf(stderr, "Cannot find material by UID.\n");
+                       return NULL;
+               }
+               
+               Material *ma = uid_material_map[ma_uid];
+               assign_material(ob, ma, ob->totcol + 1);
+               
+               COLLADAFW::InstanceGeometry::TextureCoordinateBindingArray& tex_array = 
+                       cmaterial.getTextureCoordinateBindingArray();
+               TexIndexTextureArrayMap texindex_texarray_map = material_texture_mapping_map[ma];
+               unsigned int i;
+               // loop through <bind_vertex_inputs>
+               for (i = 0; i < tex_array.getCount(); i++) {
+                       
+                       *color_texture = assign_textures_to_uvlayer(tex_array[i], me, texindex_texarray_map,
+                                                                                                               *color_texture);
+               }
+               
+               // set texture face
+               if (*color_texture &&
+                       strlen((*color_texture)->uvname) &&
+                       strcmp(layername, (*color_texture)->uvname) != 0) {
+                       
+                       texture_face = (MTFace*)CustomData_get_layer_named(&me->fdata, CD_MTFACE,
+                                                                                                                          (*color_texture)->uvname);
+                       strcpy(layername, (*color_texture)->uvname);
+               }
+               
+               MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[*geom_uid];
+               COLLADAFW::MaterialId mat_id = cmaterial.getMaterialId();
+               
+               // assign material indices to mesh faces
+               if (mat_prim_map.find(mat_id) != mat_prim_map.end()) {
+                       
+                       std::vector<Primitive>& prims = mat_prim_map[mat_id];
+                       
+                       std::vector<Primitive>::iterator it;
+                       
+                       for (it = prims.begin(); it != prims.end(); it++) {
+                               Primitive& prim = *it;
+                               i = 0;
+                               while (i++ < prim.totface) {
+                                       prim.mface->mat_nr = mat_index;
+                                       prim.mface++;
+                                       // bind texture images to faces
+                                       if (texture_face && (*color_texture)) {
+                                               texture_face->mode = TF_TEX;
+                                               texture_face->tpage = (Image*)(*color_texture)->tex->ima;
+                                               texture_face++;
+                                       }
+                               }
+                       }
+               }
+               
+               return texture_face;
+       }
+       
+       
+       Object *create_mesh_object(COLLADAFW::Node *node, COLLADAFW::InstanceGeometry *geom,
+                                                          bool isController,
+                                                          std::map<COLLADAFW::UniqueId, Material*>& uid_material_map,
+                                                          std::map<Material*, TexIndexTextureArrayMap>& material_texture_mapping_map)
+       {
+               const COLLADAFW::UniqueId *geom_uid = &geom->getInstanciatedObjectId();
+               
+               // check if node instanciates controller or geometry
+               if (isController) {
+                       
+                       geom_uid = armature_importer->get_geometry_uid(*geom_uid);
+                       
+                       if (!geom_uid) {
+                               fprintf(stderr, "Couldn't find a mesh UID by controller's UID.\n");
+                               return NULL;
+                       }
+               }
+               else {
+                       
+                       if (uid_mesh_map.find(*geom_uid) == uid_mesh_map.end()) {
+                               // this could happen if a mesh was not created
+                               // (e.g. if it contains unsupported geometry)
+                               fprintf(stderr, "Couldn't find a mesh by UID.\n");
+                               return NULL;
+                       }
+               }
+               if (!uid_mesh_map[*geom_uid]) return NULL;
+               
+               Object *ob = add_object(scene, OB_MESH);
+
+               // store object pointer for ArmatureImporter
+               uid_object_map[*geom_uid] = ob;
+               
+               // name Object
+               const std::string& id = node->getOriginalId();
+               if (id.length())
+                       rename_id(&ob->id, (char*)id.c_str());
+               
+               // replace ob->data freeing the old one
+               Mesh *old_mesh = (Mesh*)ob->data;
+
+               set_mesh(ob, uid_mesh_map[*geom_uid]);
+               
+               if (old_mesh->id.us == 0) free_libblock(&G.main->mesh, old_mesh);
+               
+               char layername[100];
+               MTFace *texture_face = NULL;
+               MTex *color_texture = NULL;
+               
+               COLLADAFW::InstanceGeometry::MaterialBindingArray& mat_array = 
+                       geom->getMaterialBindings();
+               
+               // loop through geom's materials
+               for (unsigned int i = 0; i < mat_array.getCount(); i++) {
+                       
+                       texture_face = assign_material_to_geom(mat_array[i], uid_material_map, ob, geom_uid,
+                                                                                                  &color_texture, layername, texture_face,
+                                                                                                  material_texture_mapping_map, i);
+               }
+                       
+               return ob;
+       }
+
+       // create a mesh storing a pointer in a map so it can be retrieved later by geometry UID
+       bool write_geometry(const COLLADAFW::Geometry* geom) 
+       {
+               // TODO: import also uvs, normals
+               // XXX what to do with normal indices?
+               // XXX num_normals may be != num verts, then what to do?
+
+               // check geometry->getType() first
+               if (geom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) {
+                       // TODO: report warning
+                       fprintf(stderr, "Mesh type %s is not supported\n", geomTypeToStr(geom->getType()));
+                       return true;
+               }
+               
+               COLLADAFW::Mesh *mesh = (COLLADAFW::Mesh*)geom;
+               
+               if (!is_nice_mesh(mesh)) {
+                       fprintf(stderr, "Ignoring mesh %s\n", get_dae_name(mesh));
+                       return true;
+               }
+               
+               const std::string& str_geom_id = mesh->getOriginalId();
+               Mesh *me = add_mesh((char*)str_geom_id.c_str());
+
+               // store the Mesh pointer to link it later with an Object
+               this->uid_mesh_map[mesh->getUniqueId()] = me;
+               
+               int new_tris = 0;
+               
+               read_vertices(mesh, me);
+
+               new_tris = count_new_tris(mesh, me, new_tris);
+               
+               read_faces(mesh, me, new_tris);
+               
+               mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
+
+               return true;
+       }
+
+};
+
+class AnimationImporter : private TransformReader, public AnimationImporterBase
+{
+private:
+
+       ArmatureImporter *armature_importer;
+       Scene *scene;
+
+       std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > uid_fcurve_map;
+       std::map<COLLADAFW::UniqueId, TransformReader::Animation> uid_animated_map;
+       std::map<bActionGroup*, std::vector<FCurve*> > fcurves_actionGroup_map;
+       
+       FCurve *create_fcurve(int array_index, 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 create_bezt(FCurve *fcu, float frame, float output)
+       {
+               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, 0);
+               calchandles_fcurve(fcu);
+       }
+
+       void make_fcurves_from_animation(COLLADAFW::AnimationCurve *curve,
+                                                                        COLLADAFW::FloatOrDoubleArray& input,
+                                                                        COLLADAFW::FloatOrDoubleArray& output,
+                                                                        COLLADAFW::FloatOrDoubleArray& intan,
+                                                                        COLLADAFW::FloatOrDoubleArray& outtan, size_t dim, float fps)
+       {
+               int i;
+               // char *path = "location";
+               std::vector<FCurve*>& fcurves = uid_fcurve_map[curve->getUniqueId()];
+
+               if (dim == 1) {
+                       // create fcurve
+                       FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
+
+                       fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+                       // fcu->rna_path = BLI_strdupn(path, strlen(path));
+                       fcu->array_index = 0;
+                       //fcu->totvert = curve->getKeyCount();
+                       
+                       // create beztriple for each key
+                       for (i = 0; i < curve->getKeyCount(); i++) {
+                               BezTriple bez;
+                               memset(&bez, 0, sizeof(BezTriple));
+                               // intangent
+                               bez.vec[0][0] = get_float_value(intan, i + i) * fps;
+                               bez.vec[0][1] = get_float_value(intan, i + i + 1);
+                               // input, output
+                               bez.vec[1][0] = get_float_value(input, i) * fps;
+                               bez.vec[1][1] = get_float_value(output, i);
+                               // outtangent
+                               bez.vec[2][0] = get_float_value(outtan, i + i) * fps;
+                               bez.vec[2][1] = get_float_value(outtan, i + i + 1);
+                               
+                               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, 0);
+                               calchandles_fcurve(fcu);
+                       }
+
+                       fcurves.push_back(fcu);
+               }
+               else if(dim == 3) {
+                       for (i = 0; i < dim; i++ ) {
+                               // create fcurve
+                               FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
+                               
+                               fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+                               // fcu->rna_path = BLI_strdupn(path, strlen(path));
+                               fcu->array_index = 0;
+                               //fcu->totvert = curve->getKeyCount();
+                               
+                               // create beztriple for each key
+                               for (int j = 0; j < curve->getKeyCount(); j++) {
+                                       BezTriple bez;
+                                       memset(&bez, 0, sizeof(BezTriple));
+                                       // intangent
+                                       bez.vec[0][0] = get_float_value(intan, j * 6 + i + i) * fps;
+                                       bez.vec[0][1] = get_float_value(intan, j * 6 + i + i + 1);
+                                       // input, output
+                                       bez.vec[1][0] = get_float_value(input, j) * fps; 
+                                       bez.vec[1][1] = get_float_value(output, j * 3 + i);
+                                       // outtangent
+                                       bez.vec[2][0] = get_float_value(outtan, j * 6 + i + i) * fps;
+                                       bez.vec[2][1] = get_float_value(outtan, j * 6 + i + i + 1);
+
+                                       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, 0);
+                                       calchandles_fcurve(fcu);
+                               }
+
+                               fcurves.push_back(fcu);
+                       }
+               }
+       }
+       
+       void add_fcurves_to_object(Object *ob, std::vector<FCurve*>& curves, char *rna_path, int array_index, Animation *animated)
+       {
+               ID *id = &ob->id;
+               bAction *act;
+               bActionGroup *grp = NULL;
+               
+               if (!ob->adt || !ob->adt->action) act = verify_adt_action(id, 1);
+               else act = verify_adt_action(id, 0);
+
+               if (!ob->adt || !ob->adt->action) {
+                       fprintf(stderr, "Cannot create anim data or action for this object. \n");
+                       return;
+               }
+               
+               FCurve *fcu;
+               std::vector<FCurve*>::iterator it;
+               int i = 0;
+               
+               for (it = curves.begin(); it != curves.end(); it++) {
+                       fcu = *it;
+                       fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
+                       
+                       if (array_index == -1) fcu->array_index = i;
+                       else fcu->array_index = array_index;
+
+                       // convert degrees to radians for rotation
+                       char *p = strstr(rna_path, "rotation");
+                       if (p && *(p + strlen("rotation")) == '\0') {
+                               for(int j = 0; j < fcu->totvert; j++) {
+                                       float rot_intan = fcu->bezt[j].vec[0][1];
+                                       float rot_output = fcu->bezt[j].vec[1][1];
+                                       float rot_outtan = fcu->bezt[j].vec[2][1];
+                                   fcu->bezt[j].vec[0][1] = rot_intan * M_PI / 180.0f;
+                                       fcu->bezt[j].vec[1][1] = rot_output * M_PI / 180.0f;
+                                       fcu->bezt[j].vec[2][1] = rot_outtan * M_PI / 180.0f;
+                               }
+                       }
+                       
+                       if (ob->type == OB_ARMATURE) {
+                               bAction *act = ob->adt->action;
+                               const char *bone_name = get_joint_name(animated->node);
+                               
+                               if (bone_name) {
+                                       /* try to find group */
+                                       grp = action_groups_find_named(act, bone_name);
+                                       
+                                       /* no matching groups, so add one */
+                                       if (grp == NULL) {
+                                               /* Add a new group, and make it active */
+                                               grp = (bActionGroup*)MEM_callocN(sizeof(bActionGroup), "bActionGroup");
+                                               
+                                               grp->flag = AGRP_SELECTED;
+                                               BLI_snprintf(grp->name, sizeof(grp->name), bone_name);
+                                               
+                                               BLI_addtail(&act->groups, grp);
+                                               BLI_uniquename(&act->groups, grp, "Group", '.', offsetof(bActionGroup, name), 64);
+                                       }
+                                       
+                                       /* add F-Curve to group */
+                                       action_groups_add_channel(act, grp, fcu);
+                                       
+                               }
+                               if (p && *(p + strlen("rotation")) == '\0') {
+                                       fcurves_actionGroup_map[grp].push_back(fcu);
+                               }
+                       }
+                       else {
+                               BLI_addtail(&act->curves, fcu);
+                       }
+
+                       i++;
+               }
+       }
+public:
+
+       AnimationImporter(UnitConverter *conv, ArmatureImporter *arm, Scene *scene) :
+               TransformReader(conv), armature_importer(arm), scene(scene) { }
+
+       bool write_animation( const COLLADAFW::Animation* anim ) 
+       {
+               float fps = (float)FPS;
+
+               if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) {
+                       COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve*)anim;
+                       size_t dim = curve->getOutDimension();
+                       
+                       // XXX Don't know if it's necessary
+                       // Should we check outPhysicalDimension?
+                       if (curve->getInPhysicalDimension() != COLLADAFW::PHYSICAL_DIMENSION_TIME) {
+                               fprintf(stderr, "Inputs physical dimension is not time. \n");
+                               return true;
+                       }
+
+                       COLLADAFW::FloatOrDoubleArray& input = curve->getInputValues();
+                       COLLADAFW::FloatOrDoubleArray& output = curve->getOutputValues();
+                       COLLADAFW::FloatOrDoubleArray& intan = curve->getInTangentValues();
+                       COLLADAFW::FloatOrDoubleArray& outtan = curve->getOutTangentValues();
+
+                       // a curve can have mixed interpolation type,
+                       // in this case curve->getInterpolationTypes returns a list of interpolation types per key
+                       COLLADAFW::AnimationCurve::InterpolationType interp = curve->getInterpolationType();
+
+                       if (interp != COLLADAFW::AnimationCurve::INTERPOLATION_MIXED) {
+                               switch (interp) {
+                               case COLLADAFW::AnimationCurve::INTERPOLATION_LINEAR:
+                                       // support this
+                                       make_fcurves_from_animation(curve, input, output, intan, outtan, dim, fps);
+                                       break;
+                               case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER:
+                                       // and this
+                                       make_fcurves_from_animation(curve, input, output, intan, outtan, dim, fps);
+                                       break;
+                               case COLLADAFW::AnimationCurve::INTERPOLATION_CARDINAL:
+                               case COLLADAFW::AnimationCurve::INTERPOLATION_HERMITE:
+                               case COLLADAFW::AnimationCurve::INTERPOLATION_BSPLINE:
+                               case COLLADAFW::AnimationCurve::INTERPOLATION_STEP:
+                                       fprintf(stderr, "CARDINAL, HERMITE, BSPLINE and STEP anim interpolation types not supported yet.\n");
+                                       break;
+                               }
+                       }
+                       else {
+                               // not supported yet
+                               fprintf(stderr, "MIXED anim interpolation type is not supported yet.\n");
+                       }
+               }
+               else {
+                       fprintf(stderr, "FORMULA animation type is not supported yet.\n");
+               }
+               
+               return true;
+       }
+       
+       // called on post-process stage after writeVisualScenes
+       bool write_animation_list( const COLLADAFW::AnimationList* animationList ) 
+       {
+               const COLLADAFW::UniqueId& anim_list_id = animationList->getUniqueId();
+
+               // possible in case we cannot interpret some transform
+               if (uid_animated_map.find(anim_list_id) == uid_animated_map.end()) {
+                       return true;
+               }
+
+               // for bones rna_path is like: pose.pose_channels["bone-name"].rotation
+               
+               // what does this AnimationList animate?
+               Animation& animated = uid_animated_map[anim_list_id];
+               Object *ob = animated.ob;
+
+               char rna_path[100];
+               char joint_path[100];
+               bool is_joint = false;
+
+               // if ob is NULL, it should be a JOINT
+               if (!ob) {
+                       ob = armature_importer->get_armature_for_joint(animated.node);
+
+                       if (!ob) {
+                               fprintf(stderr, "Cannot find armature for node %s\n", get_joint_name(animated.node));
+                               return true;
+                       }
+
+                       armature_importer->get_rna_path_for_joint(animated.node, joint_path, sizeof(joint_path));
+
+                       is_joint = true;
+               }
+               
+               const COLLADAFW::AnimationList::AnimationBindings& bindings = animationList->getAnimationBindings();
+
+               switch (animated.tm->getTransformationType()) {
+               case COLLADAFW::Transformation::TRANSLATE:
+                       {
+                               if (is_joint)
+                                       BLI_snprintf(rna_path, sizeof(rna_path), "%s.location", joint_path);
+                               else
+                                       BLI_strncpy(rna_path, "location", sizeof(rna_path));
+
+                               for (int i = 0; i < bindings.getCount(); i++) {
+                                       const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
+                                       COLLADAFW::UniqueId anim_uid = binding.animation;
+
+                                       if (uid_fcurve_map.find(anim_uid) == uid_fcurve_map.end()) {
+                                               fprintf(stderr, "Cannot find FCurve by animation UID.\n");
+                                               continue;
+                                       }
+
+                                       std::vector<FCurve*>& fcurves = uid_fcurve_map[anim_uid];
+                                       
+                                       switch (binding.animationClass) {
+                                       case COLLADAFW::AnimationList::POSITION_X:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, 0, &animated);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Y:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, 1, &animated);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Z:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, 2, &animated);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_XYZ:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, -1, &animated);
+                                               break;
+                                       default:
+                                               fprintf(stderr, "AnimationClass %d is not supported for TRANSLATE transformation.\n",
+                                                               binding.animationClass);
+                                       }
+                               }
+                       }
+                       break;
+               case COLLADAFW::Transformation::ROTATE:
+                       {
+                               if (is_joint)
+                                       BLI_snprintf(rna_path, sizeof(rna_path), "%s.euler_rotation", joint_path);
+                               else
+                                       BLI_strncpy(rna_path, "rotation", sizeof(rna_path));
+
+                               COLLADAFW::Rotate* rot = (COLLADAFW::Rotate*)animated.tm;
+                               COLLADABU::Math::Vector3& axis = rot->getRotationAxis();
+                               
+                               for (int i = 0; i < bindings.getCount(); i++) {
+                                       const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
+                                       COLLADAFW::UniqueId anim_uid = binding.animation;
+
+                                       if (uid_fcurve_map.find(anim_uid) == uid_fcurve_map.end()) {
+                                               fprintf(stderr, "Cannot find FCurve by animation UID.\n");
+                                               continue;
+                                       }
+
+                                       std::vector<FCurve*>& fcurves = uid_fcurve_map[anim_uid];
+
+                                       switch (binding.animationClass) {
+                                       case COLLADAFW::AnimationList::ANGLE:
+                                               if (COLLADABU::Math::Vector3::UNIT_X == axis) {
+                                                       add_fcurves_to_object(ob, fcurves, rna_path, 0, &animated);
+                                               }
+                                               else if (COLLADABU::Math::Vector3::UNIT_Y == axis) {
+                                                       add_fcurves_to_object(ob, fcurves, rna_path, 1, &animated);
+                                               }
+                                               else if (COLLADABU::Math::Vector3::UNIT_Z == axis) {
+                                                       add_fcurves_to_object(ob, fcurves, rna_path, 2, &animated);
+                                               }
+                                               break;
+                                       case COLLADAFW::AnimationList::AXISANGLE:
+                                               // convert axis-angle to quat? or XYZ?
+                                               break;
+                                       default:
+                                               fprintf(stderr, "AnimationClass %d is not supported for ROTATE transformation.\n",
+                                                               binding.animationClass);
+                                       }
+                               }
+                       }
+                       break;
+               case COLLADAFW::Transformation::SCALE:
+                       {
+                               if (is_joint)
+                                       BLI_snprintf(rna_path, sizeof(rna_path), "%s.scale", joint_path);
+                               else
+                                       BLI_strncpy(rna_path, "scale", sizeof(rna_path));
+
+                               // same as for TRANSLATE
+                               for (int i = 0; i < bindings.getCount(); i++) {
+                                       const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
+                                       COLLADAFW::UniqueId anim_uid = binding.animation;
+
+                                       if (uid_fcurve_map.find(anim_uid) == uid_fcurve_map.end()) {
+                                               fprintf(stderr, "Cannot find FCurve by animation UID.\n");
+                                               continue;
+                                       }
+                                       
+                                       std::vector<FCurve*>& fcurves = uid_fcurve_map[anim_uid];
+                                       
+                                       switch (binding.animationClass) {
+                                       case COLLADAFW::AnimationList::POSITION_X:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, 0, &animated);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Y:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, 1, &animated);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Z:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, 2, &animated);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_XYZ:
+                                               add_fcurves_to_object(ob, fcurves, rna_path, -1, &animated);
+                                               break;
+                                       default:
+                                               fprintf(stderr, "AnimationClass %d is not supported for TRANSLATE transformation.\n",
+                                                               binding.animationClass);
+                                       }
+                               }
+                       }
+                       break;
+               case COLLADAFW::Transformation::MATRIX:
+               case COLLADAFW::Transformation::SKEW:
+               case COLLADAFW::Transformation::LOOKAT:
+                       fprintf(stderr, "Animation of MATRIX, SKEW and LOOKAT transformations is not supported yet.\n");
+                       break;
+               }
+               
+               return true;
+       }
+
+       void read_node_transform(COLLADAFW::Node *node, Object *ob)
+       {
+               float mat[4][4];
+               TransformReader::get_node_mat(mat, node, &uid_animated_map, ob);
+               if (ob)
+                       TransformReader::decompose(mat, ob->loc, ob->rot, ob->size);
+       }
+       
+       virtual void change_eul_to_quat(Object *ob, bAction *act)
+       {
+               bActionGroup *grp;
+               int i;
+               
+               for (grp = (bActionGroup*)act->groups.first; grp; grp = grp->next) {
+
+                       FCurve *eulcu[3] = {NULL, NULL, NULL};
+                       
+                       if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end())
+                               continue;
+
+                       std::vector<FCurve*> &rot_fcurves = fcurves_actionGroup_map[grp];
+                       
+                       if (rot_fcurves.size() > 3) continue;
+
+                       for (i = 0; i < rot_fcurves.size(); i++)
+                               eulcu[rot_fcurves[i]->array_index] = rot_fcurves[i];
+
+                       char joint_path[100];
+                       char rna_path[100];
+
+                       BLI_snprintf(joint_path, sizeof(joint_path), "pose.pose_channels[\"%s\"]", grp->name);
+                       BLI_snprintf(rna_path, sizeof(rna_path), "%s.rotation", joint_path);
+
+                       FCurve *quatcu[4] = {
+                               create_fcurve(0, rna_path),
+                               create_fcurve(1, rna_path),
+                               create_fcurve(2, rna_path),
+                               create_fcurve(3, rna_path)
+                       };
+
+                       for (i = 0; i < 3; i++) {
+
+                               FCurve *cu = eulcu[i];
+
+                               if (!cu) continue;
+
+                               for (int j = 0; j < cu->totvert; j++) {
+                                       float frame = cu->bezt[j].vec[1][0];
+
+                                       float eul[3] = {
+                                               eulcu[0] ? evaluate_fcurve(eulcu[0], frame) : 0.0f,
+                                               eulcu[1] ? evaluate_fcurve(eulcu[1], frame) : 0.0f,
+                                               eulcu[2] ? evaluate_fcurve(eulcu[2], frame) : 0.0f
+                                       };
+
+                                       float quat[4];
+
+                                       EulToQuat(eul, quat);
+
+                                       for (int k = 0; k < 4; k++)
+                                               create_bezt(quatcu[k], frame, quat[k]);
+                               }
+                       }
+
+                       // now replace old Euler curves
+
+                       for (i = 0; i < 3; i++) {
+                               if (!eulcu[i]) continue;
+
+                               action_groups_remove_channel(act, eulcu[i]);
+                               free_fcurve(eulcu[i]);
+                       }
+
+                       get_pose_channel(ob->pose, grp->name)->rotmode = ROT_MODE_QUAT;
+
+                       for (i = 0; i < 4; i++)
+                               action_groups_add_channel(act, grp, quatcu[i]);
+               }
+
+               bPoseChannel *pchan;
+               for (pchan = (bPoseChannel*)ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+                       pchan->rotmode = ROT_MODE_QUAT;
+               }
+       }       
+};
+
+/*
+
+  COLLADA Importer limitations:
+
+  - no multiple scene import, all objects are added to active scene
+
+ */
+/** Class that needs to be implemented by a writer. 
+       IMPORTANT: The write functions are called in arbitrary order.*/
+class Writer: public COLLADAFW::IWriter
+{
+private:
+       std::string mFilename;
+       
+       bContext *mContext;
+
+       UnitConverter unit_converter;
+       ArmatureImporter armature_importer;
+       MeshImporter mesh_importer;
+       AnimationImporter anim_importer;
+
+       std::map<COLLADAFW::UniqueId, Image*> uid_image_map;
+       std::map<COLLADAFW::UniqueId, Material*> uid_material_map;
+       std::map<COLLADAFW::UniqueId, Material*> uid_effect_map;
+       std::map<COLLADAFW::UniqueId, Camera*> uid_camera_map;
+       std::map<COLLADAFW::UniqueId, Lamp*> uid_lamp_map;
+       std::map<Material*, TexIndexTextureArrayMap> material_texture_mapping_map;
+       // animation
+       // std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > uid_fcurve_map;
+       // Nodes don't share AnimationLists (Arystan)
+       // std::map<COLLADAFW::UniqueId, Animation> uid_animated_map; // AnimationList->uniqueId to AnimatedObject map
+
+public:
+
+       /** Constructor. */
+       Writer(bContext *C, const char *filename) : mContext(C), mFilename(filename),
+                                                                                               armature_importer(&unit_converter, &mesh_importer, &anim_importer, CTX_data_scene(C)),
+                                                                                               mesh_importer(&armature_importer, CTX_data_scene(C)),
+                                                                                               anim_importer(&unit_converter, &armature_importer, CTX_data_scene(C)) {}
+
+       /** Destructor. */
+       ~Writer() {}
+
+       bool write()
+       {
+               COLLADASaxFWL::Loader loader;
+               COLLADAFW::Root root(&loader, this);
+
+               // XXX report error
+               if (!root.loadDocument(mFilename))
+                       return false;
+
+               return true;
+       }
+
+       /** This method will be called if an error in the loading process occurred and the loader cannot
+               continue to to load. The writer should undo all operations that have been performed.
+               @param errorMessage A message containing informations about the error that occurred.
+       */
+       virtual void cancel(const COLLADAFW::String& errorMessage)
+       {
+               // TODO: if possible show error info
+               //
+               // Should we get rid of invisible Meshes that were created so far
+               // or maybe create objects at coordinate space origin?
+               //
+               // The latter sounds better.
+       }
+
+       /** This is the method called. The writer hast to prepare to receive data.*/
+       virtual void start()
+       {
+       }
+
+       /** This method is called after the last write* method. No other methods will be called after this.*/
+       virtual void finish()
+       {
+               armature_importer.fix_animation();
+       }
+
+       /** When this method is called, the writer must write the global document asset.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeGlobalAsset ( const COLLADAFW::FileInfo* asset ) 
+       {
+               // XXX take up_axis, unit into account
+               // COLLADAFW::FileInfo::Unit unit = asset->getUnit();
+               // COLLADAFW::FileInfo::UpAxisType upAxis = asset->getUpAxisType();
+               unit_converter.read_asset(asset);
+
+               return true;
+       }
+
+       /** When this method is called, the writer must write the scene.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeScene ( const COLLADAFW::Scene* scene ) 
+       {
+               // XXX could store the scene id, but do nothing for now
+               return true;
+       }
+       Object *create_camera_object(COLLADAFW::InstanceCamera *camera, Object *ob, Scene *sce)
+       {
+               const COLLADAFW::UniqueId& cam_uid = camera->getInstanciatedObjectId();
+               if (uid_camera_map.find(cam_uid) == uid_camera_map.end()) {     
+                       fprintf(stderr, "Couldn't find camera by UID. \n");
+                       return NULL;
+               }
+               ob = add_object(sce, OB_CAMERA);
+               Camera *cam = uid_camera_map[cam_uid];
+               Camera *old_cam = (Camera*)ob->data;
+               old_cam->id.us--;
+               ob->data = cam;
+               if (old_cam->id.us == 0) free_libblock(&G.main->camera, old_cam);
+               return ob;
+       }
+       
+       Object *create_lamp_object(COLLADAFW::InstanceLight *lamp, Object *ob, Scene *sce)
+       {
+               const COLLADAFW::UniqueId& lamp_uid = lamp->getInstanciatedObjectId();
+               if (uid_lamp_map.find(lamp_uid) == uid_lamp_map.end()) {        
+                       fprintf(stderr, "Couldn't find lamp by UID. \n");
+                       return NULL;
+               }
+               ob = add_object(sce, OB_LAMP);
+               Lamp *la = uid_lamp_map[lamp_uid];
+               Lamp *old_lamp = (Lamp*)ob->data;
+               old_lamp->id.us--;
+               ob->data = la;
+               if (old_lamp->id.us == 0) free_libblock(&G.main->lamp, old_lamp);
+               return ob;
+       }
+       
+       void write_node (COLLADAFW::Node *node, COLLADAFW::Node *parent_node, Scene *sce, Object *par)
+       {
+               Object *ob = NULL;
+
+               if (node->getType() == COLLADAFW::Node::JOINT) {
+
+                       if (node->getType() == COLLADAFW::Node::JOINT) {
+                               armature_importer.add_joint(node, parent_node == NULL || parent_node->getType() != COLLADAFW::Node::JOINT);
+                       }
+
+               }
+               else {
+                       COLLADAFW::InstanceGeometryPointerArray &geom = node->getInstanceGeometries();
+                       COLLADAFW::InstanceCameraPointerArray &camera = node->getInstanceCameras();
+                       COLLADAFW::InstanceLightPointerArray &lamp = node->getInstanceLights();
+                       COLLADAFW::InstanceControllerPointerArray &controller = node->getInstanceControllers();
+                       COLLADAFW::InstanceNodePointerArray &inst_node = node->getInstanceNodes();
+
+                       // XXX linking object with the first <instance_geometry>, though a node may have more of them...
+                       // maybe join multiple <instance_...> meshes into 1, and link object with it? not sure...
+                       // <instance_geometry>
+                       if (geom.getCount() != 0) {
+                               ob = mesh_importer.create_mesh_object(node, geom[0], false, uid_material_map,
+                                                                                                         material_texture_mapping_map);
+                       }
+                       else if (camera.getCount() != 0) {
+                               ob = create_camera_object(camera[0], ob, sce);
+                       }
+                       else if (lamp.getCount() != 0) {
+                               ob = create_lamp_object(lamp[0], ob, sce);
+                       }
+                       else if (controller.getCount() != 0) {
+                               COLLADAFW::InstanceController *geom = (COLLADAFW::InstanceController*)controller[0];
+                               ob = mesh_importer.create_mesh_object(node, geom, true, uid_material_map, material_texture_mapping_map);
+                       }
+                       // XXX instance_node is not supported yet
+                       else if (inst_node.getCount() != 0) {
+                               return;
+                       }
+                       // if node is empty - create empty object
+                       // XXX empty node may not mean it is empty object, not sure about this
+                       else {
+                               ob = add_object(sce, OB_EMPTY);
+                       }
+                       
+                       // check if object is not NULL
+                       if (!ob) return;
+                       
+                       // if par was given make this object child of the previous 
+                       if (par && ob) {
+                               Object workob;
+
+                               ob->parent = par;
+
+                               // doing what 'set parent' operator does
+                               par->recalc |= OB_RECALC_OB;
+                               ob->parsubstr[0] = 0;
+                       
+                               DAG_scene_sort(sce);
+                       }
+               }
+
+               anim_importer.read_node_transform(node, ob);
+
+               // if node has child nodes write them
+               COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes();
+               for (int i = 0; i < child_nodes.getCount(); i++) {      
+                       write_node(child_nodes[i], node, sce, ob);
+               }
+       }
+
+       /** When this method is called, the writer must write the entire visual scene.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeVisualScene ( const COLLADAFW::VisualScene* visualScene ) 
+       {
+               // This method is guaranteed to be called _after_ writeGeometry, writeMaterial, etc.
+
+               // for each <node> in <visual_scene>:
+               // create an Object
+               // if Mesh (previously created in writeGeometry) to which <node> corresponds exists, link Object with that mesh
+
+               // update: since we cannot link a Mesh with Object in
+               // writeGeometry because <geometry> does not reference <node>,
+               // we link Objects with Meshes here
+
+               // TODO: create a new scene except the selected <visual_scene> - use current blender
+               // scene for it
+               Scene *sce = CTX_data_scene(mContext);
+
+               for (int i = 0; i < visualScene->getRootNodes().getCount(); i++) {
+                       COLLADAFW::Node *node = visualScene->getRootNodes()[i];
+                       const COLLADAFW::Node::NodeType& type = node->getType();
+
+                       write_node(node, NULL, sce, NULL);
+               }
+
+               armature_importer.make_armatures(mContext);
+               
+               return true;
+       }
+
+       /** When this method is called, the writer must handle all nodes contained in the 
+               library nodes.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeLibraryNodes ( const COLLADAFW::LibraryNodes* libraryNodes ) 
+       {
+               return true;
+       }
+
+       /** When this method is called, the writer must write the geometry.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeGeometry ( const COLLADAFW::Geometry* geom ) 
+       {
+               return mesh_importer.write_geometry(geom);
+       }
+
+       /** When this method is called, the writer must write the material.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeMaterial( const COLLADAFW::Material* cmat ) 
+       {
+               const std::string& str_mat_id = cmat->getOriginalId();
+               Material *ma = add_material((char*)str_mat_id.c_str());
+               
+               this->uid_effect_map[cmat->getInstantiatedEffect()] = ma;
+               this->uid_material_map[cmat->getUniqueId()] = ma;
+               
+               return true;
+       }
+       
+       // create mtex, create texture, set texture image
+       MTex *create_texture(COLLADAFW::EffectCommon *ef, COLLADAFW::Texture &ctex, Material *ma,
+                                                int i, TexIndexTextureArrayMap &texindex_texarray_map)
+       {
+               COLLADAFW::SamplerPointerArray& samp_array = ef->getSamplerPointerArray();
+               COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()];
+                       
+               const COLLADAFW::UniqueId& ima_uid = sampler->getSourceImage();
+               
+               if (uid_image_map.find(ima_uid) == uid_image_map.end()) {
+                       fprintf(stderr, "Couldn't find an image by UID.\n");
+                       return NULL;
+               }
+               
+               ma->mtex[i] = add_mtex();
+               ma->mtex[i]->texco = TEXCO_UV;
+               ma->mtex[i]->tex = add_texture("texture");
+               ma->mtex[i]->tex->type = TEX_IMAGE;
+               ma->mtex[i]->tex->imaflag &= ~TEX_USEALPHA;
+               ma->mtex[i]->tex->ima = uid_image_map[ima_uid];
+               
+               texindex_texarray_map[ctex.getTextureMapId()].push_back(ma->mtex[i]);
+               
+               return ma->mtex[i];
+       }
+       
+       void write_profile_COMMON(COLLADAFW::EffectCommon *ef, Material *ma)
+       {
+               COLLADAFW::EffectCommon::ShaderType shader = ef->getShaderType();
+               
+               // blinn
+               if (shader == COLLADAFW::EffectCommon::SHADER_BLINN) {
+                       ma->spec_shader = MA_SPEC_BLINN;
+                       ma->spec = ef->getShininess().getFloatValue();
+               }
+               // phong
+               else if (shader == COLLADAFW::EffectCommon::SHADER_PHONG) {
+                       ma->spec_shader = MA_SPEC_PHONG;
+                       // XXX setting specular hardness instead of specularity intensity
+                       ma->har = ef->getShininess().getFloatValue() * 4;
+               }
+               // lambert
+               else if (shader == COLLADAFW::EffectCommon::SHADER_LAMBERT) {
+                       ma->diff_shader = MA_DIFF_LAMBERT;
+               }
+               // default - lambert
+               else {
+                       ma->diff_shader = MA_DIFF_LAMBERT;
+                       fprintf(stderr, "Current shader type is not supported.\n");
+               }
+               // reflectivity
+               ma->ray_mirror = ef->getReflectivity().getFloatValue();
+               // index of refraction
+               ma->ang = ef->getIndexOfRefraction().getFloatValue();
+               
+               int i = 0;
+               COLLADAFW::Color col;
+               COLLADAFW::Texture ctex;
+               MTex *mtex = NULL;
+               TexIndexTextureArrayMap texindex_texarray_map;
+               
+               // DIFFUSE
+               // color
+               if (ef->getDiffuse().isColor()) {
+                       col = ef->getDiffuse().getColor();
+                       ma->r = col.getRed();
+                       ma->g = col.getGreen();
+                       ma->b = col.getBlue();
+               }
+               // texture
+               else if (ef->getDiffuse().isTexture()) {
+                       ctex = ef->getDiffuse().getTexture(); 
+                       mtex = create_texture(ef, ctex, ma, i, texindex_texarray_map);
+                       if (mtex != NULL) {
+                               mtex->mapto = MAP_COL;
+                               ma->texact = (int)i;
+                               i++;
+                       }
+               }
+               // AMBIENT
+               // color
+               if (ef->getAmbient().isColor()) {
+                       col = ef->getAmbient().getColor();
+                       ma->ambr = col.getRed();
+                       ma->ambg = col.getGreen();
+                       ma->ambb = col.getBlue();
+               }
+               // texture
+               else if (ef->getAmbient().isTexture()) {
+                       ctex = ef->getAmbient().getTexture(); 
+                       mtex = create_texture(ef, ctex, ma, i, texindex_texarray_map);
+                       if (mtex != NULL) {
+                               mtex->mapto = MAP_AMB; 
+                               i++;
+                       }
+               }
+               // SPECULAR
+               // color
+               if (ef->getSpecular().isColor()) {
+                       col = ef->getSpecular().getColor();
+                       ma->specr = col.getRed();
+                       ma->specg = col.getGreen();
+                       ma->specb = col.getBlue();
+               }
+               // texture
+               else if (ef->getSpecular().isTexture()) {
+                       ctex = ef->getSpecular().getTexture(); 
+                       mtex = create_texture(ef, ctex, ma, i, texindex_texarray_map);
+                       if (mtex != NULL) {
+                               mtex->mapto = MAP_SPEC; 
+                               i++;
+                       }
+               }
+               // REFLECTIVE
+               // color
+               if (ef->getReflective().isColor()) {
+                       col = ef->getReflective().getColor();
+                       ma->mirr = col.getRed();
+                       ma->mirg = col.getGreen();
+                       ma->mirb = col.getBlue();
+               }
+               // texture
+               else if (ef->getReflective().isTexture()) {
+                       ctex = ef->getReflective().getTexture(); 
+                       mtex = create_texture(ef, ctex, ma, i, texindex_texarray_map);
+                       if (mtex != NULL) {
+                               mtex->mapto = MAP_REF; 
+                               i++;
+                       }
+               }
+               // EMISSION
+               // color
+               if (ef->getEmission().isColor()) {
+                       // XXX there is no emission color in blender
+                       // but I am not sure
+               }
+               // texture
+               else if (ef->getEmission().isTexture()) {
+                       ctex = ef->getEmission().getTexture(); 
+                       mtex = create_texture(ef, ctex, ma, i, texindex_texarray_map);
+                       if (mtex != NULL) {
+                               mtex->mapto = MAP_EMIT; 
+                               i++;
+                       }
+               }
+               // TRANSPARENT
+               // color
+       //      if (ef->getOpacity().isColor()) {
+//                     // XXX don't know what to do here
+//             }
+//             // texture
+//             else if (ef->getOpacity().isTexture()) {
+//                     ctex = ef->getOpacity().getTexture();
+//                     if (mtex != NULL) mtex->mapto &= MAP_ALPHA;
+//                     else {
+//                             mtex = create_texture(ef, ctex, ma, i, texindex_texarray_map);
+//                             if (mtex != NULL) mtex->mapto = MAP_ALPHA;
+//                     }
+//             }
+               material_texture_mapping_map[ma] = texindex_texarray_map;
+       }
+       
+       /** When this method is called, the writer must write the effect.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       
+       virtual bool writeEffect( const COLLADAFW::Effect* effect ) 
+       {
+               
+               const COLLADAFW::UniqueId& uid = effect->getUniqueId();
+               if (uid_effect_map.find(uid) == uid_effect_map.end()) {
+                       fprintf(stderr, "Couldn't find a material by UID.\n");
+                       return true;
+               }
+               
+               Material *ma = uid_effect_map[uid];
+               
+               COLLADAFW::CommonEffectPointerArray common_efs = effect->getCommonEffects();
+               if (common_efs.getCount() < 1) {
+                       fprintf(stderr, "Couldn't find <profile_COMMON>.\n");
+                       return true;
+               }
+               // XXX TODO: Take all <profile_common>s
+               // Currently only first <profile_common> is supported
+               COLLADAFW::EffectCommon *ef = common_efs[0];
+               write_profile_COMMON(ef, ma);
+               
+               return true;
+       }
+       
+       
+       /** When this method is called, the writer must write the camera.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeCamera( const COLLADAFW::Camera* camera ) 
+       {
+               Camera *cam = NULL;
+               std::string cam_id, cam_name;
+               
+               cam_id = camera->getOriginalId();
+               cam_name = camera->getName();
+               if (cam_name.size()) cam = (Camera*)add_camera((char*)cam_name.c_str());
+               else cam = (Camera*)add_camera((char*)cam_id.c_str());
+               
+               if (!cam) {
+                       fprintf(stderr, "Cannot create camera. \n");
+                       return true;
+               }
+               cam->clipsta = camera->getNearClippingPlane().getValue();
+               cam->clipend = camera->getFarClippingPlane().getValue();
+               
+               COLLADAFW::Camera::CameraType type = camera->getCameraType();
+               switch(type) {
+               case COLLADAFW::Camera::ORTHOGRAPHIC:
+                       {
+                               cam->type = CAM_ORTHO;
+                       }
+                       break;
+               case COLLADAFW::Camera::PERSPECTIVE:
+                       {
+                               cam->type = CAM_PERSP;
+                       }
+                       break;
+               case COLLADAFW::Camera::UNDEFINED_CAMERATYPE:
+                       {
+                               fprintf(stderr, "Current camera type is not supported. \n");
+                               cam->type = CAM_PERSP;
+                       }
+                       break;
+               }
+               this->uid_camera_map[camera->getUniqueId()] = cam;
+               // XXX import camera options
+               return true;
+       }
+
+       /** When this method is called, the writer must write the image.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeImage( const COLLADAFW::Image* image ) 
+       {
+               // XXX maybe it is necessary to check if the path is absolute or relative
+           const std::string& filepath = image->getImageURI().toNativePath();
+               const char *filename = (const char*)mFilename.c_str();
+               char dir[FILE_MAX];
+               char full_path[FILE_MAX];
+               
+               BLI_split_dirfile_basic(filename, dir, NULL);
+               BLI_join_dirfile(full_path, dir, filepath.c_str());
+               Image *ima = BKE_add_image_file(full_path, 0);
+               if (!ima) {
+                       fprintf(stderr, "Cannot create image. \n");
+                       return true;
+               }
+               this->uid_image_map[image->getUniqueId()] = ima;
+               
+               return true;
+       }
+
+       /** When this method is called, the writer must write the light.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeLight( const COLLADAFW::Light* light ) 
+       {
+               Lamp *lamp = NULL;
+               std::string la_id, la_name;
+               
+               la_id = light->getOriginalId();
+               la_name = light->getName();
+               if (la_name.size()) lamp = (Lamp*)add_lamp((char*)la_name.c_str());
+               else lamp = (Lamp*)add_lamp((char*)la_id.c_str());
+               
+               if (!lamp) {
+                       fprintf(stderr, "Cannot create lamp. \n");
+                       return true;
+               }
+               if (light->getColor().isValid()) {
+                       COLLADAFW::Color col = light->getColor();
+                       lamp->r = col.getRed();
+                       lamp->g = col.getGreen();
+                       lamp->b = col.getBlue();
+               }
+               COLLADAFW::Light::LightType type = light->getLightType();
+               switch(type) {
+               case COLLADAFW::Light::AMBIENT_LIGHT:
+                       {
+                               lamp->type = LA_HEMI;
+                       }
+                       break;
+               case COLLADAFW::Light::SPOT_LIGHT:
+                       {
+                               lamp->type = LA_SPOT;
+                               lamp->falloff_type = LA_FALLOFF_SLIDERS;
+                               lamp->att1 = light->getLinearAttenuation().getValue();
+                               lamp->att2 = light->getQuadraticAttenuation().getValue();
+                               lamp->spotsize = light->getFallOffAngle().getValue();
+                               lamp->spotblend = light->getFallOffExponent().getValue();
+                       }
+                       break;
+               case COLLADAFW::Light::DIRECTIONAL_LIGHT:
+                       {
+                               lamp->type = LA_SUN;
+                       }
+                       break;
+               case COLLADAFW::Light::POINT_LIGHT:
+                       {
+                               lamp->type = LA_LOCAL;
+                               lamp->att1 = light->getLinearAttenuation().getValue();
+                               lamp->att2 = light->getQuadraticAttenuation().getValue();
+                       }
+                       break;
+               case COLLADAFW::Light::UNDEFINED:
+                       {
+                               fprintf(stderr, "Current lamp type is not supported. \n");
+                               lamp->type = LA_LOCAL;
+                       }
+                       break;
+               }
+                       
+               this->uid_lamp_map[light->getUniqueId()] = lamp;
+               return true;
+       }
+       
+       // this function is called only for animations that pass COLLADAFW::validate
+       virtual bool writeAnimation( const COLLADAFW::Animation* anim ) 
+       {
+               return anim_importer.write_animation(anim);
+       }
+       
+       // called on post-process stage after writeVisualScenes
+       virtual bool writeAnimationList( const COLLADAFW::AnimationList* animationList ) 
+       {
+               return anim_importer.write_animation_list(animationList);
+       }
+       
+       /** When this method is called, the writer must write the skin controller data.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeSkinControllerData( const COLLADAFW::SkinControllerData* skin ) 
+       {
+               return armature_importer.write_skin_controller_data(skin);
+       }
+
+       // this is called on postprocess, before writeVisualScenes
+       virtual bool writeController( const COLLADAFW::Controller* controller ) 
+       {
+               return armature_importer.write_controller(controller);
+       }
+
+       virtual bool writeFormulas( const COLLADAFW::Formulas* formulas )
+       {
+               return true;
+       }
+
+       virtual bool writeKinematicsScene( const COLLADAFW::KinematicsScene* kinematicsScene )
+       {
+               return true;
+       }
+};
+
+void DocumentImporter::import(bContext *C, const char *filename)
+{
+       Writer w(C, filename);
+       w.write();
+}
diff --git a/source/blender/collada/DocumentImporter.h b/source/blender/collada/DocumentImporter.h
new file mode 100644 (file)
index 0000000..5dee101
--- /dev/null
@@ -0,0 +1,8 @@
+struct Main;
+struct bContext;
+
+class DocumentImporter
+{
+ public:
+       void import(bContext *C, const char *filename);
+};
diff --git a/source/blender/collada/SConscript b/source/blender/collada/SConscript
new file mode 100644 (file)
index 0000000..7bf2870
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/python
+Import ('env')
+
+sources = env.Glob('*.cpp')
+
+# relative paths to include dirs, space-separated, string
+incs = '../blenlib ../blenkernel ../windowmanager ../makesdna ../makesrna ../editors/include ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter/include [OPENCOLLADA]/COLLADABaseUtils/include [OPENCOLLADA]/COLLADAFramework/include [OPENCOLLADA]/COLLADASaxFrameworkLoader/include '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA_INC'])
+
+env.BlenderLib ('bf_collada', sources, Split(incs), [], libtype='core', priority=200 )
+
diff --git a/source/blender/collada/collada.cpp b/source/blender/collada/collada.cpp
new file mode 100644 (file)
index 0000000..5aed51c
--- /dev/null
@@ -0,0 +1,26 @@
+#include "BKE_main.h"
+#include "BKE_scene.h"
+#include "BKE_context.h"
+
+#include "DocumentExporter.h"
+#include "DocumentImporter.h"
+
+extern "C"
+{
+       int collada_import(bContext *C, const char *filepath)
+       {
+               DocumentImporter imp;
+               imp.import(C, filepath);
+
+               return 1;
+       }
+
+       int collada_export(Scene *sce, const char *filepath)
+       {
+
+               DocumentExporter exp;
+               exp.exportCurrentScene(sce, filepath);
+
+               return 1;
+       }
+}
diff --git a/source/blender/collada/collada.h b/source/blender/collada/collada.h
new file mode 100644 (file)
index 0000000..cccca07
--- /dev/null
@@ -0,0 +1,19 @@
+#ifndef BLENDER_COLLADA_H
+#define BLENDER_COLLADA_H
+
+struct bContext;
+struct Scene;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+       /*
+        * both return 1 on success, 0 on error
+        */
+       int collada_import(bContext *C, const char *filepath);
+       int collada_export(Scene *sce, const char *filepath);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/source/blender/collada/collada_internal.h b/source/blender/collada/collada_internal.h
new file mode 100644 (file)
index 0000000..c0d7450
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef BLENDER_COLLADA_H
+#define BLENDER_COLLADA_H
+
+#include "COLLADAFWFileInfo.h"
+#include "Math/COLLADABUMathMatrix4.h"
+
+class UnitConverter
+{
+private:
+       COLLADAFW::FileInfo::Unit unit;
+       COLLADAFW::FileInfo::UpAxisType up_axis;
+
+public:
+
+       UnitConverter() : unit(), up_axis(COLLADAFW::FileInfo::Z_UP) {}
+
+       void read_asset(const COLLADAFW::FileInfo* asset)
+       {
+       }
+
+       // TODO
+       // convert vector vec from COLLADA format to Blender
+       void convertVec3(float *vec)
+       {
+       }
+               
+       // TODO need also for angle conversion, time conversion...
+
+       void mat4_from_dae(float out[][4], const COLLADABU::Math::Matrix4& in)
+       {
+               // in DAE, matrices use columns vectors, (see comments in COLLADABUMathMatrix4.h)
+               // so here, to make a blender matrix, we swap columns and rows
+               for (int i = 0; i < 4; i++) {
+                       for (int j = 0; j < 4; j++) {
+                               out[i][j] = in[j][i];
+                       }
+               }
+       }
+
+       void mat4_to_dae(float out[][4], float in[][4])
+       {
+               Mat4CpyMat4(out, in);
+               Mat4Transp(out);
+       }
+
+       void mat4_to_dae_double(double out[][4], float in[][4])
+       {
+               float mat[4][4];
+
+               mat4_to_dae(mat, in);
+
+               for (int i = 0; i < 4; i++)
+                       for (int j = 0; j < 4; j++)
+                               out[i][j] = mat[i][j];
+       }
+};
+
+class TransformBase
+{
+public:
+       void decompose(float mat[][4], float *loc, float *rot, float *size)
+       {
+               Mat4ToSize(mat, size);
+               Mat4ToEul(mat, rot);
+               VecCopyf(loc, mat[3]);
+       }
+};
+
+#endif
index 7deac8a4aa0fb89b65ae683d04699238e2bc1cb4..ec489e5261a21c99eeada8fe586d8dd08cac7497 100644 (file)
@@ -50,6 +50,10 @@ IF(WITH_OPENEXR)
        ADD_DEFINITIONS(-DWITH_OPENEXR)
 ENDIF(WITH_OPENEXR)
 
+IF(WITH_OPENCOLLADA)
+       ADD_DEFINITIONS(-DWITH_COLLADA)
+ENDIF(WITH_OPENCOLLADA)
+
 IF(WITH_QUICKTIME)
        SET(INC ${INC} ../quicktime ${QUICKTIME_INC})
        ADD_DEFINITIONS(-DWITH_QUICKTIME)
index 08a291871f1398f2b558c26b0c0662e8880df2e2..c01649485a575e6b57e1e4ecc82905f78da65b12 100644 (file)
@@ -19,6 +19,9 @@ defs = []
 if not env['WITH_BF_PYTHON']:
        defs.append('DISABLE_PYTHON')
 
+if env['WITH_BF_COLLADA']:
+       defs.append('WITH_COLLADA')
+
 if env['OURPLATFORM'] == 'linux2':
        cflags='-pthread'
        incs += ' ../../../extern/binreloc/include'
index f82e16b7951dbce7f626743d206b7a918080b856..1ab6d0b352ed77b7b6cf03417144ca4deb422f54 100644 (file)
@@ -1345,6 +1345,105 @@ static void WM_OT_save_mainfile(wmOperatorType *ot)
 }
 
 
+/* XXX: move these collada operators to a more appropriate place */
+#ifdef WITH_COLLADA
+
+#include "../../collada/collada.h"
+
+static int wm_collada_export_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       //char name[FILE_MAX];
+       //BLI_strncpy(name, G.sce, FILE_MAX);
+       //untitled(name);
+
+       /* RNA_string_set(op->ptr, "path", "/tmp/test.dae"); */
+       
+       WM_event_add_fileselect(C, op);
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+/* function used for WM_OT_save_mainfile too */
+static int wm_collada_export_exec(bContext *C, wmOperator *op)
+{
+       char filename[FILE_MAX];
+       
+       if(RNA_property_is_set(op->ptr, "path"))
+               RNA_string_get(op->ptr, "path", filename);
+       else {
+               BLI_strncpy(filename, G.sce, FILE_MAX);
+               untitled(filename);
+       }
+       
+       //WM_write_file(C, filename, op->reports);
+       collada_export(CTX_data_scene(C), filename);
+       
+       /* WM_event_add_notifier(C, NC_WM|ND_FILESAVE, NULL); */
+
+       return OPERATOR_FINISHED;
+}
+
+static void WM_OT_collada_export(wmOperatorType *ot)
+{
+       ot->name= "Export COLLADA";
+       ot->idname= "WM_OT_collada_export";
+       
+       ot->invoke= wm_collada_export_invoke;
+       ot->exec= wm_collada_export_exec;
+       ot->poll= WM_operator_winactive;
+       
+       ot->flag= 0;
+       
+       RNA_def_property(ot->srna, "path", PROP_STRING, PROP_FILEPATH);
+}
+
+static int wm_collada_import_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       /* RNA_string_set(op->ptr, "path", "/tmp/test.dae"); */
+       
+       WM_event_add_fileselect(C, op);
+
+       return OPERATOR_RUNNING_MODAL;
+}
+
+/* function used for WM_OT_save_mainfile too */
+static int wm_collada_import_exec(bContext *C, wmOperator *op)
+{
+       char filename[FILE_MAX];
+       
+       if(RNA_property_is_set(op->ptr, "path"))
+               RNA_string_get(op->ptr, "path", filename);
+       else {
+               BLI_strncpy(filename, G.sce, FILE_MAX);
+               untitled(filename);
+       }
+       
+       //WM_write_file(C, filename, op->reports);
+       collada_import(C, filename);
+       
+       /* WM_event_add_notifier(C, NC_WM|ND_FILESAVE, NULL); */
+
+       return OPERATOR_FINISHED;
+}
+
+static void WM_OT_collada_import(wmOperatorType *ot)
+{
+       ot->name= "Import COLLADA";
+       ot->idname= "WM_OT_collada_import";
+       
+       ot->invoke= wm_collada_import_invoke;
+       ot->exec= wm_collada_import_exec;
+       ot->poll= WM_operator_winactive;
+       
+       ot->flag= 0;
+       
+       RNA_def_property(ot->srna, "path", PROP_STRING, PROP_FILEPATH);
+}
+
+#endif
+
+
+
 /* *********************** */
 
 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
@@ -2248,6 +2347,13 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_debug_menu);
        WM_operatortype_append(WM_OT_search_menu);
        WM_operatortype_append(WM_OT_call_menu);
+
+#ifdef WITH_COLLADA
+       /* XXX: move these */
+       WM_operatortype_append(WM_OT_collada_export);
+       WM_operatortype_append(WM_OT_collada_import);
+#endif
+
 }
 
 /* default keymap for windows and screens, only call once per WM */
index fa59324c5dc9279c479a8814a2fa21dd9806b3ba..61c0fe187fd9f9e101576a7e897bb71ce62c8df3 100644 (file)
@@ -378,7 +378,8 @@ IF(UNIX)
                bf_cineon 
                bf_openexr 
                bf_dds
-               bf_readblenfile 
+               bf_readblenfile
+               bf_collada
                blender_bop 
                bf_kernel 
                bf_decimation 
index 8a2ef93bb26bee0e027088a11c02b3bb38acc787..94ad485e17606cd408a0ad997b55b489d0fd9469 100644 (file)
@@ -161,6 +161,13 @@ def setup_staticlibs(lenv):
        if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
                libincs += Split(lenv['BF_PTHREADS_LIBPATH'])
 
+       if lenv['WITH_BF_COLLADA']:
+               libincs += Split(lenv['BF_OPENCOLLADA_LIBPATH'])
+               if lenv['OURPLATFORM'] not in ('win32-vc', 'win32-mingw', 'linuxcross', 'win64-vc'):
+                       libincs += Split(lenv['BF_PCRE_LIBPATH'])
+                       libincs += Split(lenv['BF_EXPAT_LIBPATH'])
+
+
        return statlibs, libincs
 
 def setup_syslibs(lenv):
@@ -211,6 +218,10 @@ def setup_syslibs(lenv):
                syslibs += Split(lenv['BF_PTHREADS_LIB'])
        if lenv['WITH_BF_LCMS']:
                syslibs.append(lenv['BF_LCMS_LIB'])
+       if lenv['WITH_BF_COLLADA']:
+               syslibs.append(lenv['BF_PCRE_LIB'])
+               syslibs += Split(lenv['BF_OPENCOLLADA_LIB'])
+               syslibs.append(lenv['BF_EXPAT_LIB'])
 
 
        syslibs += lenv['LLIBS']
index cacff349dcfc56c6ab181f0e1c0919e6ed55a342..4f554a6901b55a25f1209d17d64a3e3f82df9f1e 100755 (executable)
@@ -53,6 +53,7 @@ def validate_arguments(args, bc):
                        'WITH_BF_QUICKTIME', 'BF_QUICKTIME', 'BF_QUICKTIME_INC', 'BF_QUICKTIME_LIB', 'BF_QUICKTIME_LIBPATH',
                        'WITH_BF_FFTW3', 'BF_FFTW3', 'BF_FFTW3_INC', 'BF_FFTW3_LIB', 'BF_FFTW3_LIBPATH',
                        'WITH_BF_STATICOPENGL', 'BF_OPENGL', 'BF_OPENGL_INC', 'BF_OPENGL_LIB', 'BF_OPENGL_LIBPATH', 'BF_OPENGL_LIB_STATIC',
+                       'WITH_BF_COLLADA', 'BF_COLLADA', 'BF_COLLADA_INC', 'BF_COLLADA_LIB', 'BF_OPENCOLLADA', 'BF_OPENCOLLADA_INC', 'BF_OPENCOLLADA_LIB', 'BF_OPENCOLLADA_LIBPATH', 'BF_PCRE', 'BF_PCRE_LIB', 'BF_PCRE_LIBPATH', 'BF_EXPAT', 'BF_EXPAT_LIB', 'BF_EXPAT_LIBPATH',
                        'WITH_BF_PLAYER',
                        'WITH_BF_NOBLENDER',
                        'WITH_BF_BINRELOC',
@@ -333,6 +334,21 @@ def read_opts(cfg, args):
                ('BF_OPENGL_LIBPATH', 'OpenGL library path', ''),
                ('BF_OPENGL_LIB_STATIC', 'OpenGL static libraries', ''),
                ('BF_OPENGL_LINKFLAGS', 'OpenGL link flags', ''),
+
+               (BoolVariable('WITH_BF_COLLADA', 'Build COLLADA import/export module if true', True)),
+               ('BF_COLLADA', 'COLLADA base path', ''),
+               ('BF_COLLADA_INC', 'COLLADA include path', ''),
+               ('BF_COLLADA_LIB', 'COLLADA library', ''),
+               ('BF_OPENCOLLADA', 'OpenCollada base path', ''),
+               ('BF_OPENCOLLADA_INC', 'OpenCollada base include path', ''),
+               ('BF_OPENCOLLADA_LIB', 'OpenCollada library', ''),
+               ('BF_OPENCOLLADA_LIBPATH', 'OpenCollada library path', ''),
+               ('BF_PCRE', 'PCRE base path', ''),
+               ('BF_PCRE_LIB', 'PCRE library', ''),
+               ('BF_PCRE_LIBPATH', 'PCRE library path', ''),
+               ('BF_EXPAT', 'Expat base path', ''),
+               ('BF_EXPAT_LIB', 'Expat library', ''),
+               ('BF_EXPAT_LIBPATH', 'Expat library path', ''),
                
                (BoolVariable('WITH_BF_PLAYER', 'Build blenderplayer if true', False)),
                (BoolVariable('WITH_BF_NOBLENDER', 'Do not build blender if true', False)),