Merge with 2.5 -r 21003:21788.
authorArystanbek Dyussenov <arystan.d@gmail.com>
Wed, 22 Jul 2009 05:35:12 +0000 (05:35 +0000)
committerArystanbek Dyussenov <arystan.d@gmail.com>
Wed, 22 Jul 2009 05:35:12 +0000 (05:35 +0000)
Run smoothly :)

14 files changed:
config/linux2-config.py
source/blender/SConscript
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/editors/include/ED_object.h
source/blender/editors/object/object_edit.c
source/blender/windowmanager/intern/wm_operators.c
tools/Blender.py
tools/btools.py

index b6a1eeb2c441021e2daf05a5efd2625ece84d06e..5724617e307a29d10221ccbf2989d98be9959572 100644 (file)
@@ -145,6 +145,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 = True
+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 a064850c170deae9b1355b2208cbe6168c72bc1e..e12d627461cc3c0e2a338da5f7ffef4db4943dd2 100644 (file)
@@ -32,3 +32,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/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp
new file mode 100644 (file)
index 0000000..c711c6b
--- /dev/null
@@ -0,0 +1,1319 @@
+#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"
+
+extern "C" 
+{
+#include "BKE_DerivedMesh.h"
+}
+#include "BKE_scene.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+
+#include "BLI_arithb.h"
+#include "BLI_string.h"
+
+#include "DocumentExporter.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 <vector>
+#include <algorithm> // std::find
+
+// TODO: this can handy in BLI_arith.b
+// 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;
+       }
+}
+
+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;
+}
+
+std::string id_name(void *id)
+{
+       return ((ID*)id)->name + 2;
+}
+
+/*
+  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);
+               }
+               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?
+               
+               DerivedMesh *dm = mesh_get_derived_final(mScene, ob, CD_MASK_BAREMESH);
+               Mesh *me = (Mesh*)ob->data;
+               std::string geom_name(id_name(ob));
+               
+               // openMesh(geoId, geoName, meshId)
+               openMesh(geom_name, "", "");
+               
+               // writes <source> for vertex coords
+               createVertsSource(geom_name, dm);
+               
+               // writes <source> for normal coords
+               createNormalsSource(geom_name, dm);
+
+               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_name, dm, (Mesh*)ob->data);
+               }
+               // <vertices>
+               COLLADASW::Vertices verts(mSW);
+               verts.setId(getIdBySemantics(geom_name, COLLADASW::VERTEX));
+               COLLADASW::InputList &input_list = verts.getInputList();
+               COLLADASW::Input input(COLLADASW::POSITION,
+                                                          getUrlBySemantics(geom_name, 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, dm, geom_name);
+                       }
+               }
+               else {
+                       createPolylist(false, 0, has_uvs, ob, dm, geom_name);
+               }
+               
+               closeMesh();
+               closeGeometry();
+               
+               dm->release(dm);
+               
+       }
+
+       // 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,
+                                               DerivedMesh *dm,
+                                               std::string& geom_name)
+       {
+               MFace *mfaces = dm->getFaceArray(dm);
+               int totfaces = dm->getNumFaces(dm);
+               Mesh *me = (Mesh*)ob->data;
+
+               // <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_name, COLLADASW::VERTEX), 0);
+                       
+               // creates <input> in <polylist> for normals
+               COLLADASW::Input input2(COLLADASW::NORMAL, getUrlBySemantics
+                                                               (geom_name, 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_name, 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_name, DerivedMesh *dm)
+       {
+               int totverts = dm->getNumVerts(dm);
+               MVert *verts = dm->getVertArray(dm);
+               
+               
+               COLLADASW::FloatSourceF source(mSW);
+               source.setId(getIdBySemantics(geom_name, COLLADASW::POSITION));
+               source.setArrayId(getIdBySemantics(geom_name, 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_name, int layer_index)
+       {
+               char suffix[20];
+               sprintf(suffix, "-%d", layer_index);
+               return getIdBySemantics(geom_name, COLLADASW::TEXCOORD) + suffix;
+       }
+
+       //creates <source> for texcoords
+       void createTexcoordsSource(std::string geom_name, DerivedMesh *dm, Mesh *me)
+       {
+
+               int totfaces = dm->getNumFaces(dm);
+               MFace *mfaces = dm->getFaceArray(dm);
+               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_name, 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_name, DerivedMesh *dm)
+       {
+               int totverts = dm->getNumVerts(dm);
+               MVert *verts = dm->getVertArray(dm);
+               
+               COLLADASW::FloatSourceF source(mSW);
+               source.setId(getIdBySemantics(geom_name, COLLADASW::NORMAL));
+               source.setArrayId(getIdBySemantics(geom_name, 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_name, COLLADASW::Semantics type, std::string other_suffix = "") {
+               return geom_name + getSuffixBySemantic(type) + other_suffix;
+       }
+       
+       
+       COLLADASW::URI getUrlBySemantics(std::string geom_name, COLLADASW::Semantics type, std::string other_suffix = "") {
+               
+               std::string id(getIdBySemantics(geom_name, 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 SceneExporter: COLLADASW::LibraryVisualScenes
+{
+public:
+       SceneExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryVisualScenes(sw) {}
+       
+       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();
+       }
+
+       // called for each object
+       //void operator()(Object *ob) {
+       void writeNodes(Object *ob, Scene *sce) {
+               
+               COLLADASW::Node node(mSW);
+               node.setNodeId(ob->id.name);
+               node.setType(COLLADASW::Node::NODE);
+
+               std::string ob_name(id_name(ob));
+
+               node.start();
+               node.addTranslate("location", ob->loc[0], ob->loc[1], ob->loc[2]);
+               
+               // this code used to create a single <rotate> representing object rotation
+               // float quat[4];
+               // float axis[3];
+               // float angle;
+               // double angle_deg;
+               // EulToQuat(ob->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);
+
+               float *rot = ob->rot;
+               node.addRotateX("rotationX", COLLADABU::Math::Utils::radToDegF(rot[0]));
+               node.addRotateY("rotationY", COLLADABU::Math::Utils::radToDegF(rot[1]));
+               node.addRotateZ("rotationZ", COLLADABU::Math::Utils::radToDegF(rot[2]));
+
+               node.addScale("scale", ob->size[0], ob->size[1], ob->size[2]);
+               
+               // <instance_geometry>
+               if (ob->type == OB_MESH) {
+                       COLLADASW::InstanceGeometry instGeom(mSW);
+                       instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, ob_name));
+                       
+                       for(int a = 0; a < ob->totcol; a++)     {
+                               Material *ma = give_current_material(ob, a+1);
+                               
+                               COLLADASW::BindMaterial& bm = instGeom.getBindMaterial();
+                               COLLADASW::InstanceMaterialList& iml = bm.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);
+                               }
+                       }
+                       
+                       instGeom.add();
+               }
+               
+               // <instance_camera>
+               else if (ob->type == OB_CAMERA) {
+                       COLLADASW::InstanceCamera instCam(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, ob_name));
+                       instCam.add();
+               }
+               
+               // <instance_light>
+               else if (ob->type == OB_LAMP) {
+                       COLLADASW::InstanceLight instLa(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, ob_name));
+                       instLa.add();
+               }
+               // empty object
+               else if (ob->type == OB_EMPTY) {
+               }
+               
+               // write node for child object
+               Base *b = (Base*) sce->base.first;
+               while(b) {
+                       
+                       Object *cob = b->object;
+                       
+                       if ((cob->type == OB_MESH || cob->type == OB_CAMERA || cob->type == OB_LAMP || cob->type == OB_EMPTY) && cob->parent == ob) {
+                               // write node...
+                               writeNodes(cob, sce);
+                       }
+                       b = b->next;
+               }
+               
+               node.end();
+       }
+
+       void exportHierarchy(Scene *sce)
+       {
+               Base *base= (Base*) sce->base.first;
+               while(base) {
+                       Object *ob = base->object;
+                       
+                       if ((ob->type == OB_MESH || ob->type == OB_CAMERA || ob->type == OB_LAMP || ob->type == OB_EMPTY) && !ob->parent) {
+                               // write nodes....
+                               writeNodes(ob, sce);
+                               
+                       }
+                       base= base->next;
+               }
+       }
+
+};
+
+class ImagesExporter: COLLADASW::LibraryImages
+{
+       std::vector<std::string> mImages; // contains list of written images, to avoid duplicates
+public:
+       ImagesExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryImages(sw)
+       {}
+       
+       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));
+
+                               if (find(mImages.begin(), mImages.end(), name) == mImages.end()) {
+                                       COLLADASW::Image img(COLLADABU::URI(COLLADABU::URI::nativePathToUri(image->name)), 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);
+               }
+               else if (ma->spec_shader == MA_SPEC_PHONG) {
+                       ep.setShaderType(COLLADASW::EffectProfile::PHONG);
+               }
+               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);
+               // shininess
+               ep.setShininess(ma->spec);
+               // emission
+               COLLADASW::ColorOrTexture cot = getcol(0.0f, 0.0f, 0.0f, 1.0f);
+               ep.setEmission(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];
+
+               // 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::Surface::SURFACE_SID_SUFFIX);
+
+                               // 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 TEXTCO_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, surface));
+                       }
+                       // ambient
+                       if (t->mapto & MAP_AMB) {
+                               ep.setAmbient(createTexture(ima, uvname, sampler, surface));
+                       }
+                       // specular
+                       if (t->mapto & MAP_SPEC) {
+                               ep.setSpecular(createTexture(ima, uvname, sampler, surface));
+                       }
+                       // emission
+                       if (t->mapto & MAP_EMIT) {
+                               ep.setEmission(createTexture(ima, uvname, sampler, surface));
+                       }
+                       // reflective
+                       if (t->mapto & MAP_REF) {
+                               ep.setReflective(createTexture(ima, uvname, sampler, surface));
+                       }
+               }
+               // 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){
+                               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)
+       {
+               // XXX add other params later
+               Camera *cam = (Camera*)ob->data;
+               std::string cam_name(id_name(ob));
+               if (cam->type == CAM_PERSP) {
+                       COLLADASW::PerspectiveOptic persp(mSW);
+                       persp.setXFov(1.0);
+                       //persp.setYFov(1.0);
+                       persp.setAspectRatio(1.0);
+                       persp.setZFar(cam->clipend);
+                       persp.setZNear(cam->clipsta);
+                       COLLADASW::Camera ccam(mSW, &persp, cam_name);
+                       addCamera(ccam);
+               }
+               else {
+                       COLLADASW::OrthographicOptic ortho(mSW);
+                       ortho.setXMag(1.0);
+                       //ortho.setYMag(1.0, true);
+                       ortho.setAspectRatio(1.0);
+                       ortho.setZFar(cam->clipend);
+                       ortho.setZNear(cam->clipsta);
+                       COLLADASW::Camera ccam(mSW, &ortho, 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_name(id_name(ob));
+               COLLADASW::Color col(la->r, la->g, la->b);
+               
+               // sun
+               if (la->type == LA_SUN) {
+                       COLLADASW::DirectionalLight cla(mSW, la_name, la->energy);
+                       cla.setColor(col);
+                       addLight(cla);
+               }
+               // hemi
+               else if (la->type == LA_HEMI) {
+                       COLLADASW::AmbientLight cla(mSW, la_name, la->energy);
+                       cla.setColor(col);
+                       addLight(cla);
+               }
+               // spot
+               // XXX add other params later
+               else if (la->type == LA_SPOT) {
+                       COLLADASW::SpotLight cla(mSW, la_name, la->energy);
+                       cla.setColor(col);
+                       addLight(cla);
+               }
+               // lamp
+               else if (la->type != LA_AREA) {
+                       COLLADASW::PointLight cla(mSW, la_name, la->energy);
+                       cla.setColor(col);
+                       addLight(cla);
+               }
+               else {
+                       // XXX write error
+                       return;
+               }
+       }
+};
+
+// 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;
+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, 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, char *axis_name)
+       {
+               std::string source_id = anim_id + get_semantic_suffix(semantic);
+
+               bool is_rotation = !strcmp(fcu->rna_path, "rotation");
+
+               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, 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, char *axis_name)
+       {
+               if (!strcmp(rna_path, "rotation"))
+                       return std::string(rna_path) + axis_name;
+
+               return std::string(rna_path) + "." + axis_name;
+       }
+
+       void add_animation(FCurve *fcu, const char *ob_name)
+       {
+               static char *axis_names[] = {"X", "Y", "Z"};
+               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", ob_name, 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 = std::string(ob_name) + "/" + get_transform_sid(fcu->rna_path, axis_name);
+               addChannel(COLLADABU::URI(empty, sampler_id), target);
+
+               closeAnimation();
+       }
+
+       // called for each exported object
+       void operator() (Object *ob) 
+       {
+               if (!ob->adt || !ob->adt->action) return;
+
+               // XXX this needs to be handled differently?
+               if (ob->type == OB_ARMATURE) return;
+
+               FCurve *fcu = (FCurve*)ob->adt->action->curves.first;
+               while (fcu) {
+
+                       if (!strcmp(fcu->rna_path, "location") ||
+                               !strcmp(fcu->rna_path, "scale") ||
+                               !strcmp(fcu->rna_path, "rotation")) {
+
+                               add_animation(fcu, ob->id.name);
+                       }
+
+                       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("meter", 1.0);
+       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);
+       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_visual_scenes>
+       SceneExporter se(&sw);
+       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..6676a06
--- /dev/null
@@ -0,0 +1,1468 @@
+#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 "COLLADAFWRotate.h"
+#include "COLLADAFWSampler.h"
+#include "COLLADAFWScale.h"
+#include "COLLADAFWSkinController.h"
+#include "COLLADAFWTransformation.h"
+#include "COLLADAFWTranslate.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 "BKE_main.h"
+#include "BKE_customdata.h"
+#include "BKE_library.h"
+#include "BKE_texture.h"
+#include "ED_keyframing.h"
+#include "BKE_fcurve.h"
+#include "BKE_depsgraph.h"
+}
+#include "DNA_lamp_types.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 "BLI_arithb.h"
+#include "BLI_listbase.h"
+#include "BLI_string.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 <string>
+#include <map>
+
+
+// #define COLLADA_DEBUG
+
+char *CustomData_get_layer_name(const struct CustomData *data, int type, int n);
+
+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";
+}
+
+/*
+
+  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;
+       
+       std::vector<COLLADAFW::VisualScene> mVisualScenes;
+
+       bContext *mContext;
+
+       std::map<COLLADAFW::UniqueId, Mesh*> uid_mesh_map; // geometry unique id-to-mesh map
+       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;
+       // maps for assigning textures to uv layers
+       std::map<COLLADAFW::TextureMapId, char*> set_layername_map;
+       std::map<COLLADAFW::TextureMapId, std::vector<MTex*> > index_mtex_map;
+       // this structure is used to assign material indices to faces
+       // when materials are assigned to an object
+       struct Primitive {
+               MFace *mface;
+               int totface;
+       };
+       typedef std::map<COLLADAFW::MaterialId, std::vector<Primitive> > MaterialIdPrimitiveArrayMap;
+       // amazing name!
+       std::map<COLLADAFW::UniqueId, MaterialIdPrimitiveArrayMap> geom_uid_mat_mapping_map;
+       // maps for animation
+       std::map<COLLADAFW::UniqueId, std::vector<FCurve*> > uid_fcurve_map;
+       struct AnimatedTransform {
+               Object *ob;
+               // COLLADAFW::Node *node;
+               COLLADAFW::Transformation *tm; // which transform is animated by an AnimationList->id
+       };
+       // Nodes don't share AnimationLists (Arystan)
+       std::map<COLLADAFW::UniqueId, AnimatedTransform> uid_animated_map; // AnimationList->uniqueId to AnimatedObject map
+
+       class UnitConverter
+       {
+       private:
+               COLLADAFW::FileInfo::Unit mUnit;
+               COLLADAFW::FileInfo::UpAxisType mUpAxis;
+       public:
+               UnitConverter(COLLADAFW::FileInfo::UpAxisType upAxis, COLLADAFW::FileInfo::Unit& unit) :
+                       mUpAxis(upAxis), mUnit(unit)
+               {
+               }
+
+               // TODO
+               // convert vector vec from COLLADA format to Blender
+               void convertVec3(float *vec)
+               {
+               }
+               
+               // TODO need also for angle conversion, time conversion...
+       };
+
+       class UVDataWrapper
+       {
+               COLLADAFW::MeshVertexData *mVData;
+       public:
+               UVDataWrapper(COLLADAFW::MeshVertexData& vdata) : mVData(&vdata)
+               {}
+
+#ifdef COLLADA_DEBUG
+               void print()
+               {
+                       fprintf(stderr, "UVs:\n");
+                       COLLADAFW::ArrayPrimitiveType<float>* values = mVData->getFloatValues();
+                       for (int i = 0; i < values->getCount(); i += 2) {
+                               fprintf(stderr, "%.1f, %.1f\n", (*values)[i], (*values)[i+1]);
+                       }
+                       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();
+                                       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();
+                                       
+                                       uv[0] = (float)(*values)[uv_index[0]];
+                                       uv[1] = (float)(*values)[uv_index[1]];
+                                       
+                                       break;
+                               }
+                       }
+               }
+       };
+
+public:
+
+       /** Constructor. */
+       Writer(bContext *C, const char *filename) : mContext(C), mFilename(filename) {};
+
+       /** 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()
+       {
+               // using mVisualScenes, do:
+               // - write <node> data to Objects: materials, transforms, etc.
+
+               // TODO: import materials (<instance_material> inside <instance_geometry>) and textures
+
+               std::vector<COLLADAFW::VisualScene>::iterator it = mVisualScenes.begin();
+               for (; it != mVisualScenes.end(); it++) {
+                       COLLADAFW::VisualScene &visscene = *it;
+
+                       // create new blender scene
+
+                       // create Objects from <node>s inside this <visual_scene>
+
+                       // link each Object with a Mesh
+                       // for each Object's <instance_geometry> there should already exist a Mesh
+               }
+       }
+
+       /** 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();
+
+               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;
+       }
+       
+       // bind early created mesh to object, assign materials and textures
+       Object *create_mesh_object(Object *ob, Scene *sce, COLLADAFW::Node *node,
+                                                          COLLADAFW::InstanceGeometry *geom)
+       {
+               ob = add_object(sce, OB_MESH);
+               
+               const std::string& id = node->getOriginalId();
+               if (id.length())
+                       rename_id(&ob->id, (char*)id.c_str());
+               
+               const COLLADAFW::UniqueId& geom_uid = geom->getInstanciatedObjectId();
+               if (uid_mesh_map.find(geom_uid) == uid_mesh_map.end()) {
+                       // XXX report to user
+                       // 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;
+               }
+               // 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);
+               
+               // assign materials to object
+               // assign material indices to mesh faces
+               Mesh *me = (Mesh*)ob->data;
+               MTex *mtex = NULL;
+               MTFace *tface = NULL;
+               char *layername = CustomData_get_layer_name(&me->fdata, CD_MTFACE, 0);
+               
+               for (int k = 0; k < geom->getMaterialBindings().getCount(); k++) {
+                       
+                       const COLLADAFW::UniqueId& ma_uid = geom->getMaterialBindings()[k].getReferencedMaterial();
+                       // check if material was properly written to map
+                       if (uid_material_map.find(ma_uid) == uid_material_map.end()) {
+                               fprintf(stderr, "Cannot find material by UID.\n");
+                               continue;
+                       }
+                       Material *ma = uid_material_map[ma_uid];
+                       int l;
+                       
+                       // assign textures to uv layers
+                       // bvi_array "bind_vertex_input array"
+                       COLLADAFW::InstanceGeometry::TextureCoordinateBindingArray& bvi_array = 
+                               geom->getMaterialBindings()[k].getTextureCoordinateBindingArray();
+                       
+                       for (l = 0; l < bvi_array.getCount(); l++) {
+                               
+                               COLLADAFW::TextureMapId tex_index = bvi_array[l].textureMapId;
+                               size_t set_index = bvi_array[l].setIndex;
+                               char *uvname = set_layername_map[set_index];
+                               
+                               // check if mtexes were properly added to vector
+                               if (index_mtex_map.find(tex_index) == index_mtex_map.end()) {
+                                       fprintf(stderr, "Cannot find mtexes by texmap id.\n");
+                                       continue;
+                               }
+                               std::vector<MTex*> mtexes = index_mtex_map[tex_index];
+                               std::vector<MTex*>::iterator it;
+                               for (it = mtexes.begin(); it != mtexes.end(); it++) {
+                                       mtex = *it;
+                                       strcpy(mtex->uvname, uvname);
+                                       
+                               }       
+                       }
+                       mtex = NULL;
+                       // find and save texture mapped to diffuse
+                       for (l = 0; l < 18; l++) {
+                               if (ma->mtex[l] != NULL && ma->mtex[l]->mapto == MAP_COL)
+                                       mtex = ma->mtex[l];
+                       }
+                       // get mtface for first uv layer
+                       if (tface == NULL && mtex != NULL)
+                               tface = (MTFace*)CustomData_get_layer_named(&me->fdata, CD_MTFACE, mtex->uvname);
+                       // get mtface for next uv layer
+                       else if(layername != NULL && mtex != NULL && strcmp(layername, mtex->uvname) != 0) {
+                               tface = (MTFace*)CustomData_get_layer_named(&me->fdata, CD_MTFACE, mtex->uvname);
+                               layername = mtex->uvname;
+                       }
+                       
+                       assign_material(ob, ma, ob->totcol + 1);
+                       
+                       MaterialIdPrimitiveArrayMap& mat_prim_map = geom_uid_mat_mapping_map[geom_uid];
+                       COLLADAFW::MaterialId mat_id = geom->getMaterialBindings()[k].getMaterialId();
+                       
+                       // if there's geometry that uses this material,
+                       // set mface->mat_nr=k for each face in that geometry
+                       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;
+                                       l = 0;
+                                       while (l++ < prim.totface) {
+                                               prim.mface->mat_nr = k;
+                                               prim.mface++;
+                                               if (mtex != NULL && tface != NULL) {
+                                                       tface->tpage = (Image*)mtex->tex->ima;
+                                                       tface->mode = TF_TEX;
+                                                       tface++;
+                                               }
+                                       }
+                               }
+                       }
+               }
+               return ob;
+       }
+       
+       void write_node (COLLADAFW::Node *node, Scene *sce, Object *par = NULL)
+       {
+               // 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...
+               if (node->getType() != COLLADAFW::Node::NODE) return;
+               
+               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();
+               Object *ob = NULL;
+               int k;
+               
+               // if node has <instance_geometries> - connect mesh with object
+               // XXX currently only one <instance_geometry> in a node is supported
+               if (geom.getCount() != 0) {
+                       ob = create_mesh_object(ob, sce, node, geom[0]);
+               }
+               // checking all other possible instances
+               // <instance_camera>
+               else if (camera.getCount() != 0) {
+                       const COLLADAFW::UniqueId& cam_uid = camera[0]->getInstanciatedObjectId();
+                       if (uid_camera_map.find(cam_uid) == uid_camera_map.end()) {     
+                               fprintf(stderr, "Couldn't find camera by UID. \n");
+                               return;
+                       }
+                       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);
+               }
+               // <instance_light>
+               else if (lamp.getCount() != 0) {
+                       const COLLADAFW::UniqueId& lamp_uid = lamp[0]->getInstanciatedObjectId();
+                       if (uid_lamp_map.find(lamp_uid) == uid_lamp_map.end()) {        
+                               fprintf(stderr, "Couldn't find lamp by UID. \n");
+                               return;
+                       }
+                       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);
+               }
+               else if (controller.getCount() != 0) {
+                       //ob = create_mesh_object(ob, sce, node, controller[0]);
+                       return;
+               }
+               else if (inst_node.getCount() != 0) {
+                       return;
+               }
+               // if node has no instances - create empty object
+               else {
+                       ob = add_object(sce, OB_EMPTY);
+               }
+               // just checking if object wasn't created
+               if (ob == NULL) return;
+               // if par was given make this object child of the previous 
+               if (par != NULL) {
+                       Object workob;
+
+                       ob->parent = par;
+
+                       // doing what 'set parent' operator does
+                       par->recalc |= OB_RECALC_OB;
+                       ob->parsubstr[0] = 0;
+
+                       // since ob->obmat is identity, this is not needed?
+                       what_does_parent(sce, ob, &workob);
+                       Mat4Invert(ob->parentinv, workob.obmat);
+
+                       ob->recalc |= OB_RECALC_OB|OB_RECALC_DATA;
+                       ob->partype = PAROBJECT;
+                       DAG_scene_sort(sce);
+               }
+               // transform Object
+               float rot[3][3];
+               Mat3One(rot);
+               
+               // transform Object and store animation linking info
+               for (k = 0; k < node->getTransformations().getCount(); k ++) {
+                       
+                       COLLADAFW::Transformation *tm = node->getTransformations()[k];
+                       COLLADAFW::Transformation::TransformationType type = tm->getTransformationType();
+
+                       switch(type) {
+                       case COLLADAFW::Transformation::TRANSLATE:
+                               {
+                                       COLLADAFW::Translate *tra = (COLLADAFW::Translate*)tm;
+                                       COLLADABU::Math::Vector3& t = tra->getTranslation();
+                                       ob->loc[0] = (float)t[0];
+                                       ob->loc[1] = (float)t[1];
+                                       ob->loc[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);
+                                       
+                                       QuatToMat3(quat, mat);
+                                       Mat3CpyMat3(rot_copy, rot);
+                                       Mat3MulMat3(rot, rot_copy, mat);
+                               }
+                               break;
+                       case COLLADAFW::Transformation::SCALE:
+                               {
+                                       COLLADABU::Math::Vector3& s = ((COLLADAFW::Scale*)tm)->getScale();
+                                       ob->size[0] = (float)s[0];
+                                       ob->size[1] = (float)s[1];
+                                       ob->size[2] = (float)s[2];
+                               }
+                               break;
+                       case COLLADAFW::Transformation::MATRIX:
+                       case COLLADAFW::Transformation::LOOKAT:
+                       case COLLADAFW::Transformation::SKEW:
+                               fprintf(stderr, "MATRIX, LOOKAT and SKEW transformations are not supported yet.\n");
+                               break;
+                       }
+                       
+                       // AnimationList that drives this Transformation
+                       const COLLADAFW::UniqueId& anim_list_id = tm->getAnimationList();
+                       
+                       // store this so later we can link animation data with ob
+                       AnimatedTransform anim = {ob, tm};
+                       this->uid_animated_map[anim_list_id] = anim;
+               }
+               Mat3ToEul(rot, ob->rot);
+               
+               // if node has child nodes write them
+               COLLADAFW::NodePointerArray &child_nodes = node->getChildNodes();
+               for (k = 0; k < child_nodes.getCount(); k++) {  
+                       
+                       COLLADAFW::Node *child_node = child_nodes[k];
+                       write_node(child_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];
+                       
+                       if (node->getType() != COLLADAFW::Node::NODE) {
+                               continue;
+                       }
+                       
+                       write_node(node, sce);
+               }
+               
+               mVisualScenes.push_back(*visualScene);
+
+               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;
+       }
+
+       // utility functions
+
+       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];
+       }
+
+       // 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, 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
+
+       /** 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* cgeom ) 
+       {
+               // - create a mesh object
+               // - write geometry
+
+               // - ignore usupported primitive types
+               
+               // 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 (cgeom->getType() != COLLADAFW::Geometry::GEO_TYPE_MESH) {
+                       // TODO: report warning
+                       fprintf(stderr, "Mesh type %s is not supported\n", geomTypeToStr(cgeom->getType()));
+                       return true;
+               }
+               
+               COLLADAFW::Mesh *cmesh = (COLLADAFW::Mesh*)cgeom;
+               
+               // first check if we can import this mesh
+               COLLADAFW::MeshPrimitiveArray& prim_arr = cmesh->getMeshPrimitives();
+               int i;
+               
+               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 && count != 4) {
+                                               fprintf(stderr, "%s has at least one face with vertex count > 4 or < 3\n",
+                                                               type_str);
+                                               return true;
+                                       }
+                               }
+                                       
+                       }
+                       else if(type != COLLADAFW::MeshPrimitive::TRIANGLES) {
+                               fprintf(stderr, "Primitive type %s is not supported.\n", type_str);
+                               return true;
+                       }
+               }
+               
+               size_t totvert = cmesh->getPositions().getFloatValues()->getCount() / 3;
+               
+               const std::string& str_geom_id = cgeom->getOriginalId();
+               Mesh *me = add_mesh((char*)str_geom_id.c_str());
+
+               // store mesh ptr
+               // to link it later with Object
+               this->uid_mesh_map[cgeom->getUniqueId()] = me;
+               
+               // vertices     
+               me->mvert = (MVert*)CustomData_add_layer(&me->vdata, CD_MVERT, CD_CALLOC, NULL, totvert);
+               me->totvert = totvert;
+               
+               float *pos_float_array = cmesh->getPositions().getFloatValues()->getData();
+               
+               MVert *mvert = me->mvert;
+               i = 0;
+               while (i < totvert) {
+                       // fill mvert
+                       mvert->co[0] = pos_float_array[0];
+                       mvert->co[1] = pos_float_array[1];
+                       mvert->co[2] = pos_float_array[2];
+
+                       pos_float_array += 3;
+                       mvert++;
+                       i++;
+               }
+
+               // count totface
+               int totface = cmesh->getFacesCount();
+
+               // allocate faces
+               me->mface = (MFace*)CustomData_add_layer(&me->fdata, CD_MFACE, CD_CALLOC, NULL, totface);
+               me->totface = totface;
+               
+               // UVs
+               int totuvset = cmesh->getUVCoords().getInputInfosArray().getCount();
+               
+               for (i = 0; i < totuvset; i++) {
+                       // add new CustomData layer
+                       CustomData_add_layer(&me->fdata, CD_MTFACE, CD_CALLOC, NULL, totface);
+                       this->set_layername_map[i] = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i);
+                       
+               }
+
+               // activate the first uv layer if any
+               if (totuvset) me->mtface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, 0);
+
+               UVDataWrapper uvs(cmesh->getUVCoords());
+
+#ifdef COLLADA_DEBUG
+               uvs.print();
+#endif
+
+               // read faces
+               MFace *mface = me->mface;
+
+               MaterialIdPrimitiveArrayMap mat_prim_map;
+
+               // TODO: import uv set names
+               int face_index = 0;
+
+               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];
+
+                                       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++;
+                               }
+                       }
+                       
+                       mat_prim_map[mp->getMaterialId()].push_back(prim);
+                       
+               }
+               
+               geom_uid_mat_mapping_map[cgeom->getUniqueId()] = mat_prim_map;
+               
+               mesh_calc_normals(me->mvert, me->totvert, me->mface, me->totface, NULL);
+               make_edges(me, 0);
+
+               return true;
+       }
+
+       /** 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)
+       {
+               COLLADAFW::SamplerPointerArray& samp_array = ef->getSamplerPointerArray();
+               COLLADAFW::Sampler *sampler = samp_array[ctex.getSamplerId()];
+               
+               if (sampler->getSamplerType() == COLLADAFW::Sampler::SAMPLER_TYPE_2D) {
+                       
+                       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->ima = uid_image_map[ima_uid];
+                       index_mtex_map[ctex.getTextureMapId()].push_back(ma->mtex[i]);
+                       return ma->mtex[i];
+               }
+       }
+
+       /** 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, "<effect> hasn't got <profile_COMMON>s.\n Currently we support only them. \n");
+                       return true;
+               }
+               // XXX TODO: Take all <profile_common>s
+               // Currently only first <profile_common> is supported
+               COLLADAFW::EffectCommon *ef = common_efs[0];
+               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;
+                       ma->spec = ef->getShininess().getFloatValue();
+               }
+               // 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;
+               
+               // 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);
+                       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);
+                       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);
+                       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);
+                       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);
+                       if (mtex != NULL) {
+                               mtex->mapto = MAP_EMIT; 
+                               i++;
+                       }
+               }
+               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 ) 
+       {
+               std::string name = camera->getOriginalId();
+               Camera *cam = (Camera*)add_camera((char*)name.c_str());
+               if (cam != NULL)
+                       this->uid_camera_map[camera->getUniqueId()] = cam;
+               else fprintf(stderr, "Cannot create camera. \n");
+               // 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 ) 
+       {
+           const std::string& filepath = image->getImageURI().toNativePath();
+               Image *ima = BKE_add_image_file((char*)filepath.c_str(), 0);
+               if (ima == NULL)
+                       fprintf(stderr, "Cannot create image. \n");
+               else
+                       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 ) 
+       {
+               std::string name = light->getOriginalId();
+               Lamp *lamp = (Lamp*)add_lamp((char*)name.c_str());
+               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;
+                       }
+                       break;
+               case COLLADAFW::Light::DIRECTIONAL_LIGHT:
+                       {
+                               lamp->type = LA_SUN;
+                       }
+                       break;
+               case COLLADAFW::Light::POINT_LIGHT:
+                       {
+                               lamp->type = LA_AREA;
+                       }
+                       break;
+               case COLLADAFW::Light::UNDEFINED:
+                       {
+                               fprintf(stderr, "Current lamp type is not supported. \n");
+                               lamp->type = LA_LOCAL;
+                       }
+                       break;
+               }
+                       
+               if (lamp != NULL)
+                       this->uid_lamp_map[light->getUniqueId()] = lamp;
+               else fprintf(stderr, "Cannot create lamp. \n");
+               // XXX import light options*/
+               return true;
+       }
+       
+       float get_float(COLLADAFW::FloatOrDoubleArray array, int i)
+       {
+               switch(array.getType()) {
+               case COLLADAFW::MeshVertexData::DATA_TYPE_FLOAT:
+                       {
+                               COLLADAFW::ArrayPrimitiveType<float> *values = array.getFloatValues();
+                               return (*values)[i];
+                       }
+               case COLLADAFW::MeshVertexData::DATA_TYPE_DOUBLE:
+                       {
+                               COLLADAFW::ArrayPrimitiveType<double> *values = array.getDoubleValues();
+                               return (float)(*values)[i];
+                       }
+               }
+       }
+       
+       void write_curves(const COLLADAFW::Animation* anim,
+                                         COLLADAFW::AnimationCurve *curve,
+                                         COLLADAFW::FloatOrDoubleArray input,
+                                         COLLADAFW::FloatOrDoubleArray output,
+                                         COLLADAFW::FloatOrDoubleArray intan,
+                                         COLLADAFW::FloatOrDoubleArray outtan, size_t dim, float fps)
+       {
+               int i;
+               if (dim == 1) {
+                       // create fcurve
+                       FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
+                       if (!fcu) {
+                               fprintf(stderr, "Cannot create fcurve. \n");
+                               return;
+                       }
+                       char *path = "location";
+                       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(intan, i + i) * fps;
+                               bez.vec[0][1] = get_float(intan, i + i + 1);
+                               // input, output
+                               bez.vec[1][0] = get_float(input, i) * fps;
+                               bez.vec[1][1] = get_float(output, i);
+                               // outtangent
+                               bez.vec[2][0] = get_float(outtan, i + i) * fps;
+                               bez.vec[2][1] = get_float(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);
+                               calchandles_fcurve(fcu);
+                       }
+                       // map fcurve to animation's UID
+                       this->uid_fcurve_map[anim->getUniqueId()].push_back(fcu);
+               }
+               else if(dim == 3) {
+                       for (i = 0; i < dim; i++ ) {
+                               // create fcurve
+                               FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
+                               if (!fcu) {
+                                       fprintf(stderr, "Cannot create fcurve. \n");
+                                       continue;
+                               }
+                               fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+                               fcu->rna_path = "location";
+                               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(intan, j * 6 + i + i) * fps;
+                                       bez.vec[0][1] = get_float(intan, j * 6 + i + i + 1);
+                                       // input, output
+                                       bez.vec[1][0] = get_float(input, j) * fps; 
+                                       bez.vec[1][1] = get_float(output, j * 3 + i);
+                                       // outtangent
+                                       bez.vec[2][0] = get_float(outtan, j * 6 + i + i) * fps;
+                                       bez.vec[2][1] = get_float(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);
+                                       calchandles_fcurve(fcu);
+                               }
+                               // map fcurve to animation's UID
+                               this->uid_fcurve_map[anim->getUniqueId()].push_back(fcu);
+                               
+                       }
+               }
+       }
+       
+       // this function is called only for animations that pass COLLADAFW::validate
+       virtual bool writeAnimation( const COLLADAFW::Animation* anim ) 
+       {
+               if (anim->getAnimationType() == COLLADAFW::Animation::ANIMATION_CURVE) {
+                       COLLADAFW::AnimationCurve *curve = (COLLADAFW::AnimationCurve*)anim;
+                       Scene *scene = CTX_data_scene(mContext);
+                       float fps = (float)FPS;
+                       // I wonder how do we use this (Arystan)
+                       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
+                                       write_curves(anim, curve, input, output, intan, outtan, dim, fps);
+                                       break;
+                               case COLLADAFW::AnimationCurve::INTERPOLATION_BEZIER:
+                                       // and this
+                                       write_curves(anim, 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;
+       }
+       
+       void change_fcurve(Object *ob, const COLLADAFW::UniqueId& anim_id, char *rna_path, int array_index)
+       {
+               if (uid_fcurve_map.find(anim_id) == uid_fcurve_map.end()) {
+                       fprintf(stderr, "Cannot find fcurves by UID.\n");
+                       return;
+               }
+               ID *id = &ob->id;
+               bAction *act;
+               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*> fcurves = uid_fcurve_map[anim_id];
+               std::vector<FCurve*>::iterator it;
+               int i = 0;
+               for (it = fcurves.begin(); it != fcurves.end(); it++) {
+                       fcu = *it;
+                       strcpy(fcu->rna_path, rna_path);
+                       if (array_index == -1)
+                               fcu->array_index = i;
+                       else
+                               fcu->array_index = array_index;
+                       // convert degrees to radians for rotation
+                       if (strcmp(rna_path, "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;
+                               }
+                       }
+                       i++;
+                       BLI_addtail(&act->curves, fcu);
+               }
+       }
+       
+       // called on post-process stage after writeVisualScenes
+       virtual bool writeAnimationList( 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;
+               }
+               
+               // what does this AnimationList animate?
+               AnimatedTransform& animated = uid_animated_map[anim_list_id];
+               char *loc = "location";
+               char *rotate = "rotation";
+               char *scale = "scale";
+               Object *ob = animated.ob;
+               
+               const COLLADAFW::AnimationList::AnimationBindings& bindings = animationList->getAnimationBindings();
+               switch (animated.tm->getTransformationType()) {
+               case COLLADAFW::Transformation::TRANSLATE:
+                       {
+                               for (int i = 0; i < bindings.getCount(); i++) {
+                                       const COLLADAFW::AnimationList::AnimationBinding& binding = bindings[i];
+                                       COLLADAFW::UniqueId anim_uid = binding.animation;
+                                       
+                                       switch (binding.animationClass) {
+                                       case COLLADAFW::AnimationList::POSITION_X:
+                                               change_fcurve(ob, anim_uid, loc, 0);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Y:
+                                               change_fcurve(ob, anim_uid, loc, 1);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Z:
+                                               change_fcurve(ob, anim_uid, loc, 2);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_XYZ:
+                                               change_fcurve(ob, anim_uid, loc, -1);
+                                               break;
+                                       default:
+                                               fprintf(stderr, "AnimationClass %d is not supported for TRANSLATE transformation.\n", binding.animationClass);
+                                       }
+                               }
+                       }
+                       break;
+               case COLLADAFW::Transformation::ROTATE:
+                       {
+                               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;
+                                       
+                                       switch (binding.animationClass) {
+                                       case COLLADAFW::AnimationList::ANGLE:
+                                               if (COLLADABU::Math::Vector3::UNIT_X == axis) {
+                                                       change_fcurve(ob, anim_uid, rotate, 0);
+                                               }
+                                               else if (COLLADABU::Math::Vector3::UNIT_Y == axis) {
+                                                       change_fcurve(ob, anim_uid, rotate, 1);
+                                               }
+                                               else if (COLLADABU::Math::Vector3::UNIT_Z == axis) {
+                                                       change_fcurve(ob, anim_uid, rotate, 2);
+                                               }
+                                               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:
+                       {
+                               // 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;
+                                       
+                                       switch (binding.animationClass) {
+                                       case COLLADAFW::AnimationList::POSITION_X:
+                                               change_fcurve(ob, anim_uid, scale, 0);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Y:
+                                               change_fcurve(ob, anim_uid, scale, 1);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_Z:
+                                               change_fcurve(ob, anim_uid, scale, 2);
+                                               break;
+                                       case COLLADAFW::AnimationList::POSITION_XYZ:
+                                               change_fcurve(ob, anim_uid, scale, -1);
+                                               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;
+       }
+       
+       /** 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* skinControllerData ) 
+       {
+               // see COLLADAFW::validate for an example of how to use SkinControllerData
+               return true;
+       }
+
+       /** When this method is called, the writer must write the controller.
+               @return The writer should return true, if writing succeeded, false otherwise.*/
+       virtual bool writeController( const COLLADAFW::Controller* controller ) 
+       {
+               // if skin controller
+               if (controller->getControllerType() == COLLADAFW::Controller::CONTROLLER_TYPE_SKIN) {
+                       return true;
+               }
+               // if morph controller
+               else {
+                       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..10967a6
--- /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 ../makesdna ../makesrna ../editors/include ../../../intern/guardedalloc [OPENCOLLADA]/COLLADAStreamWriter/include [OPENCOLLADA]/COLLADABaseUtils/include [OPENCOLLADA]/COLLADAFramework/include [OPENCOLLADA]/COLLADASaxFrameworkLoader/include '.replace('[OPENCOLLADA]', env['BF_OPENCOLLADA'])
+
+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
index 8d4819d2d71015385cbe0137811dc899939f4679..d4c67afed8ccfa6345504775685ea4e5790c1aba 100644 (file)
@@ -45,6 +45,7 @@ struct ModifierData;
 /* object_edit.c */
 void ED_operatortypes_object(void);
 void ED_keymap_object(struct wmWindowManager *wm);
+struct Object *ED_object_add_type(struct bContext *C, int type);
 
        /* send your own notifier for select! */
 void ED_base_object_select(struct Base *base, short mode);
index 37453039cf57458f0e1e88e08a72f79fc6ed624c..6c645406d2e9ea9caa124645f963b902b485fff0 100644 (file)
@@ -291,6 +291,12 @@ static Object *object_add_type(bContext *C, int type)
        return ob;
 }
 
+/* for COLLADA imp/exp */
+Object *ED_object_add_type(bContext *C, int type)
+{
+       return object_add_type(C, type);
+}
+
 /* for object add operator */
 static int object_add_exec(bContext *C, wmOperator *op)
 {
index 5e60207f62d2a3a81b05951c72ca745a05632469..755ba87d080133b01a431707657b733b46704274 100644 (file)
@@ -387,6 +387,7 @@ int WM_operator_redo_popup(bContext *C, wmOperator *op)
        return OPERATOR_CANCELLED;
 }
 
+
 /* ***************** Debug menu ************************* */
 
 static uiBlock *wm_block_create_menu(bContext *C, ARegion *ar, void *arg_op)
@@ -532,6 +533,7 @@ static void WM_OT_search_menu(wmOperatorType *ot)
 }
 
 
+
 /* ************ window / screen operator definitions ************** */
 
 static void WM_OT_window_duplicate(wmOperatorType *ot)
@@ -757,6 +759,108 @@ static void WM_OT_save_mainfile(wmOperatorType *ot)
 }
 
 
+
+
+
+/* XXX: move these to a more appropriate place */
+#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);
+
+       // XXX: temporary
+       RNA_string_set(op->ptr, "filename", "/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, "filename"))
+               RNA_string_get(op->ptr, "filename", 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= "Collada Export";
+       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, "filename", PROP_STRING, PROP_FILEPATH);
+}
+
+static int wm_collada_import_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+       // XXX: temporary
+       RNA_string_set(op->ptr, "filename", "/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, "filename"))
+               RNA_string_get(op->ptr, "filename", 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= "Collada Import";
+       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, "filename", PROP_STRING, PROP_FILEPATH);
+}
+
+
+
+
+
 /* *********************** */
 
 static void WM_OT_window_fullscreen_toggle(wmOperatorType *ot)
@@ -1485,6 +1589,7 @@ void WM_OT_radial_control_partial(wmOperatorType *ot)
        RNA_def_int_vector(ot->srna, "initial_mouse", 2, NULL, INT_MIN, INT_MAX, "initial_mouse", "", INT_MIN, INT_MAX);
 }
 
+
 /* ************************** timer for testing ***************** */
 
 /* uses no type defines, fully local testing function anyway... ;) */
@@ -1596,6 +1701,10 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_ten_timer);
        WM_operatortype_append(WM_OT_debug_menu);
        WM_operatortype_append(WM_OT_search_menu);
+
+       /* XXX: move these */
+       WM_operatortype_append(WM_OT_collada_export);
+       WM_operatortype_append(WM_OT_collada_import);
 }
 
 /* default keymap for windows and screens, only call once per WM */
index 749fa55a83332b1174be67f0881f55752ef94993..613979dc359708afa5d2e86c45c9fbce4e8d3f1f 100644 (file)
@@ -148,6 +148,12 @@ 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'])
+               libincs += Split(lenv['BF_PCRE_LIBPATH'])
+        libincs += Split(lenv['BF_EXPAT_LIBPATH'])
+
+
        return statlibs, libincs
 
 def setup_syslibs(lenv):
@@ -189,8 +195,13 @@ def setup_syslibs(lenv):
                syslibs += Split(lenv['BF_OPENGL_LIB'])
        if lenv['OURPLATFORM'] in ('win32-vc', 'win32-mingw','linuxcross', 'win64-vc'):
                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_OPENCOLLADA_LIB'])
+               syslibs.append(lenv['BF_PCRE_LIB'])
+        syslibs.append(lenv['BF_EXPAT_LIB'])
 
 
        syslibs += lenv['LLIBS']
index 9603022deaa554f36a1d758f11fcbf560efd0e1a..5b1b7b89648ea68af8cc2ddddd185de393b04389 100755 (executable)
@@ -49,6 +49,7 @@ def validate_arguments(args, bc):
                        'WITH_BF_FREETYPE', 'BF_FREETYPE', 'BF_FREETYPE_INC', 'BF_FREETYPE_LIB', 'BF_FREETYPE_LIBPATH',
                        'WITH_BF_QUICKTIME', 'BF_QUICKTIME', 'BF_QUICKTIME_INC', 'BF_QUICKTIME_LIB', 'BF_QUICKTIME_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_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',
@@ -299,6 +300,20 @@ 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_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)),