Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArystanbek Dyussenov <arystan.d@gmail.com>2009-10-30 18:35:50 +0300
committerArystanbek Dyussenov <arystan.d@gmail.com>2009-10-30 18:35:50 +0300
commit31258507d047d6594a97fc99496855d6d1a83d5b (patch)
tree5195b48002554dfc4b03dbf64e1145dd94d419b0 /source/blender/collada/DocumentExporter.cpp
parent84eb897caa029f1dc8bd9e61298fa7bfb8ccc622 (diff)
Merge of the COLLADA GSoC branch into trunk.
COLLADA code is disabled by default (it has dependencies requiring manual install). SCons and CMake builds are supported on Windows and Linux, no Mac building yet. More on building COLLADA code: http://wiki.blender.org/index.php/User:Kazanbas/Building_Collada_Branch. The detailed command log of the merge (can be useful for educational purposes): branch=https://svn.blender.org/svnroot/bf-blender/branches/soc-2009-chingachgook # collada code svn copy $branch/source/blender/collada source/blender/collada # operator svn merge -c 20401,20955,21077,24077,24079 $branch/source/blender/windowmanager/intern/wm_operators.c source/blender/windowmanager/intern/wm_operators.c # menu svn merge -c 24079 $branch/release/scripts/ui/space_info.py release/scripts/ui/space_info.py # scons svn merge -c 20398 $branch/source/blender/SConscript source/blender/SConscript svn merge -c 20398,20691,20955,22726 $branch/tools/btools.py tools/btools.py svn merge -c 20691,20955,22726 $branch/tools/Blender.py tools/Blender.py svn merge -c 20398,20692,20955 $branch/config/linux2-config.py config/linux2-config.py svn merge -c 22726 $branch/config/win64-vc-config.py config/win64-vc-config.py svn merge -c 22726 $branch/config/win32-vc-config.py config/win32-vc-config.py svn merge -c 24077 $branch/source/blender/windowmanager/SConscript source/blender/windowmanager/SConscript # cmake svn merge -c 23319,23905,24077,24158 $branch/CMakeLists.txt CMakeLists.txt svn merge -c 23319 $branch/source/blender/CMakeLists.txt source/blender/CMakeLists.txt svn merge -c 23319 $branch/source/creator/CMakeLists.txt source/creator/CMakeLists.txt svn merge -c 23319 $branch/CMake/macros.cmake CMake/macros.cmake svn merge -c 24077 $branch/source/blender/windowmanager/CMakeLists.txt source/blender/windowmanager/CMakeLists.txt
Diffstat (limited to 'source/blender/collada/DocumentExporter.cpp')
-rw-r--r--source/blender/collada/DocumentExporter.cpp2137
1 files changed, 2137 insertions, 0 deletions
diff --git a/source/blender/collada/DocumentExporter.cpp b/source/blender/collada/DocumentExporter.cpp
new file mode 100644
index 00000000000..09db4ba062f
--- /dev/null
+++ b/source/blender/collada/DocumentExporter.cpp
@@ -0,0 +1,2137 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_image_types.h"
+#include "DNA_material_types.h"
+#include "DNA_texture_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_lamp_types.h"
+#include "DNA_anim_types.h"
+#include "DNA_action_types.h"
+#include "DNA_curve_types.h"
+#include "DNA_armature_types.h"
+#include "DNA_modifier_types.h"
+
+extern "C"
+{
+#include "BKE_DerivedMesh.h"
+#include "BKE_fcurve.h"
+#include "BLI_util.h"
+#include "BLI_fileops.h"
+#include "ED_keyframing.h"
+}
+
+#include "MEM_guardedalloc.h"
+
+#include "BKE_scene.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_action.h" // pose functions
+#include "BKE_armature.h"
+#include "BKE_image.h"
+#include "BKE_utildefines.h"
+
+#include "BLI_arithb.h"
+#include "BLI_string.h"
+#include "BLI_listbase.h"
+
+#include "COLLADASWAsset.h"
+#include "COLLADASWLibraryVisualScenes.h"
+#include "COLLADASWNode.h"
+#include "COLLADASWLibraryGeometries.h"
+#include "COLLADASWSource.h"
+#include "COLLADASWInstanceGeometry.h"
+#include "COLLADASWInputList.h"
+#include "COLLADASWPrimitves.h"
+#include "COLLADASWVertices.h"
+#include "COLLADASWLibraryAnimations.h"
+#include "COLLADASWLibraryImages.h"
+#include "COLLADASWLibraryEffects.h"
+#include "COLLADASWImage.h"
+#include "COLLADASWEffectProfile.h"
+#include "COLLADASWColorOrTexture.h"
+#include "COLLADASWParamTemplate.h"
+#include "COLLADASWParamBase.h"
+#include "COLLADASWSurfaceInitOption.h"
+#include "COLLADASWSampler.h"
+#include "COLLADASWScene.h"
+//#include "COLLADASWSurface.h"
+#include "COLLADASWTechnique.h"
+#include "COLLADASWTexture.h"
+#include "COLLADASWLibraryMaterials.h"
+#include "COLLADASWBindMaterial.h"
+#include "COLLADASWLibraryCameras.h"
+#include "COLLADASWLibraryLights.h"
+#include "COLLADASWInstanceCamera.h"
+#include "COLLADASWInstanceLight.h"
+#include "COLLADASWCameraOptic.h"
+#include "COLLADASWConstants.h"
+#include "COLLADASWLibraryControllers.h"
+#include "COLLADASWInstanceController.h"
+#include "COLLADASWBaseInputElement.h"
+
+#include "collada_internal.h"
+#include "DocumentExporter.h"
+
+#include <vector>
+#include <algorithm> // std::find
+
+// arithb.c now has QuatToAxisAngle too
+#if 0
+// This function assumes that quat is normalized.
+// The following document was used as reference:
+// http://www.euclideanspace.com/maths/geometry/rotations/conversions/quaternionToAngle/index.htm
+void QuatToAxisAngle(float *q, float *axis, float *angle)
+{
+ // quat to axis angle
+ *angle = 2 * acos(q[0]);
+ float divisor = sqrt(1 - q[0] * q[0]);
+
+ // test to avoid divide by zero, divisor is always positive
+ if (divisor < 0.001f ) {
+ axis[0] = 1.0f;
+ axis[1] = 0.0f;
+ axis[2] = 0.0f;
+ }
+ else {
+ axis[0] = q[1] / divisor;
+ axis[1] = q[2] / divisor;
+ axis[2] = q[3] / divisor;
+ }
+}
+#endif
+
+char *CustomData_get_layer_name(const struct CustomData *data, int type, int n)
+{
+ int layer_index = CustomData_get_layer_index(data, type);
+ if(layer_index < 0) return NULL;
+
+ return data->layers[layer_index+n].name;
+}
+
+char *CustomData_get_active_layer_name(const CustomData *data, int type)
+{
+ /* get the layer index of the active layer of type */
+ int layer_index = CustomData_get_active_layer_index(data, type);
+ if(layer_index < 0) return NULL;
+
+ return data->layers[layer_index].name;
+}
+
+static std::string id_name(void *id)
+{
+ return ((ID*)id)->name + 2;
+}
+
+static std::string get_geometry_id(Object *ob)
+{
+ return id_name(ob) + "-mesh";
+}
+
+static std::string get_light_id(Object *ob)
+{
+ return id_name(ob) + "-light";
+}
+
+static std::string get_camera_id(Object *ob)
+{
+ return id_name(ob) + "-camera";
+}
+
+static void replace_chars(char *str, char chars[], char with)
+{
+ char *ch, *p;
+
+ for (ch = chars; *ch; ch++) {
+ while ((p = strchr(str, *ch))) {
+ *p = with;
+ }
+ }
+}
+
+/*
+ Utilities to avoid code duplication.
+ Definition can take some time to understand, but they should be useful.
+*/
+
+// f should have
+// void operator()(Object* ob)
+template<class Functor>
+void forEachMeshObjectInScene(Scene *sce, Functor &f)
+{
+
+ Base *base= (Base*) sce->base.first;
+ while(base) {
+ Object *ob = base->object;
+
+ if (ob->type == OB_MESH && ob->data) {
+ f(ob);
+ }
+ base= base->next;
+
+ }
+}
+
+template<class Functor>
+void forEachObjectInScene(Scene *sce, Functor &f)
+{
+ Base *base= (Base*) sce->base.first;
+ while(base) {
+ Object *ob = base->object;
+
+ f(ob);
+
+ base= base->next;
+ }
+}
+
+template<class Functor>
+void forEachCameraObjectInScene(Scene *sce, Functor &f)
+{
+ Base *base= (Base*) sce->base.first;
+ while(base) {
+ Object *ob = base->object;
+
+ if (ob->type == OB_CAMERA && ob->data) {
+ f(ob, sce);
+ }
+ base= base->next;
+ }
+}
+
+template<class Functor>
+void forEachLampObjectInScene(Scene *sce, Functor &f)
+{
+ Base *base= (Base*) sce->base.first;
+ while(base) {
+ Object *ob = base->object;
+
+ if (ob->type == OB_LAMP && ob->data) {
+ f(ob);
+ }
+ base= base->next;
+ }
+}
+
+// used in forEachMaterialInScene
+template <class MaterialFunctor>
+class ForEachMaterialFunctor
+{
+ std::vector<std::string> mMat; // contains list of material names, to avoid duplicate calling of f
+ MaterialFunctor *f;
+public:
+ ForEachMaterialFunctor(MaterialFunctor *f) : f(f) { }
+ void operator ()(Object *ob)
+ {
+ int a;
+ for(a = 0; a < ob->totcol; a++) {
+
+ Material *ma = give_current_material(ob, a+1);
+
+ if (!ma) continue;
+
+ if (find(mMat.begin(), mMat.end(), id_name(ma)) == mMat.end()) {
+ (*this->f)(ma, ob);
+
+ mMat.push_back(id_name(ma));
+ }
+ }
+ }
+};
+
+// calls f for each unique material linked to each object in sce
+// f should have
+// void operator()(Material* ma)
+template<class Functor>
+void forEachMaterialInScene(Scene *sce, Functor &f)
+{
+ ForEachMaterialFunctor<Functor> matfunc(&f);
+ forEachMeshObjectInScene(sce, matfunc);
+}
+
+// OB_MESH is assumed
+std::string getActiveUVLayerName(Object *ob)
+{
+ Mesh *me = (Mesh*)ob->data;
+
+ int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+ if (num_layers)
+ return std::string(CustomData_get_active_layer_name(&me->fdata, CD_MTFACE));
+
+ return "";
+}
+
+// TODO: optimize UV sets by making indexed list with duplicates removed
+class GeometryExporter : COLLADASW::LibraryGeometries
+{
+ Scene *mScene;
+public:
+ GeometryExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryGeometries(sw) {}
+
+ void exportGeom(Scene *sce)
+ {
+ openLibrary();
+
+ mScene = sce;
+ forEachMeshObjectInScene(sce, *this);
+
+ closeLibrary();
+ }
+
+ void operator()(Object *ob)
+ {
+ // XXX don't use DerivedMesh, Mesh instead?
+
+#if 0
+ DerivedMesh *dm = mesh_get_derived_final(mScene, ob, CD_MASK_BAREMESH);
+#endif
+ Mesh *me = (Mesh*)ob->data;
+ std::string geom_id = get_geometry_id(ob);
+
+ // openMesh(geoId, geoName, meshId)
+ openMesh(geom_id);
+
+ // writes <source> for vertex coords
+ createVertsSource(geom_id, me);
+
+ // writes <source> for normal coords
+ createNormalsSource(geom_id, me);
+
+ int has_uvs = CustomData_has_layer(&me->fdata, CD_MTFACE);
+
+ // writes <source> for uv coords if mesh has uv coords
+ if (has_uvs) {
+ createTexcoordsSource(geom_id, (Mesh*)ob->data);
+ }
+ // <vertices>
+ COLLADASW::Vertices verts(mSW);
+ verts.setId(getIdBySemantics(geom_id, COLLADASW::VERTEX));
+ COLLADASW::InputList &input_list = verts.getInputList();
+ COLLADASW::Input input(COLLADASW::POSITION, getUrlBySemantics(geom_id, COLLADASW::POSITION));
+ input_list.push_back(input);
+ verts.add();
+
+ // XXX slow
+ if (ob->totcol) {
+ for(int a = 0; a < ob->totcol; a++) {
+ // account for NULL materials, this should not normally happen?
+ Material *ma = give_current_material(ob, a + 1);
+ createPolylist(ma != NULL, a, has_uvs, ob, geom_id);
+ }
+ }
+ else {
+ createPolylist(false, 0, has_uvs, ob, geom_id);
+ }
+
+ closeMesh();
+ closeGeometry();
+
+#if 0
+ dm->release(dm);
+#endif
+ }
+
+ // powerful because it handles both cases when there is material and when there's not
+ void createPolylist(bool has_material,
+ int material_index,
+ bool has_uvs,
+ Object *ob,
+ std::string& geom_id)
+ {
+#if 0
+ MFace *mfaces = dm->getFaceArray(dm);
+ int totfaces = dm->getNumFaces(dm);
+#endif
+ Mesh *me = (Mesh*)ob->data;
+ MFace *mfaces = me->mface;
+ int totfaces = me->totface;
+
+ // <vcount>
+ int i;
+ int faces_in_polylist = 0;
+ std::vector<unsigned long> vcount_list;
+
+ // count faces with this material
+ for (i = 0; i < totfaces; i++) {
+ MFace *f = &mfaces[i];
+
+ if ((has_material && f->mat_nr == material_index) || !has_material) {
+ faces_in_polylist++;
+ if (f->v4 == 0) {
+ vcount_list.push_back(3);
+ }
+ else {
+ vcount_list.push_back(4);
+ }
+ }
+ }
+
+ // no faces using this material
+ if (faces_in_polylist == 0) {
+ return;
+ }
+
+ Material *ma = has_material ? give_current_material(ob, material_index + 1) : NULL;
+ COLLADASW::Polylist polylist(mSW);
+
+ // sets count attribute in <polylist>
+ polylist.setCount(faces_in_polylist);
+
+ // sets material name
+ if (has_material)
+ polylist.setMaterial(id_name(ma));
+
+ COLLADASW::InputList &til = polylist.getInputList();
+
+ // creates <input> in <polylist> for vertices
+ COLLADASW::Input input1(COLLADASW::VERTEX, getUrlBySemantics
+ (geom_id, COLLADASW::VERTEX), 0);
+
+ // creates <input> in <polylist> for normals
+ COLLADASW::Input input2(COLLADASW::NORMAL, getUrlBySemantics
+ (geom_id, COLLADASW::NORMAL), 0);
+
+ til.push_back(input1);
+ til.push_back(input2);
+
+ // if mesh has uv coords writes <input> for TEXCOORD
+ int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+
+ for (i = 0; i < num_layers; i++) {
+ char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, i);
+ COLLADASW::Input input3(COLLADASW::TEXCOORD,
+ makeUrl(makeTexcoordSourceId(geom_id, i)),
+ 1, // offset always 1, this is only until we have optimized UV sets
+ i // set number equals UV layer index
+ );
+ til.push_back(input3);
+ }
+
+ // sets <vcount>
+ polylist.setVCountList(vcount_list);
+
+ // performs the actual writing
+ polylist.prepareToAppendValues();
+
+ // <p>
+ int texindex = 0;
+ for (i = 0; i < totfaces; i++) {
+ MFace *f = &mfaces[i];
+
+ if ((has_material && f->mat_nr == material_index) || !has_material) {
+
+ unsigned int *v = &f->v1;
+ for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) {
+ polylist.appendValues(v[j]);
+
+ if (has_uvs)
+ polylist.appendValues(texindex + j);
+ }
+ }
+
+ texindex += 3;
+ if (f->v4 != 0)
+ texindex++;
+ }
+
+ polylist.finish();
+ }
+
+ // creates <source> for positions
+ void createVertsSource(std::string geom_id, Mesh *me)
+ {
+#if 0
+ int totverts = dm->getNumVerts(dm);
+ MVert *verts = dm->getVertArray(dm);
+#endif
+ int totverts = me->totvert;
+ MVert *verts = me->mvert;
+
+ COLLADASW::FloatSourceF source(mSW);
+ source.setId(getIdBySemantics(geom_id, COLLADASW::POSITION));
+ source.setArrayId(getIdBySemantics(geom_id, COLLADASW::POSITION) +
+ ARRAY_ID_SUFFIX);
+ source.setAccessorCount(totverts);
+ source.setAccessorStride(3);
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ param.push_back("X");
+ param.push_back("Y");
+ param.push_back("Z");
+ /*main function, it creates <source id = "">, <float_array id = ""
+ count = ""> */
+ source.prepareToAppendValues();
+ //appends data to <float_array>
+ int i = 0;
+ for (i = 0; i < totverts; i++) {
+ source.appendValues(verts[i].co[0], verts[i].co[1], verts[i].co[2]);
+ }
+
+ source.finish();
+
+ }
+
+ std::string makeTexcoordSourceId(std::string& geom_id, int layer_index)
+ {
+ char suffix[20];
+ sprintf(suffix, "-%d", layer_index);
+ return getIdBySemantics(geom_id, COLLADASW::TEXCOORD) + suffix;
+ }
+
+ //creates <source> for texcoords
+ void createTexcoordsSource(std::string geom_id, Mesh *me)
+ {
+#if 0
+ int totfaces = dm->getNumFaces(dm);
+ MFace *mfaces = dm->getFaceArray(dm);
+#endif
+ int totfaces = me->totface;
+ MFace *mfaces = me->mface;
+
+ int totuv = 0;
+ int i;
+
+ // count totuv
+ for (i = 0; i < totfaces; i++) {
+ MFace *f = &mfaces[i];
+ if (f->v4 == 0) {
+ totuv+=3;
+ }
+ else {
+ totuv+=4;
+ }
+ }
+
+ int num_layers = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+
+ // write <source> for each layer
+ // each <source> will get id like meshName + "map-channel-1"
+ for (int a = 0; a < num_layers; a++) {
+ MTFace *tface = (MTFace*)CustomData_get_layer_n(&me->fdata, CD_MTFACE, a);
+ char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, a);
+
+ COLLADASW::FloatSourceF source(mSW);
+ std::string layer_id = makeTexcoordSourceId(geom_id, a);
+ source.setId(layer_id);
+ source.setArrayId(layer_id + ARRAY_ID_SUFFIX);
+
+ source.setAccessorCount(totuv);
+ source.setAccessorStride(2);
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ param.push_back("X");
+ param.push_back("Y");
+
+ source.prepareToAppendValues();
+
+ for (i = 0; i < totfaces; i++) {
+ MFace *f = &mfaces[i];
+
+ for (int j = 0; j < (f->v4 == 0 ? 3 : 4); j++) {
+ source.appendValues(tface[i].uv[j][0],
+ tface[i].uv[j][1]);
+ }
+ }
+
+ source.finish();
+ }
+ }
+
+
+ //creates <source> for normals
+ void createNormalsSource(std::string geom_id, Mesh *me)
+ {
+#if 0
+ int totverts = dm->getNumVerts(dm);
+ MVert *verts = dm->getVertArray(dm);
+#endif
+
+ int totverts = me->totvert;
+ MVert *verts = me->mvert;
+
+ COLLADASW::FloatSourceF source(mSW);
+ source.setId(getIdBySemantics(geom_id, COLLADASW::NORMAL));
+ source.setArrayId(getIdBySemantics(geom_id, COLLADASW::NORMAL) +
+ ARRAY_ID_SUFFIX);
+ source.setAccessorCount(totverts);
+ source.setAccessorStride(3);
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ param.push_back("X");
+ param.push_back("Y");
+ param.push_back("Z");
+
+ source.prepareToAppendValues();
+
+ int i = 0;
+
+ for( i = 0; i < totverts; ++i ){
+
+ source.appendValues(float(verts[i].no[0]/32767.0),
+ float(verts[i].no[1]/32767.0),
+ float(verts[i].no[2]/32767.0));
+
+ }
+ source.finish();
+ }
+
+ std::string getIdBySemantics(std::string geom_id, COLLADASW::Semantics type, std::string other_suffix = "") {
+ return geom_id + getSuffixBySemantic(type) + other_suffix;
+ }
+
+
+ COLLADASW::URI getUrlBySemantics(std::string geom_id, COLLADASW::Semantics type, std::string other_suffix = "") {
+
+ std::string id(getIdBySemantics(geom_id, type, other_suffix));
+ return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
+
+ }
+
+ COLLADASW::URI makeUrl(std::string id)
+ {
+ return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
+ }
+
+
+ /* int getTriCount(MFace *faces, int totface) {
+ int i;
+ int tris = 0;
+ for (i = 0; i < totface; i++) {
+ // if quad
+ if (faces[i].v4 != 0)
+ tris += 2;
+ else
+ tris++;
+ }
+
+ return tris;
+ }*/
+};
+
+class TransformWriter : protected TransformBase
+{
+protected:
+ void add_node_transform(COLLADASW::Node& node, float mat[][4], float parent_mat[][4])
+ {
+ float loc[3], rot[3], size[3];
+ float local[4][4];
+
+ if (parent_mat) {
+ float invpar[4][4];
+ Mat4Invert(invpar, parent_mat);
+ Mat4MulMat4(local, mat, invpar);
+ }
+ else {
+ Mat4CpyMat4(local, mat);
+ }
+
+ TransformBase::decompose(local, loc, rot, size);
+
+ /*
+ // this code used to create a single <rotate> representing object rotation
+ float quat[4];
+ float axis[3];
+ float angle;
+ double angle_deg;
+ EulToQuat(rot, quat);
+ NormalQuat(quat);
+ QuatToAxisAngle(quat, axis, &angle);
+ angle_deg = angle * 180.0f / M_PI;
+ node.addRotate(axis[0], axis[1], axis[2], angle_deg);
+ */
+ node.addTranslate("location", loc[0], loc[1], loc[2]);
+
+ node.addRotateZ("rotationZ", COLLADABU::Math::Utils::radToDegF(rot[2]));
+ node.addRotateY("rotationY", COLLADABU::Math::Utils::radToDegF(rot[1]));
+ node.addRotateX("rotationX", COLLADABU::Math::Utils::radToDegF(rot[0]));
+
+ node.addScale("scale", size[0], size[1], size[2]);
+ }
+};
+
+class InstanceWriter
+{
+protected:
+ void add_material_bindings(COLLADASW::BindMaterial& bind_material, Object *ob)
+ {
+ for(int a = 0; a < ob->totcol; a++) {
+ Material *ma = give_current_material(ob, a+1);
+
+ COLLADASW::InstanceMaterialList& iml = bind_material.getInstanceMaterialList();
+
+ if (ma) {
+ std::string matid(id_name(ma));
+ COLLADASW::InstanceMaterial im(matid, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, matid));
+
+ // create <bind_vertex_input> for each uv layer
+ Mesh *me = (Mesh*)ob->data;
+ int totlayer = CustomData_number_of_layers(&me->fdata, CD_MTFACE);
+
+ for (int b = 0; b < totlayer; b++) {
+ char *name = CustomData_get_layer_name(&me->fdata, CD_MTFACE, b);
+ im.push_back(COLLADASW::BindVertexInput(name, "TEXCOORD", b));
+ }
+
+ iml.push_back(im);
+ }
+ }
+ }
+};
+
+// XXX exporter writes wrong data for shared armatures. A separate
+// controller should be written for each armature-mesh binding how do
+// we make controller ids then?
+class ArmatureExporter: public COLLADASW::LibraryControllers, protected TransformWriter, protected InstanceWriter
+{
+private:
+ Scene *scene;
+
+public:
+ ArmatureExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryControllers(sw) {}
+
+ // write bone nodes
+ void add_armature_bones(Object *ob_arm, Scene *sce)
+ {
+ // write bone nodes
+ bArmature *arm = (bArmature*)ob_arm->data;
+ for (Bone *bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) {
+ // start from root bones
+ if (!bone->parent)
+ add_bone_node(bone, ob_arm);
+ }
+ }
+
+ bool is_skinned_mesh(Object *ob)
+ {
+ return get_assigned_armature(ob) != NULL;
+ }
+
+ void add_instance_controller(Object *ob)
+ {
+ Object *ob_arm = get_assigned_armature(ob);
+ bArmature *arm = (bArmature*)ob_arm->data;
+
+ const std::string& controller_id = get_controller_id(ob_arm);
+
+ COLLADASW::InstanceController ins(mSW);
+ ins.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, controller_id));
+
+ // write root bone URLs
+ Bone *bone;
+ for (bone = (Bone*)arm->bonebase.first; bone; bone = bone->next) {
+ if (!bone->parent)
+ ins.addSkeleton(COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_joint_id(bone, ob_arm)));
+ }
+
+ InstanceWriter::add_material_bindings(ins.getBindMaterial(), ob);
+
+ ins.add();
+ }
+
+ void export_controllers(Scene *sce)
+ {
+ scene = sce;
+
+ openLibrary();
+
+ forEachMeshObjectInScene(sce, *this);
+
+ closeLibrary();
+ }
+
+ void operator()(Object *ob)
+ {
+ Object *ob_arm = get_assigned_armature(ob);
+
+ if (ob_arm /*&& !already_written(ob_arm)*/)
+ export_controller(ob, ob_arm);
+ }
+
+private:
+
+ UnitConverter converter;
+
+#if 0
+ std::vector<Object*> written_armatures;
+
+ bool already_written(Object *ob_arm)
+ {
+ return std::find(written_armatures.begin(), written_armatures.end(), ob_arm) != written_armatures.end();
+ }
+
+ void wrote(Object *ob_arm)
+ {
+ written_armatures.push_back(ob_arm);
+ }
+
+ void find_objects_using_armature(Object *ob_arm, std::vector<Object *>& objects, Scene *sce)
+ {
+ objects.clear();
+
+ Base *base= (Base*) sce->base.first;
+ while(base) {
+ Object *ob = base->object;
+
+ if (ob->type == OB_MESH && get_assigned_armature(ob) == ob_arm) {
+ objects.push_back(ob);
+ }
+
+ base= base->next;
+ }
+ }
+#endif
+
+ Object *get_assigned_armature(Object *ob)
+ {
+ Object *ob_arm = NULL;
+
+ if (ob->parent && ob->partype == PARSKEL && ob->parent->type == OB_ARMATURE) {
+ ob_arm = ob->parent;
+ }
+ else {
+ ModifierData *mod = (ModifierData*)ob->modifiers.first;
+ while (mod) {
+ if (mod->type == eModifierType_Armature) {
+ ob_arm = ((ArmatureModifierData*)mod)->object;
+ }
+
+ mod = mod->next;
+ }
+ }
+
+ return ob_arm;
+ }
+
+ std::string get_joint_id(Bone *bone, Object *ob_arm)
+ {
+ return id_name(ob_arm) + "_" + bone->name;
+ }
+
+ std::string get_joint_sid(Bone *bone)
+ {
+ char name[100];
+ BLI_strncpy(name, bone->name, sizeof(name));
+
+ // these chars have special meaning in SID
+ replace_chars(name, ".()", '_');
+
+ return name;
+ }
+
+ // parent_mat is armature-space
+ void add_bone_node(Bone *bone, Object *ob_arm)
+ {
+ std::string node_id = get_joint_id(bone, ob_arm);
+ std::string node_name = std::string(bone->name);
+ std::string node_sid = get_joint_sid(bone);
+
+ COLLADASW::Node node(mSW);
+
+ node.setType(COLLADASW::Node::JOINT);
+ node.setNodeId(node_id);
+ node.setNodeName(node_name);
+ node.setNodeSid(node_sid);
+
+ node.start();
+
+ add_bone_transform(ob_arm, bone, node);
+
+ for (Bone *child = (Bone*)bone->childbase.first; child; child = child->next) {
+ add_bone_node(child, ob_arm);
+ }
+
+ node.end();
+ }
+
+ void add_bone_transform(Object *ob_arm, Bone *bone, COLLADASW::Node& node)
+ {
+ bPose *pose = ob_arm->pose;
+
+ bPoseChannel *pchan = get_pose_channel(ob_arm->pose, bone->name);
+
+ float mat[4][4];
+
+ if (bone->parent) {
+ // get bone-space matrix from armature-space
+ bPoseChannel *parchan = get_pose_channel(ob_arm->pose, bone->parent->name);
+
+ float invpar[4][4];
+ Mat4Invert(invpar, parchan->pose_mat);
+ Mat4MulMat4(mat, pchan->pose_mat, invpar);
+ }
+ else {
+ // get world-space from armature-space
+ Mat4MulMat4(mat, pchan->pose_mat, ob_arm->obmat);
+ }
+
+ TransformWriter::add_node_transform(node, mat, NULL);
+ }
+
+ std::string get_controller_id(Object *ob_arm)
+ {
+ return id_name(ob_arm) + SKIN_CONTROLLER_ID_SUFFIX;
+ }
+
+ // ob should be of type OB_MESH
+ // both args are required
+ void export_controller(Object* ob, Object *ob_arm)
+ {
+ // joint names
+ // joint inverse bind matrices
+ // vertex weights
+
+ // input:
+ // joint names: ob -> vertex group names
+ // vertex group weights: me->dvert -> groups -> index, weight
+
+ /*
+ me->dvert:
+
+ typedef struct MDeformVert {
+ struct MDeformWeight *dw;
+ int totweight;
+ int flag; // flag only in use for weightpaint now
+ } MDeformVert;
+
+ typedef struct MDeformWeight {
+ int def_nr;
+ float weight;
+ } MDeformWeight;
+ */
+
+ Mesh *me = (Mesh*)ob->data;
+ if (!me->dvert) return;
+
+ std::string controller_name = id_name(ob_arm);
+ std::string controller_id = get_controller_id(ob_arm);
+
+ openSkin(controller_id, controller_name,
+ COLLADABU::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob)));
+
+ add_bind_shape_mat(ob);
+
+ std::string joints_source_id = add_joints_source(ob_arm, &ob->defbase, controller_id);
+ std::string inv_bind_mat_source_id = add_inv_bind_mats_source(ob_arm, &ob->defbase, controller_id);
+ std::string weights_source_id = add_weights_source(me, controller_id);
+
+ add_joints_element(&ob->defbase, joints_source_id, inv_bind_mat_source_id);
+ add_vertex_weights_element(weights_source_id, joints_source_id, me, ob_arm, &ob->defbase);
+
+ closeSkin();
+ closeController();
+ }
+
+ void add_joints_element(ListBase *defbase,
+ const std::string& joints_source_id, const std::string& inv_bind_mat_source_id)
+ {
+ COLLADASW::JointsElement joints(mSW);
+ COLLADASW::InputList &input = joints.getInputList();
+
+ int offset = 0;
+ input.push_back(COLLADASW::Input(COLLADASW::JOINT, // constant declared in COLLADASWInputList.h
+ COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id)));
+ input.push_back(COLLADASW::Input(COLLADASW::BINDMATRIX,
+ COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, inv_bind_mat_source_id)));
+ joints.add();
+ }
+
+ void add_bind_shape_mat(Object *ob)
+ {
+ double bind_mat[4][4];
+
+ converter.mat4_to_dae_double(bind_mat, ob->obmat);
+
+ addBindShapeTransform(bind_mat);
+ }
+
+ std::string add_joints_source(Object *ob_arm, ListBase *defbase, const std::string& controller_id)
+ {
+ std::string source_id = controller_id + JOINTS_SOURCE_ID_SUFFIX;
+
+ int totjoint = 0;
+ bDeformGroup *def;
+ for (def = (bDeformGroup*)defbase->first; def; def = def->next) {
+ if (is_bone_defgroup(ob_arm, def))
+ totjoint++;
+ }
+
+ COLLADASW::NameSource source(mSW);
+ source.setId(source_id);
+ source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+ source.setAccessorCount(totjoint);
+ source.setAccessorStride(1);
+
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ param.push_back("JOINT");
+
+ source.prepareToAppendValues();
+
+ for (def = (bDeformGroup*)defbase->first; def; def = def->next) {
+ Bone *bone = get_bone_from_defgroup(ob_arm, def);
+ if (bone)
+ source.appendValues(get_joint_sid(bone));
+ }
+
+ source.finish();
+
+ return source_id;
+ }
+
+ std::string add_inv_bind_mats_source(Object *ob_arm, ListBase *defbase, const std::string& controller_id)
+ {
+ std::string source_id = controller_id + BIND_POSES_SOURCE_ID_SUFFIX;
+
+ COLLADASW::FloatSourceF source(mSW);
+ source.setId(source_id);
+ source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+ source.setAccessorCount(BLI_countlist(defbase));
+ source.setAccessorStride(16);
+
+ source.setParameterTypeName(&COLLADASW::CSWC::CSW_VALUE_TYPE_FLOAT4x4);
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ param.push_back("TRANSFORM");
+
+ source.prepareToAppendValues();
+
+ bPose *pose = ob_arm->pose;
+ bArmature *arm = (bArmature*)ob_arm->data;
+
+ int flag = arm->flag;
+
+ // put armature in rest position
+ if (!(arm->flag & ARM_RESTPOS)) {
+ arm->flag |= ARM_RESTPOS;
+ where_is_pose(scene, ob_arm);
+ }
+
+ for (bDeformGroup *def = (bDeformGroup*)defbase->first; def; def = def->next) {
+ if (is_bone_defgroup(ob_arm, def)) {
+
+ bPoseChannel *pchan = get_pose_channel(pose, def->name);
+
+ float mat[4][4];
+ float world[4][4];
+ float inv_bind_mat[4][4];
+
+ // make world-space matrix, pose_mat is armature-space
+ Mat4MulMat4(world, pchan->pose_mat, ob_arm->obmat);
+
+ Mat4Invert(mat, world);
+ converter.mat4_to_dae(inv_bind_mat, mat);
+
+ source.appendValues(inv_bind_mat);
+ }
+ }
+
+ // back from rest positon
+ if (!(flag & ARM_RESTPOS)) {
+ arm->flag = flag;
+ where_is_pose(scene, ob_arm);
+ }
+
+ source.finish();
+
+ return source_id;
+ }
+
+ Bone *get_bone_from_defgroup(Object *ob_arm, bDeformGroup* def)
+ {
+ bPoseChannel *pchan = get_pose_channel(ob_arm->pose, def->name);
+ return pchan ? pchan->bone : NULL;
+ }
+
+ bool is_bone_defgroup(Object *ob_arm, bDeformGroup* def)
+ {
+ return get_bone_from_defgroup(ob_arm, def) != NULL;
+ }
+
+ std::string add_weights_source(Mesh *me, const std::string& controller_id)
+ {
+ std::string source_id = controller_id + WEIGHTS_SOURCE_ID_SUFFIX;
+
+ int i;
+ int totweight = 0;
+
+ for (i = 0; i < me->totvert; i++) {
+ totweight += me->dvert[i].totweight;
+ }
+
+ COLLADASW::FloatSourceF source(mSW);
+ source.setId(source_id);
+ source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+ source.setAccessorCount(totweight);
+ source.setAccessorStride(1);
+
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ param.push_back("WEIGHT");
+
+ source.prepareToAppendValues();
+
+ // NOTE: COLLADA spec says weights should be normalized
+
+ for (i = 0; i < me->totvert; i++) {
+ MDeformVert *vert = &me->dvert[i];
+ for (int j = 0; j < vert->totweight; j++) {
+ source.appendValues(vert->dw[j].weight);
+ }
+ }
+
+ source.finish();
+
+ return source_id;
+ }
+
+ void add_vertex_weights_element(const std::string& weights_source_id, const std::string& joints_source_id, Mesh *me,
+ Object *ob_arm, ListBase *defbase)
+ {
+ COLLADASW::VertexWeightsElement weights(mSW);
+ COLLADASW::InputList &input = weights.getInputList();
+
+ int offset = 0;
+ input.push_back(COLLADASW::Input(COLLADASW::JOINT, // constant declared in COLLADASWInputList.h
+ COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, joints_source_id), offset++));
+ input.push_back(COLLADASW::Input(COLLADASW::WEIGHT,
+ COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, weights_source_id), offset++));
+
+ weights.setCount(me->totvert);
+
+ // write number of deformers per vertex
+ COLLADASW::PrimitivesBase::VCountList vcount;
+ int i;
+ for (i = 0; i < me->totvert; i++) {
+ vcount.push_back(me->dvert[i].totweight);
+ }
+
+ weights.prepareToAppendVCountValues();
+ weights.appendVertexCount(vcount);
+
+ // def group index -> joint index
+ std::map<int, int> joint_index_by_def_index;
+ bDeformGroup *def;
+ int j;
+ for (def = (bDeformGroup*)defbase->first, i = 0, j = 0; def; def = def->next, i++) {
+ if (is_bone_defgroup(ob_arm, def))
+ joint_index_by_def_index[i] = j++;
+ else
+ joint_index_by_def_index[i] = -1;
+ }
+
+ weights.CloseVCountAndOpenVElement();
+
+ // write deformer index - weight index pairs
+ int weight_index = 0;
+ for (i = 0; i < me->totvert; i++) {
+ MDeformVert *dvert = &me->dvert[i];
+ for (int j = 0; j < dvert->totweight; j++) {
+ weights.appendValues(joint_index_by_def_index[dvert->dw[j].def_nr]);
+ weights.appendValues(weight_index++);
+ }
+ }
+
+ weights.finish();
+ }
+};
+
+class SceneExporter: COLLADASW::LibraryVisualScenes, protected TransformWriter, protected InstanceWriter
+{
+ ArmatureExporter *arm_exporter;
+public:
+ SceneExporter(COLLADASW::StreamWriter *sw, ArmatureExporter *arm) : COLLADASW::LibraryVisualScenes(sw),
+ arm_exporter(arm) {}
+
+ void exportScene(Scene *sce) {
+ // <library_visual_scenes> <visual_scene>
+ openVisualScene(id_name(sce));
+
+ // write <node>s
+ //forEachMeshObjectInScene(sce, *this);
+ //forEachCameraObjectInScene(sce, *this);
+ //forEachLampObjectInScene(sce, *this);
+ exportHierarchy(sce);
+
+ // </visual_scene> </library_visual_scenes>
+ closeVisualScene();
+
+ closeLibrary();
+ }
+
+ void exportHierarchy(Scene *sce)
+ {
+ Base *base= (Base*) sce->base.first;
+ while(base) {
+ Object *ob = base->object;
+
+ if (!ob->parent) {
+ switch(ob->type) {
+ case OB_MESH:
+ case OB_CAMERA:
+ case OB_LAMP:
+ case OB_EMPTY:
+ case OB_ARMATURE:
+ // write nodes....
+ writeNodes(ob, sce);
+ break;
+ }
+ }
+
+ base= base->next;
+ }
+ }
+
+
+ // called for each object
+ //void operator()(Object *ob) {
+ void writeNodes(Object *ob, Scene *sce)
+ {
+ COLLADASW::Node node(mSW);
+ node.setNodeId(id_name(ob));
+ node.setType(COLLADASW::Node::NODE);
+
+ node.start();
+
+ bool is_skinned_mesh = arm_exporter->is_skinned_mesh(ob);
+
+ float mat[4][4];
+
+ if (ob->type == OB_MESH && is_skinned_mesh)
+ // for skinned mesh we write obmat in <bind_shape_matrix>
+ Mat4One(mat);
+ else
+ Mat4CpyMat4(mat, ob->obmat);
+
+ TransformWriter::add_node_transform(node, mat, ob->parent ? ob->parent->obmat : NULL);
+
+ // <instance_geometry>
+ if (ob->type == OB_MESH) {
+ if (is_skinned_mesh) {
+ arm_exporter->add_instance_controller(ob);
+ }
+ else {
+ COLLADASW::InstanceGeometry instGeom(mSW);
+ instGeom.setUrl(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_geometry_id(ob)));
+
+ InstanceWriter::add_material_bindings(instGeom.getBindMaterial(), ob);
+
+ instGeom.add();
+ }
+ }
+
+ // <instance_controller>
+ else if (ob->type == OB_ARMATURE) {
+ arm_exporter->add_armature_bones(ob, sce);
+
+ // XXX this looks unstable...
+ node.end();
+ }
+
+ // <instance_camera>
+ else if (ob->type == OB_CAMERA) {
+ COLLADASW::InstanceCamera instCam(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_camera_id(ob)));
+ instCam.add();
+ }
+
+ // <instance_light>
+ else if (ob->type == OB_LAMP) {
+ COLLADASW::InstanceLight instLa(mSW, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, get_light_id(ob)));
+ instLa.add();
+ }
+
+ // empty object
+ else if (ob->type == OB_EMPTY) {
+ }
+
+ // write nodes for child objects
+ Base *b = (Base*) sce->base.first;
+ while(b) {
+ // cob - child object
+ Object *cob = b->object;
+
+ if (cob->parent == ob) {
+ switch(cob->type) {
+ case OB_MESH:
+ case OB_CAMERA:
+ case OB_LAMP:
+ case OB_EMPTY:
+ case OB_ARMATURE:
+ // write node...
+ writeNodes(cob, sce);
+ break;
+ }
+ }
+
+ b = b->next;
+ }
+
+ if (ob->type != OB_ARMATURE)
+ node.end();
+ }
+};
+
+class ImagesExporter: COLLADASW::LibraryImages
+{
+ const char *mfilename;
+ std::vector<std::string> mImages; // contains list of written images, to avoid duplicates
+public:
+ ImagesExporter(COLLADASW::StreamWriter *sw, const char* filename) : COLLADASW::LibraryImages(sw), mfilename(filename)
+ {}
+
+ void exportImages(Scene *sce)
+ {
+ openLibrary();
+
+ forEachMaterialInScene(sce, *this);
+
+ closeLibrary();
+ }
+
+ void operator()(Material *ma, Object *ob)
+ {
+ int a;
+ for (a = 0; a < MAX_MTEX; a++) {
+ MTex *mtex = ma->mtex[a];
+ if (mtex && mtex->tex && mtex->tex->ima) {
+
+ Image *image = mtex->tex->ima;
+ std::string name(id_name(image));
+ char rel[FILE_MAX];
+ char abs[FILE_MAX];
+ char src[FILE_MAX];
+ char dir[FILE_MAX];
+
+ BLI_split_dirfile_basic(mfilename, dir, NULL);
+
+ BKE_get_image_export_path(image, dir, abs, sizeof(abs), rel, sizeof(rel));
+
+ if (strlen(abs)) {
+
+ // make absolute source path
+ BLI_strncpy(src, image->name, sizeof(src));
+ BLI_convertstringcode(src, G.sce);
+
+ // make dest directory if it doesn't exist
+ BLI_make_existing_file(abs);
+
+ if (BLI_copy_fileops(src, abs) != 0) {
+ fprintf(stderr, "Cannot copy image to file's directory. \n");
+ }
+ }
+
+ if (find(mImages.begin(), mImages.end(), name) == mImages.end()) {
+ COLLADASW::Image img(COLLADABU::URI(COLLADABU::URI::nativePathToUri(rel)), name);
+ img.add(mSW);
+
+ mImages.push_back(name);
+ }
+ }
+ }
+ }
+};
+
+
+class EffectsExporter: COLLADASW::LibraryEffects
+{
+public:
+ EffectsExporter(COLLADASW::StreamWriter *sw) : COLLADASW::LibraryEffects(sw){}
+ void exportEffects(Scene *sce)
+ {
+ openLibrary();
+
+ forEachMaterialInScene(sce, *this);
+
+ closeLibrary();
+ }
+
+ void operator()(Material *ma, Object *ob)
+ {
+ // create a list of indices to textures of type TEX_IMAGE
+ std::vector<int> tex_indices;
+ createTextureIndices(ma, tex_indices);
+
+ openEffect(id_name(ma) + "-effect");
+
+ COLLADASW::EffectProfile ep(mSW);
+ ep.setProfileType(COLLADASW::EffectProfile::COMMON);
+ ep.openProfile();
+ // set shader type - one of three blinn, phong or lambert
+ if (ma->spec_shader == MA_SPEC_BLINN) {
+ ep.setShaderType(COLLADASW::EffectProfile::BLINN);
+ // shininess
+ ep.setShininess(ma->spec);
+ }
+ else if (ma->spec_shader == MA_SPEC_PHONG) {
+ ep.setShaderType(COLLADASW::EffectProfile::PHONG);
+ // shininess
+ // XXX not sure, stolen this from previous Collada plugin
+ ep.setShininess(ma->har / 4);
+ }
+ else {
+ // XXX write warning "Current shader type is not supported"
+ ep.setShaderType(COLLADASW::EffectProfile::LAMBERT);
+ }
+ // index of refraction
+ if (ma->mode & MA_RAYTRANSP) {
+ ep.setIndexOfRefraction(ma->ang);
+ }
+ else {
+ ep.setIndexOfRefraction(1.0f);
+ }
+ // transparency
+ ep.setTransparency(ma->alpha);
+ // emission
+ COLLADASW::ColorOrTexture cot = getcol(0.0f, 0.0f, 0.0f, 1.0f);
+ ep.setEmission(cot);
+ ep.setTransparent(cot);
+ // diffuse
+ cot = getcol(ma->r, ma->g, ma->b, 1.0f);
+ ep.setDiffuse(cot);
+ // ambient
+ cot = getcol(ma->ambr, ma->ambg, ma->ambb, 1.0f);
+ ep.setAmbient(cot);
+ // reflective, reflectivity
+ if (ma->mode & MA_RAYMIRROR) {
+ cot = getcol(ma->mirr, ma->mirg, ma->mirb, 1.0f);
+ ep.setReflective(cot);
+ ep.setReflectivity(ma->ray_mirror);
+ }
+ else {
+ cot = getcol(0.0f, 0.0f, 0.0f, 1.0f);
+ ep.setReflective(cot);
+ ep.setReflectivity(0.0f);
+ }
+ // specular
+ if (ep.getShaderType() != COLLADASW::EffectProfile::LAMBERT) {
+ cot = getcol(ma->specr, ma->specg, ma->specb, 1.0f);
+ ep.setSpecular(cot);
+ }
+
+ // XXX make this more readable if possible
+
+ // create <sampler> and <surface> for each image
+ COLLADASW::Sampler samplers[MAX_MTEX];
+ //COLLADASW::Surface surfaces[MAX_MTEX];
+ //void *samp_surf[MAX_MTEX][2];
+ void *samp_surf[MAX_MTEX][1];
+
+ // image to index to samp_surf map
+ // samp_surf[index] stores 2 pointers, sampler and surface
+ std::map<std::string, int> im_samp_map;
+
+ unsigned int a, b;
+ for (a = 0, b = 0; a < tex_indices.size(); a++) {
+ MTex *t = ma->mtex[tex_indices[a]];
+ Image *ima = t->tex->ima;
+
+ std::string key(id_name(ima));
+
+ // create only one <sampler>/<surface> pair for each unique image
+ if (im_samp_map.find(key) == im_samp_map.end()) {
+ //<newparam> <surface> <init_from>
+ // COLLADASW::Surface surface(COLLADASW::Surface::SURFACE_TYPE_2D,
+// key + COLLADASW::Surface::SURFACE_SID_SUFFIX);
+// COLLADASW::SurfaceInitOption sio(COLLADASW::SurfaceInitOption::INIT_FROM);
+// sio.setImageReference(key);
+// surface.setInitOption(sio);
+
+ //<newparam> <sampler> <source>
+ COLLADASW::Sampler sampler(COLLADASW::Sampler::SAMPLER_TYPE_2D,
+ key + COLLADASW::Sampler::SAMPLER_SID_SUFFIX,
+ key + COLLADASW::Sampler::SURFACE_SID_SUFFIX);
+ sampler.setImageId(key);
+ // copy values to arrays since they will live longer
+ samplers[a] = sampler;
+ //surfaces[a] = surface;
+
+ // store pointers so they can be used later when we create <texture>s
+ samp_surf[b][0] = &samplers[a];
+ //samp_surf[b][1] = &surfaces[a];
+
+ im_samp_map[key] = b;
+ b++;
+ }
+ }
+
+ // used as fallback when MTex->uvname is "" (this is pretty common)
+ // it is indeed the correct value to use in that case
+ std::string active_uv(getActiveUVLayerName(ob));
+
+ // write textures
+ // XXX very slow
+ for (a = 0; a < tex_indices.size(); a++) {
+ MTex *t = ma->mtex[tex_indices[a]];
+ Image *ima = t->tex->ima;
+
+ // we assume map input is always TEXCO_UV
+
+ std::string key(id_name(ima));
+ int i = im_samp_map[key];
+ COLLADASW::Sampler *sampler = (COLLADASW::Sampler*)samp_surf[i][0];
+ //COLLADASW::Surface *surface = (COLLADASW::Surface*)samp_surf[i][1];
+
+ std::string uvname = strlen(t->uvname) ? t->uvname : active_uv;
+
+ // color
+ if (t->mapto & MAP_COL) {
+ ep.setDiffuse(createTexture(ima, uvname, sampler));
+ }
+ // ambient
+ if (t->mapto & MAP_AMB) {
+ ep.setAmbient(createTexture(ima, uvname, sampler));
+ }
+ // specular
+ if (t->mapto & MAP_SPEC) {
+ ep.setSpecular(createTexture(ima, uvname, sampler));
+ }
+ // emission
+ if (t->mapto & MAP_EMIT) {
+ ep.setEmission(createTexture(ima, uvname, sampler));
+ }
+ // reflective
+ if (t->mapto & MAP_REF) {
+ ep.setReflective(createTexture(ima, uvname, sampler));
+ }
+ if (t->mapto & MAP_ALPHA) {
+ ep.setTransparent(createTexture(ima, uvname, sampler));
+ }
+ }
+ // performs the actual writing
+ ep.addProfileElements();
+ ep.closeProfile();
+ closeEffect();
+ }
+
+ COLLADASW::ColorOrTexture createTexture(Image *ima,
+ std::string& uv_layer_name,
+ COLLADASW::Sampler *sampler
+ /*COLLADASW::Surface *surface*/)
+ {
+
+ COLLADASW::Texture texture(id_name(ima));
+ texture.setTexcoord(uv_layer_name);
+ //texture.setSurface(*surface);
+ texture.setSampler(*sampler);
+
+ COLLADASW::ColorOrTexture cot(texture);
+ return cot;
+ }
+
+ COLLADASW::ColorOrTexture getcol(float r, float g, float b, float a)
+ {
+ COLLADASW::Color color(r,g,b,a);
+ COLLADASW::ColorOrTexture cot(color);
+ return cot;
+ }
+
+ //returns the array of mtex indices which have image
+ //need this for exporting textures
+ void createTextureIndices(Material *ma, std::vector<int> &indices)
+ {
+ indices.clear();
+
+ for (int a = 0; a < MAX_MTEX; a++) {
+ if (ma->mtex[a] &&
+ ma->mtex[a]->tex->type == TEX_IMAGE &&
+ ma->mtex[a]->texco == TEXCO_UV){
+ indices.push_back(a);
+ }
+ }
+ }
+};
+
+class MaterialsExporter: COLLADASW::LibraryMaterials
+{
+public:
+ MaterialsExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryMaterials(sw){}
+ void exportMaterials(Scene *sce)
+ {
+ openLibrary();
+
+ forEachMaterialInScene(sce, *this);
+
+ closeLibrary();
+ }
+
+ void operator()(Material *ma, Object *ob)
+ {
+ std::string name(id_name(ma));
+
+ openMaterial(name);
+
+ std::string efid = name + "-effect";
+ addInstanceEffect(COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, efid));
+
+ closeMaterial();
+ }
+};
+
+class CamerasExporter: COLLADASW::LibraryCameras
+{
+public:
+ CamerasExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryCameras(sw){}
+ void exportCameras(Scene *sce)
+ {
+ openLibrary();
+
+ forEachCameraObjectInScene(sce, *this);
+
+ closeLibrary();
+ }
+ void operator()(Object *ob, Scene *sce)
+ {
+ // XXX add other params later
+ Camera *cam = (Camera*)ob->data;
+ std::string cam_id(get_camera_id(ob));
+ std::string cam_name(id_name(cam));
+
+ if (cam->type == CAM_PERSP) {
+ COLLADASW::PerspectiveOptic persp(mSW);
+ persp.setXFov(1.0);
+ persp.setAspectRatio(0.1);
+ persp.setZFar(cam->clipend);
+ persp.setZNear(cam->clipsta);
+ COLLADASW::Camera ccam(mSW, &persp, cam_id, cam_name);
+ addCamera(ccam);
+ }
+ else {
+ COLLADASW::OrthographicOptic ortho(mSW);
+ ortho.setXMag(1.0);
+ ortho.setAspectRatio(0.1);
+ ortho.setZFar(cam->clipend);
+ ortho.setZNear(cam->clipsta);
+ COLLADASW::Camera ccam(mSW, &ortho, cam_id, cam_name);
+ addCamera(ccam);
+ }
+ }
+};
+
+class LightsExporter: COLLADASW::LibraryLights
+{
+public:
+ LightsExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryLights(sw){}
+ void exportLights(Scene *sce)
+ {
+ openLibrary();
+
+ forEachLampObjectInScene(sce, *this);
+
+ closeLibrary();
+ }
+ void operator()(Object *ob)
+ {
+ Lamp *la = (Lamp*)ob->data;
+ std::string la_id(get_light_id(ob));
+ std::string la_name(id_name(la));
+ COLLADASW::Color col(la->r, la->g, la->b);
+ float e = la->energy;
+
+ // sun
+ if (la->type == LA_SUN) {
+ COLLADASW::DirectionalLight cla(mSW, la_id, la_name, e);
+ cla.setColor(col);
+ addLight(cla);
+ }
+ // hemi
+ else if (la->type == LA_HEMI) {
+ COLLADASW::AmbientLight cla(mSW, la_id, la_name, e);
+ cla.setColor(col);
+ addLight(cla);
+ }
+ // spot
+ else if (la->type == LA_SPOT) {
+ COLLADASW::SpotLight cla(mSW, la_id, la_name, e);
+ cla.setColor(col);
+ cla.setFallOffAngle(la->spotsize);
+ cla.setFallOffExponent(la->spotblend);
+ cla.setLinearAttenuation(la->att1);
+ cla.setQuadraticAttenuation(la->att2);
+ addLight(cla);
+ }
+ // lamp
+ else if (la->type == LA_LOCAL) {
+ COLLADASW::PointLight cla(mSW, la_id, la_name, e);
+ cla.setColor(col);
+ cla.setLinearAttenuation(la->att1);
+ cla.setQuadraticAttenuation(la->att2);
+ addLight(cla);
+ }
+ // area lamp is not supported
+ // it will be exported as a local lamp
+ else {
+ COLLADASW::PointLight cla(mSW, la_id, la_name, e);
+ cla.setColor(col);
+ cla.setLinearAttenuation(la->att1);
+ cla.setQuadraticAttenuation(la->att2);
+ addLight(cla);
+ }
+ }
+};
+
+// TODO: it would be better to instantiate animations rather than create a new one per object
+// COLLADA allows this through multiple <channel>s in <animation>.
+// For this to work, we need to know objects that use a certain action.
+class AnimationExporter: COLLADASW::LibraryAnimations
+{
+ Scene *scene;
+ std::map<bActionGroup*, std::vector<FCurve*> > fcurves_actionGroup_map;
+ std::map<bActionGroup*, std::vector<FCurve*> > rotfcurves_actionGroup_map;
+public:
+ AnimationExporter(COLLADASW::StreamWriter *sw): COLLADASW::LibraryAnimations(sw) {}
+
+ void exportAnimations(Scene *sce)
+ {
+ this->scene = sce;
+
+ openLibrary();
+
+ forEachObjectInScene(sce, *this);
+
+ closeLibrary();
+ }
+
+ // create <animation> for each transform axis
+
+ float convert_time(float frame) {
+ return FRA2TIME(frame);
+ }
+
+ float convert_angle(float angle) {
+ return COLLADABU::Math::Utils::radToDegF(angle);
+ }
+
+ std::string get_semantic_suffix(Sampler::Semantic semantic) {
+ switch(semantic) {
+ case Sampler::INPUT:
+ return INPUT_SOURCE_ID_SUFFIX;
+ case Sampler::OUTPUT:
+ return OUTPUT_SOURCE_ID_SUFFIX;
+ case Sampler::INTERPOLATION:
+ return INTERPOLATION_SOURCE_ID_SUFFIX;
+ case Sampler::IN_TANGENT:
+ return INTANGENT_SOURCE_ID_SUFFIX;
+ case Sampler::OUT_TANGENT:
+ return OUTTANGENT_SOURCE_ID_SUFFIX;
+ }
+ return "";
+ }
+
+ void add_source_parameters(COLLADASW::SourceBase::ParameterNameList& param,
+ Sampler::Semantic semantic, bool rotation, const char *axis) {
+ switch(semantic) {
+ case Sampler::INPUT:
+ param.push_back("TIME");
+ break;
+ case Sampler::OUTPUT:
+ if (rotation) {
+ param.push_back("ANGLE");
+ }
+ else {
+ param.push_back(axis);
+ }
+ break;
+ case Sampler::IN_TANGENT:
+ case Sampler::OUT_TANGENT:
+ param.push_back("X");
+ param.push_back("Y");
+ break;
+ }
+ }
+
+ void get_source_values(BezTriple *bezt, Sampler::Semantic semantic, bool rotation, float *values, int *length)
+ {
+ switch (semantic) {
+ case Sampler::INPUT:
+ *length = 1;
+ values[0] = convert_time(bezt->vec[1][0]);
+ break;
+ case Sampler::OUTPUT:
+ *length = 1;
+ if (rotation) {
+ values[0] = convert_angle(bezt->vec[1][1]);
+ }
+ else {
+ values[0] = bezt->vec[1][1];
+ }
+ break;
+ case Sampler::IN_TANGENT:
+ case Sampler::OUT_TANGENT:
+ // XXX
+ *length = 2;
+ break;
+ }
+ }
+
+ std::string create_source(Sampler::Semantic semantic, FCurve *fcu, std::string& anim_id, const char *axis_name)
+ {
+ std::string source_id = anim_id + get_semantic_suffix(semantic);
+
+ //bool is_rotation = !strcmp(fcu->rna_path, "rotation");
+ bool is_rotation = false;
+
+ if (strstr(fcu->rna_path, "rotation")) is_rotation = true;
+
+ COLLADASW::FloatSourceF source(mSW);
+ source.setId(source_id);
+ source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+ source.setAccessorCount(fcu->totvert);
+ source.setAccessorStride(1);
+
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ add_source_parameters(param, semantic, is_rotation, axis_name);
+
+ source.prepareToAppendValues();
+
+ for (int i = 0; i < fcu->totvert; i++) {
+ float values[3]; // be careful!
+ int length;
+
+ get_source_values(&fcu->bezt[i], semantic, is_rotation, values, &length);
+ for (int j = 0; j < length; j++)
+ source.appendValues(values[j]);
+ }
+
+ source.finish();
+
+ return source_id;
+ }
+
+ std::string create_interpolation_source(FCurve *fcu, std::string& anim_id, const char *axis_name)
+ {
+ std::string source_id = anim_id + get_semantic_suffix(Sampler::INTERPOLATION);
+
+ //bool is_rotation = !strcmp(fcu->rna_path, "rotation");
+
+ COLLADASW::NameSource source(mSW);
+ source.setId(source_id);
+ source.setArrayId(source_id + ARRAY_ID_SUFFIX);
+ source.setAccessorCount(fcu->totvert);
+ source.setAccessorStride(1);
+
+ COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
+ param.push_back("INTERPOLATION");
+
+ source.prepareToAppendValues();
+
+ for (int i = 0; i < fcu->totvert; i++) {
+ // XXX
+ source.appendValues(LINEAR_NAME);
+ }
+
+ source.finish();
+
+ return source_id;
+ }
+
+ std::string get_transform_sid(char *rna_path, const char *axis_name)
+ {
+ // if (!strcmp(rna_path, "rotation"))
+// return std::string(rna_path) + axis_name;
+
+// return std::string(rna_path) + "." + axis_name;
+ std::string new_rna_path;
+
+ if (strstr(rna_path, "rotation")) {
+ new_rna_path = strstr(rna_path, "rotation");
+ return new_rna_path + axis_name;
+ }
+ else if (strstr(rna_path, "location")) {
+ new_rna_path = strstr(rna_path, "location");
+ return new_rna_path + "." + axis_name;
+ }
+ else if (strstr(rna_path, "scale")) {
+ new_rna_path = strstr(rna_path, "scale");
+ return new_rna_path + "." + axis_name;
+ }
+ return NULL;
+ }
+
+ void add_animation(FCurve *fcu, std::string ob_name)
+ {
+ const char *axis_names[] = {"X", "Y", "Z"};
+ const char *axis_name = NULL;
+ char c_anim_id[100]; // careful!
+
+ if (fcu->array_index < 3)
+ axis_name = axis_names[fcu->array_index];
+
+ BLI_snprintf(c_anim_id, sizeof(c_anim_id), "%s.%s.%s", (char*)ob_name.c_str(), fcu->rna_path, axis_names[fcu->array_index]);
+ std::string anim_id(c_anim_id);
+
+ // check rna_path is one of: rotation, scale, location
+
+ openAnimation(anim_id);
+
+ // create input source
+ std::string input_id = create_source(Sampler::INPUT, fcu, anim_id, axis_name);
+
+ // create output source
+ std::string output_id = create_source(Sampler::OUTPUT, fcu, anim_id, axis_name);
+
+ // create interpolations source
+ std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name);
+
+ std::string sampler_id = anim_id + SAMPLER_ID_SUFFIX;
+ COLLADASW::LibraryAnimations::Sampler sampler(sampler_id);
+ std::string empty;
+ sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
+ sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
+
+ // this input is required
+ sampler.addInput(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
+
+ addSampler(sampler);
+
+ std::string target = ob_name + "/" + get_transform_sid(fcu->rna_path, axis_name);
+ addChannel(COLLADABU::URI(empty, sampler_id), target);
+
+ closeAnimation();
+ }
+
+ void add_bone_animation(FCurve *fcu, std::string ob_name, std::string bone_name)
+ {
+ const char *axis_names[] = {"X", "Y", "Z"};
+ const char *axis_name = NULL;
+ char c_anim_id[100]; // careful!
+
+ if (fcu->array_index < 3)
+ axis_name = axis_names[fcu->array_index];
+
+ std::string transform_sid = get_transform_sid(fcu->rna_path, axis_name);
+
+ BLI_snprintf(c_anim_id, sizeof(c_anim_id), "%s.%s.%s", (char*)ob_name.c_str(), (char*)bone_name.c_str(), (char*)transform_sid.c_str());
+ std::string anim_id(c_anim_id);
+
+ // check rna_path is one of: rotation, scale, location
+
+ openAnimation(anim_id);
+
+ // create input source
+ std::string input_id = create_source(Sampler::INPUT, fcu, anim_id, axis_name);
+
+ // create output source
+ std::string output_id = create_source(Sampler::OUTPUT, fcu, anim_id, axis_name);
+
+ // create interpolations source
+ std::string interpolation_id = create_interpolation_source(fcu, anim_id, axis_name);
+
+ std::string sampler_id = anim_id + SAMPLER_ID_SUFFIX;
+ COLLADASW::LibraryAnimations::Sampler sampler(sampler_id);
+ std::string empty;
+ sampler.addInput(Sampler::INPUT, COLLADABU::URI(empty, input_id));
+ sampler.addInput(Sampler::OUTPUT, COLLADABU::URI(empty, output_id));
+
+ // this input is required
+ sampler.addInput(Sampler::INTERPOLATION, COLLADABU::URI(empty, interpolation_id));
+
+ addSampler(sampler);
+
+ std::string target = ob_name + "_" + bone_name + "/" + transform_sid;
+ addChannel(COLLADABU::URI(empty, sampler_id), target);
+
+ closeAnimation();
+ }
+
+ FCurve *create_fcurve(int array_index, char *rna_path)
+ {
+ FCurve *fcu = (FCurve*)MEM_callocN(sizeof(FCurve), "FCurve");
+
+ fcu->flag = (FCURVE_VISIBLE|FCURVE_AUTO_HANDLES|FCURVE_SELECTED);
+ fcu->rna_path = BLI_strdupn(rna_path, strlen(rna_path));
+ fcu->array_index = array_index;
+ return fcu;
+ }
+
+ void create_bezt(FCurve *fcu, float frame, float output)
+ {
+ BezTriple bez;
+ memset(&bez, 0, sizeof(BezTriple));
+ bez.vec[1][0] = frame;
+ bez.vec[1][1] = output;
+ bez.ipo = U.ipo_new; /* use default interpolation mode here... */
+ bez.f1 = bez.f2 = bez.f3 = SELECT;
+ bez.h1 = bez.h2 = HD_AUTO;
+ insert_bezt_fcurve(fcu, &bez, 0);
+ calchandles_fcurve(fcu);
+ }
+
+ void change_quat_to_eul(Object *ob, bActionGroup *grp, char *grpname)
+ {
+ std::vector<FCurve*> &rot_fcurves = rotfcurves_actionGroup_map[grp];
+
+ FCurve *quatcu[4] = {NULL, NULL, NULL, NULL};
+ int i;
+
+ for (i = 0; i < rot_fcurves.size(); i++)
+ quatcu[rot_fcurves[i]->array_index] = rot_fcurves[i];
+
+ char *rna_path = rot_fcurves[0]->rna_path;
+
+ FCurve *eulcu[3] = {
+ create_fcurve(0, rna_path),
+ create_fcurve(1, rna_path),
+ create_fcurve(2, rna_path)
+ };
+
+ for (i = 0; i < 4; i++) {
+
+ FCurve *cu = quatcu[i];
+
+ if (!cu) continue;
+
+ for (int j = 0; j < cu->totvert; j++) {
+ float frame = cu->bezt[j].vec[1][0];
+
+ float quat[4] = {
+ quatcu[0] ? evaluate_fcurve(quatcu[0], frame) : 0.0f,
+ quatcu[1] ? evaluate_fcurve(quatcu[1], frame) : 0.0f,
+ quatcu[2] ? evaluate_fcurve(quatcu[2], frame) : 0.0f,
+ quatcu[3] ? evaluate_fcurve(quatcu[3], frame) : 0.0f
+ };
+
+ float eul[3];
+
+ QuatToEul(quat, eul);
+
+ for (int k = 0; k < 3; k++)
+ create_bezt(eulcu[k], frame, eul[k]);
+ }
+ }
+
+ for (i = 0; i < 3; i++) {
+ add_bone_animation(eulcu[i], id_name(ob), std::string(grpname));
+ free_fcurve(eulcu[i]);
+ }
+ }
+
+ // called for each exported object
+ void operator() (Object *ob)
+ {
+ if (!ob->adt || !ob->adt->action) return;
+
+ FCurve *fcu = (FCurve*)ob->adt->action->curves.first;
+
+ if (ob->type == OB_ARMATURE) {
+
+ while (fcu) {
+
+ if (strstr(fcu->rna_path, ".rotation"))
+ rotfcurves_actionGroup_map[fcu->grp].push_back(fcu);
+ else fcurves_actionGroup_map[fcu->grp].push_back(fcu);
+
+ fcu = fcu->next;
+ }
+
+ for (bPoseChannel *pchan = (bPoseChannel*)ob->pose->chanbase.first; pchan; pchan = pchan->next) {
+ int i;
+ char *grpname = pchan->name;
+ bActionGroup *grp = action_groups_find_named(ob->adt->action, grpname);
+
+ if (!grp) continue;
+
+ // write animation for location & scaling
+ if (fcurves_actionGroup_map.find(grp) == fcurves_actionGroup_map.end()) continue;
+
+ std::vector<FCurve*> &fcurves = fcurves_actionGroup_map[grp];
+ for (i = 0; i < fcurves.size(); i++)
+ add_bone_animation(fcurves[i], id_name(ob), std::string(grpname));
+
+ // ... for rotation
+ if (rotfcurves_actionGroup_map.find(grp) == rotfcurves_actionGroup_map.end())
+ continue;
+
+ // if rotation mode is euler - no need to convert it
+ if (pchan->rotmode == ROT_MODE_EUL) {
+
+ std::vector<FCurve*> &rotfcurves = rotfcurves_actionGroup_map[grp];
+
+ for (i = 0; i < rotfcurves.size(); i++)
+ add_bone_animation(rotfcurves[i], id_name(ob), std::string(grpname));
+ }
+
+ // convert rotation to euler & write animation
+ else change_quat_to_eul(ob, grp, grpname);
+ }
+ }
+ else {
+ while (fcu) {
+
+ if (!strcmp(fcu->rna_path, "location") ||
+ !strcmp(fcu->rna_path, "scale") ||
+ !strcmp(fcu->rna_path, "rotation")) {
+
+ add_animation(fcu, id_name(ob));
+ }
+
+ fcu = fcu->next;
+ }
+ }
+ }
+};
+
+void DocumentExporter::exportCurrentScene(Scene *sce, const char* filename)
+{
+ COLLADABU::NativeString native_filename =
+ COLLADABU::NativeString(std::string(filename));
+ COLLADASW::StreamWriter sw(native_filename);
+
+ // open <Collada>
+ sw.startDocument();
+
+ // <asset>
+ COLLADASW::Asset asset(&sw);
+ // XXX ask blender devs about this?
+ asset.setUnit("decimetre", 0.1);
+ asset.setUpAxisType(COLLADASW::Asset::Z_UP);
+ asset.add();
+
+ // <library_cameras>
+ CamerasExporter ce(&sw);
+ ce.exportCameras(sce);
+
+ // <library_lights>
+ LightsExporter le(&sw);
+ le.exportLights(sce);
+
+ // <library_images>
+ ImagesExporter ie(&sw, filename);
+ ie.exportImages(sce);
+
+ // <library_effects>
+ EffectsExporter ee(&sw);
+ ee.exportEffects(sce);
+
+ // <library_materials>
+ MaterialsExporter me(&sw);
+ me.exportMaterials(sce);
+
+ // <library_geometries>
+ GeometryExporter ge(&sw);
+ ge.exportGeom(sce);
+
+ // <library_animations>
+ AnimationExporter ae(&sw);
+ ae.exportAnimations(sce);
+
+ // <library_controllers>
+ ArmatureExporter arm_exporter(&sw);
+ arm_exporter.export_controllers(sce);
+
+ // <library_visual_scenes>
+ SceneExporter se(&sw, &arm_exporter);
+ se.exportScene(sce);
+
+ // <scene>
+ std::string scene_name(id_name(sce));
+ COLLADASW::Scene scene(&sw, COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING,
+ scene_name));
+ scene.add();
+
+ // close <Collada>
+ sw.endDocument();
+
+}
+
+void DocumentExporter::exportScenes(const char* filename)
+{
+}