bda6fec70a8e93a4fbcf28ee0ab17ca8293ad7be
[blender.git] / source / blender / collada / GeometryExporter.cpp
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup collada
19  */
20
21 #include <sstream>
22
23 #include "COLLADASWPrimitves.h"
24 #include "COLLADASWSource.h"
25 #include "COLLADASWVertices.h"
26 #include "COLLADABUUtils.h"
27
28 #include "GeometryExporter.h"
29
30 #include "DNA_meshdata_types.h"
31
32 extern "C" {
33 #include "BLI_utildefines.h"
34
35 #include "BKE_customdata.h"
36 #include "BKE_global.h"
37 #include "BKE_library.h"
38 #include "BKE_material.h"
39 #include "BKE_mesh.h"
40 }
41
42 #include "collada_internal.h"
43 #include "collada_utils.h"
44
45 void GeometryExporter::exportGeom()
46 {
47   Scene *sce = blender_context.get_scene();
48   openLibrary();
49
50   GeometryFunctor gf;
51   gf.forEachMeshObjectInExportSet<GeometryExporter>(sce, *this, this->export_settings->export_set);
52
53   closeLibrary();
54 }
55
56 void GeometryExporter::operator()(Object *ob)
57 {
58   bool use_instantiation = this->export_settings->use_object_instantiation;
59   Mesh *me = bc_get_mesh_copy(blender_context,
60                               ob,
61                               this->export_settings->export_mesh_type,
62                               this->export_settings->apply_modifiers,
63                               this->export_settings->triangulate);
64
65   std::string geom_id = get_geometry_id(ob, use_instantiation);
66   std::vector<Normal> nor;
67   std::vector<BCPolygonNormalsIndices> norind;
68
69   /* Skip if linked geometry was already exported from another reference */
70   if (use_instantiation && exportedGeometry.find(geom_id) != exportedGeometry.end()) {
71     return;
72   }
73
74   std::string geom_name = (use_instantiation) ? id_name(ob->data) : id_name(ob);
75   geom_name = encode_xml(geom_name);
76
77   exportedGeometry.insert(geom_id);
78
79   bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL);
80
81   create_normals(nor, norind, me);
82
83   /* openMesh(geoId, geoName, meshId) */
84   openMesh(geom_id, geom_name);
85
86   /* writes <source> for vertex coords */
87   createVertsSource(geom_id, me);
88
89   /* writes <source> for normal coords */
90   createNormalsSource(geom_id, me, nor);
91
92   bool has_uvs = (bool)CustomData_has_layer(&me->ldata, CD_MLOOPUV);
93
94   /* writes <source> for uv coords if mesh has uv coords */
95   if (has_uvs) {
96     createTexcoordsSource(geom_id, me);
97   }
98
99   if (has_color) {
100     createVertexColorSource(geom_id, me);
101   }
102   /* <vertices> */
103
104   COLLADASW::Vertices verts(mSW);
105   verts.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX));
106   COLLADASW::InputList &input_list = verts.getInputList();
107   COLLADASW::Input input(COLLADASW::InputSemantic::POSITION,
108                          getUrlBySemantics(geom_id, COLLADASW::InputSemantic::POSITION));
109   input_list.push_back(input);
110   verts.add();
111
112   createLooseEdgeList(ob, me, geom_id);
113
114   /* Only create Polylists if number of faces > 0 */
115   if (me->totface > 0) {
116     /* XXX slow */
117     if (ob->totcol) {
118       for (int a = 0; a < ob->totcol; a++) {
119         create_mesh_primitive_list(a, has_uvs, has_color, ob, me, geom_id, norind);
120       }
121     }
122     else {
123       create_mesh_primitive_list(0, has_uvs, has_color, ob, me, geom_id, norind);
124     }
125   }
126
127   closeMesh();
128
129   closeGeometry();
130
131   if (this->export_settings->include_shapekeys) {
132     Key *key = BKE_key_from_object(ob);
133     if (key) {
134       KeyBlock *kb = (KeyBlock *)key->block.first;
135       /* skip the basis */
136       kb = kb->next;
137       for (; kb; kb = kb->next) {
138         BKE_keyblock_convert_to_mesh(kb, me);
139         export_key_mesh(ob, me, kb);
140       }
141     }
142   }
143
144   BKE_id_free(NULL, me);
145 }
146
147 void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb)
148 {
149   std::string geom_id = get_geometry_id(ob, false) + "_morph_" + translate_id(kb->name);
150   std::vector<Normal> nor;
151   std::vector<BCPolygonNormalsIndices> norind;
152
153   if (exportedGeometry.find(geom_id) != exportedGeometry.end()) {
154     return;
155   }
156
157   std::string geom_name = kb->name;
158
159   exportedGeometry.insert(geom_id);
160
161   bool has_color = (bool)CustomData_has_layer(&me->fdata, CD_MCOL);
162
163   create_normals(nor, norind, me);
164
165   // openMesh(geoId, geoName, meshId)
166   openMesh(geom_id, geom_name);
167
168   /* writes <source> for vertex coords */
169   createVertsSource(geom_id, me);
170
171   /* writes <source> for normal coords */
172   createNormalsSource(geom_id, me, nor);
173
174   bool has_uvs = (bool)CustomData_has_layer(&me->ldata, CD_MLOOPUV);
175
176   /* writes <source> for uv coords if mesh has uv coords */
177   if (has_uvs) {
178     createTexcoordsSource(geom_id, me);
179   }
180
181   if (has_color) {
182     createVertexColorSource(geom_id, me);
183   }
184
185   /* <vertices> */
186
187   COLLADASW::Vertices verts(mSW);
188   verts.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX));
189   COLLADASW::InputList &input_list = verts.getInputList();
190   COLLADASW::Input input(COLLADASW::InputSemantic::POSITION,
191                          getUrlBySemantics(geom_id, COLLADASW::InputSemantic::POSITION));
192   input_list.push_back(input);
193   verts.add();
194
195   // createLooseEdgeList(ob, me, geom_id, norind);
196
197   /* XXX slow */
198   if (ob->totcol) {
199     for (int a = 0; a < ob->totcol; a++) {
200       create_mesh_primitive_list(a, has_uvs, has_color, ob, me, geom_id, norind);
201     }
202   }
203   else {
204     create_mesh_primitive_list(0, has_uvs, has_color, ob, me, geom_id, norind);
205   }
206
207   closeMesh();
208
209   closeGeometry();
210 }
211
212 void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id)
213 {
214
215   MEdge *medges = me->medge;
216   int totedges = me->totedge;
217   int edges_in_linelist = 0;
218   std::vector<unsigned int> edge_list;
219   int index;
220
221   /* Find all loose edges in Mesh
222    * and save vertex indices in edge_list */
223   for (index = 0; index < totedges; index++) {
224     MEdge *edge = &medges[index];
225
226     if (edge->flag & ME_LOOSEEDGE) {
227       edges_in_linelist += 1;
228       edge_list.push_back(edge->v1);
229       edge_list.push_back(edge->v2);
230     }
231   }
232
233   if (edges_in_linelist > 0) {
234     /* Create the list of loose edges */
235     COLLADASW::Lines lines(mSW);
236
237     lines.setCount(edges_in_linelist);
238
239     COLLADASW::InputList &til = lines.getInputList();
240
241     /* creates <input> in <lines> for vertices */
242     COLLADASW::Input input1(COLLADASW::InputSemantic::VERTEX,
243                             getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX),
244                             0);
245     til.push_back(input1);
246
247     lines.prepareToAppendValues();
248
249     for (index = 0; index < edges_in_linelist; index++) {
250       lines.appendValues(edge_list[2 * index + 1]);
251       lines.appendValues(edge_list[2 * index]);
252     }
253     lines.finish();
254   }
255 }
256
257 static void prepareToAppendValues(bool is_triangulated,
258                                   COLLADASW::PrimitivesBase &primitive_list,
259                                   std::vector<unsigned long> &vcount_list)
260 {
261   /* performs the actual writing */
262   if (is_triangulated) {
263     ((COLLADASW::Triangles &)primitive_list).prepareToAppendValues();
264   }
265   else {
266     /* sets <vcount> */
267     primitive_list.setVCountList(vcount_list);
268     ((COLLADASW::Polylist &)primitive_list).prepareToAppendValues();
269   }
270 }
271
272 static void finish_and_delete_primitive_List(bool is_triangulated,
273                                              COLLADASW::PrimitivesBase *primitive_list)
274 {
275   if (is_triangulated) {
276     ((COLLADASW::Triangles *)primitive_list)->finish();
277   }
278   else {
279     ((COLLADASW::Polylist *)primitive_list)->finish();
280   }
281   delete primitive_list;
282 }
283
284 static COLLADASW::PrimitivesBase *create_primitive_list(bool is_triangulated,
285                                                         COLLADASW::StreamWriter *mSW)
286 {
287   COLLADASW::PrimitivesBase *primitive_list;
288
289   if (is_triangulated) {
290     primitive_list = new COLLADASW::Triangles(mSW);
291   }
292   else {
293     primitive_list = new COLLADASW::Polylist(mSW);
294   }
295   return primitive_list;
296 }
297
298 static bool collect_vertex_counts_per_poly(Mesh *me,
299                                            int material_index,
300                                            std::vector<unsigned long> &vcount_list)
301 {
302   MPoly *mpolys = me->mpoly;
303   int totpolys = me->totpoly;
304   bool is_triangulated = true;
305
306   int i;
307   /* Expecting that p->mat_nr is always 0 if the mesh has no materials assigned */
308   for (i = 0; i < totpolys; i++) {
309     MPoly *p = &mpolys[i];
310     if (p->mat_nr == material_index) {
311       int vertex_count = p->totloop;
312       vcount_list.push_back(vertex_count);
313       if (vertex_count != 3)
314         is_triangulated = false;
315     }
316   }
317   return is_triangulated;
318 }
319
320 std::string GeometryExporter::makeVertexColorSourceId(std::string &geom_id, char *layer_name)
321 {
322   std::string result = getIdBySemantics(geom_id, COLLADASW::InputSemantic::COLOR) + "-" +
323                        layer_name;
324   return result;
325 }
326
327 /* powerful because it handles both cases when there is material and when there's not */
328 void GeometryExporter::create_mesh_primitive_list(short material_index,
329                                                   bool has_uvs,
330                                                   bool has_color,
331                                                   Object *ob,
332                                                   Mesh *me,
333                                                   std::string &geom_id,
334                                                   std::vector<BCPolygonNormalsIndices> &norind)
335 {
336
337   MPoly *mpolys = me->mpoly;
338   MLoop *mloops = me->mloop;
339   int totpolys = me->totpoly;
340
341   std::vector<unsigned long> vcount_list;
342
343   bool is_triangulated = collect_vertex_counts_per_poly(me, material_index, vcount_list);
344   int polygon_count = vcount_list.size();
345
346   /* no faces using this material */
347   if (polygon_count == 0) {
348     fprintf(
349         stderr, "%s: material with index %d is not used.\n", id_name(ob).c_str(), material_index);
350     return;
351   }
352
353   Material *ma = ob->totcol ? give_current_material(ob, material_index + 1) : NULL;
354   COLLADASW::PrimitivesBase *primitive_list = create_primitive_list(is_triangulated, mSW);
355
356   /* sets count attribute in <polylist> */
357   primitive_list->setCount(polygon_count);
358
359   /* sets material name */
360   if (ma) {
361     std::string material_id = get_material_id(ma);
362     std::ostringstream ostr;
363     ostr << translate_id(material_id);
364     primitive_list->setMaterial(ostr.str());
365   }
366
367   COLLADASW::Input vertex_input(COLLADASW::InputSemantic::VERTEX,
368                                 getUrlBySemantics(geom_id, COLLADASW::InputSemantic::VERTEX),
369                                 0);
370   COLLADASW::Input normals_input(COLLADASW::InputSemantic::NORMAL,
371                                  getUrlBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL),
372                                  1);
373
374   COLLADASW::InputList &til = primitive_list->getInputList();
375   til.push_back(vertex_input);
376   til.push_back(normals_input);
377
378   /* if mesh has uv coords writes <input> for TEXCOORD */
379   int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
380   int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV);
381   for (int i = 0; i < num_layers; i++) {
382     int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_MLOOPUV, i);
383     if (!this->export_settings->active_uv_only || layer_index == active_uv_index) {
384
385       // char *name = CustomData_get_layer_name(&me->ldata, CD_MLOOPUV, i);
386       COLLADASW::Input texcoord_input(
387           COLLADASW::InputSemantic::TEXCOORD,
388           makeUrl(makeTexcoordSourceId(geom_id, i, this->export_settings->active_uv_only)),
389           2,  // this is only until we have optimized UV sets
390           (this->export_settings->active_uv_only) ? 0 : layer_index - 1 /* set (0,1,2,...) */
391       );
392       til.push_back(texcoord_input);
393     }
394   }
395
396   int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL);
397   if (totlayer_mcol > 0) {
398     int map_index = 0;
399
400     for (int a = 0; a < totlayer_mcol; a++) {
401       char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a);
402       COLLADASW::Input input4(COLLADASW::InputSemantic::COLOR,
403                               makeUrl(makeVertexColorSourceId(geom_id, layer_name)),
404                               (has_uvs) ? 3 : 2,  // all color layers have same index order
405                               map_index           // set number equals color map index
406       );
407       til.push_back(input4);
408       map_index++;
409     }
410   }
411
412   /* performs the actual writing */
413   prepareToAppendValues(is_triangulated, *primitive_list, vcount_list);
414
415   /* <p> */
416   int texindex = 0;
417   for (int i = 0; i < totpolys; i++) {
418     MPoly *p = &mpolys[i];
419     int loop_count = p->totloop;
420
421     if (p->mat_nr == material_index) {
422       MLoop *l = &mloops[p->loopstart];
423       BCPolygonNormalsIndices normal_indices = norind[i];
424
425       for (int j = 0; j < loop_count; j++) {
426         primitive_list->appendValues(l[j].v);
427         primitive_list->appendValues(normal_indices[j]);
428         if (has_uvs)
429           primitive_list->appendValues(texindex + j);
430
431         if (has_color)
432           primitive_list->appendValues(texindex + j);
433       }
434     }
435
436     texindex += loop_count;
437   }
438
439   finish_and_delete_primitive_List(is_triangulated, primitive_list);
440 }
441
442 /* creates <source> for positions */
443 void GeometryExporter::createVertsSource(std::string geom_id, Mesh *me)
444 {
445 #if 0
446   int totverts = dm->getNumVerts(dm);
447   MVert *verts = dm->getVertArray(dm);
448 #endif
449   int totverts = me->totvert;
450   MVert *verts = me->mvert;
451
452   COLLADASW::FloatSourceF source(mSW);
453   source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION));
454   source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::POSITION) +
455                     ARRAY_ID_SUFFIX);
456   source.setAccessorCount(totverts);
457   source.setAccessorStride(3);
458
459   COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
460   param.push_back("X");
461   param.push_back("Y");
462   param.push_back("Z");
463   /* main function, it creates <source id = "">, <float_array id = ""
464    * count = ""> */
465   source.prepareToAppendValues();
466   /* appends data to <float_array> */
467   int i = 0;
468   for (i = 0; i < totverts; i++) {
469     source.appendValues(verts[i].co[0], verts[i].co[1], verts[i].co[2]);
470   }
471
472   source.finish();
473 }
474
475 void GeometryExporter::createVertexColorSource(std::string geom_id, Mesh *me)
476 {
477   /* Find number of vertex color layers */
478   int totlayer_mcol = CustomData_number_of_layers(&me->ldata, CD_MLOOPCOL);
479   if (totlayer_mcol == 0)
480     return;
481
482   int map_index = 0;
483   for (int a = 0; a < totlayer_mcol; a++) {
484
485     map_index++;
486     MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(&me->ldata, CD_MLOOPCOL, a);
487
488     COLLADASW::FloatSourceF source(mSW);
489
490     char *layer_name = bc_CustomData_get_layer_name(&me->ldata, CD_MLOOPCOL, a);
491     std::string layer_id = makeVertexColorSourceId(geom_id, layer_name);
492     source.setId(layer_id);
493
494     source.setNodeName(layer_name);
495
496     source.setArrayId(layer_id + ARRAY_ID_SUFFIX);
497     source.setAccessorCount(me->totloop);
498     source.setAccessorStride(4);
499
500     COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
501     param.push_back("R");
502     param.push_back("G");
503     param.push_back("B");
504     param.push_back("A");
505
506     source.prepareToAppendValues();
507
508     MPoly *mpoly;
509     int i;
510     for (i = 0, mpoly = me->mpoly; i < me->totpoly; i++, mpoly++) {
511       MLoopCol *mlc = mloopcol + mpoly->loopstart;
512       for (int j = 0; j < mpoly->totloop; j++, mlc++) {
513         source.appendValues(mlc->r / 255.0f, mlc->g / 255.0f, mlc->b / 255.0f, mlc->a / 255.0f);
514       }
515     }
516
517     source.finish();
518   }
519 }
520
521 std::string GeometryExporter::makeTexcoordSourceId(std::string &geom_id,
522                                                    int layer_index,
523                                                    bool is_single_layer)
524 {
525   char suffix[20];
526   if (is_single_layer) {
527     suffix[0] = '\0';
528   }
529   else {
530     sprintf(suffix, "-%d", layer_index);
531   }
532   return getIdBySemantics(geom_id, COLLADASW::InputSemantic::TEXCOORD) + suffix;
533 }
534
535 /* creates <source> for texcoords */
536 void GeometryExporter::createTexcoordsSource(std::string geom_id, Mesh *me)
537 {
538
539   int totpoly = me->totpoly;
540   int totuv = me->totloop;
541   MPoly *mpolys = me->mpoly;
542
543   int num_layers = CustomData_number_of_layers(&me->ldata, CD_MLOOPUV);
544
545   /* write <source> for each layer
546    * each <source> will get id like meshName + "map-channel-1" */
547   int active_uv_index = CustomData_get_active_layer_index(&me->ldata, CD_MLOOPUV);
548   for (int a = 0; a < num_layers; a++) {
549     int layer_index = CustomData_get_layer_index_n(&me->ldata, CD_MLOOPUV, a);
550     if (!this->export_settings->active_uv_only || layer_index == active_uv_index) {
551       MLoopUV *mloops = (MLoopUV *)CustomData_get_layer_n(&me->ldata, CD_MLOOPUV, a);
552
553       COLLADASW::FloatSourceF source(mSW);
554       std::string layer_id = makeTexcoordSourceId(
555           geom_id, a, this->export_settings->active_uv_only);
556       source.setId(layer_id);
557       source.setArrayId(layer_id + ARRAY_ID_SUFFIX);
558
559       source.setAccessorCount(totuv);
560       source.setAccessorStride(2);
561       COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
562       param.push_back("S");
563       param.push_back("T");
564
565       source.prepareToAppendValues();
566
567       for (int index = 0; index < totpoly; index++) {
568         MPoly *mpoly = mpolys + index;
569         MLoopUV *mloop = mloops + mpoly->loopstart;
570         for (int j = 0; j < mpoly->totloop; j++) {
571           source.appendValues(mloop[j].uv[0], mloop[j].uv[1]);
572         }
573       }
574
575       source.finish();
576     }
577   }
578 }
579
580 bool operator<(const Normal &a, const Normal &b)
581 {
582   /* only needed to sort normal vectors and find() them later in a map.*/
583   return a.x < b.x || (a.x == b.x && (a.y < b.y || (a.y == b.y && a.z < b.z)));
584 }
585
586 /* creates <source> for normals */
587 void GeometryExporter::createNormalsSource(std::string geom_id, Mesh *me, std::vector<Normal> &nor)
588 {
589 #if 0
590   int totverts = dm->getNumVerts(dm);
591   MVert *verts = dm->getVertArray(dm);
592 #endif
593
594   COLLADASW::FloatSourceF source(mSW);
595   source.setId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL));
596   source.setArrayId(getIdBySemantics(geom_id, COLLADASW::InputSemantic::NORMAL) + ARRAY_ID_SUFFIX);
597   source.setAccessorCount((unsigned long)nor.size());
598   source.setAccessorStride(3);
599   COLLADASW::SourceBase::ParameterNameList &param = source.getParameterNameList();
600   param.push_back("X");
601   param.push_back("Y");
602   param.push_back("Z");
603
604   source.prepareToAppendValues();
605
606   std::vector<Normal>::iterator it;
607   for (it = nor.begin(); it != nor.end(); it++) {
608     Normal &n = *it;
609     source.appendValues(n.x, n.y, n.z);
610   }
611
612   source.finish();
613 }
614
615 void GeometryExporter::create_normals(std::vector<Normal> &normals,
616                                       std::vector<BCPolygonNormalsIndices> &polygons_normals,
617                                       Mesh *me)
618 {
619   std::map<Normal, unsigned int> shared_normal_indices;
620   int last_normal_index = -1;
621
622   MVert *verts = me->mvert;
623   MLoop *mloops = me->mloop;
624   float(*lnors)[3] = NULL;
625   bool use_custom_normals = false;
626
627   BKE_mesh_calc_normals_split(me);
628   if (CustomData_has_layer(&me->ldata, CD_NORMAL)) {
629     lnors = (float(*)[3])CustomData_get_layer(&me->ldata, CD_NORMAL);
630     use_custom_normals = true;
631   }
632
633   for (int poly_index = 0; poly_index < me->totpoly; poly_index++) {
634     MPoly *mpoly = &me->mpoly[poly_index];
635     bool use_vertex_normals = use_custom_normals || mpoly->flag & ME_SMOOTH;
636
637     if (!use_vertex_normals) {
638       /* For flat faces use face normal as vertex normal: */
639
640       float vector[3];
641       BKE_mesh_calc_poly_normal(mpoly, mloops + mpoly->loopstart, verts, vector);
642
643       Normal n = {vector[0], vector[1], vector[2]};
644       normals.push_back(n);
645       last_normal_index++;
646     }
647
648     BCPolygonNormalsIndices poly_indices;
649     for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) {
650       unsigned int loop_idx = mpoly->loopstart + loop_index;
651       if (use_vertex_normals) {
652         float normalized[3];
653
654         if (use_custom_normals) {
655           normalize_v3_v3(normalized, lnors[loop_idx]);
656         }
657         else {
658           normal_short_to_float_v3(normalized, verts[mloops[loop_index].v].no);
659           normalize_v3(normalized);
660         }
661         Normal n = {normalized[0], normalized[1], normalized[2]};
662
663         if (shared_normal_indices.find(n) != shared_normal_indices.end()) {
664           poly_indices.add_index(shared_normal_indices[n]);
665         }
666         else {
667           last_normal_index++;
668           poly_indices.add_index(last_normal_index);
669           shared_normal_indices[n] = last_normal_index;
670           normals.push_back(n);
671         }
672       }
673       else {
674         poly_indices.add_index(last_normal_index);
675       }
676     }
677
678     polygons_normals.push_back(poly_indices);
679   }
680 }
681
682 std::string GeometryExporter::getIdBySemantics(std::string geom_id,
683                                                COLLADASW::InputSemantic::Semantics type,
684                                                std::string other_suffix)
685 {
686   return geom_id + getSuffixBySemantic(type) + other_suffix;
687 }
688
689 COLLADASW::URI GeometryExporter::getUrlBySemantics(std::string geom_id,
690                                                    COLLADASW::InputSemantic::Semantics type,
691                                                    std::string other_suffix)
692 {
693
694   std::string id(getIdBySemantics(geom_id, type, other_suffix));
695   return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
696 }
697
698 COLLADASW::URI GeometryExporter::makeUrl(std::string id)
699 {
700   return COLLADASW::URI(COLLADABU::Utils::EMPTY_STRING, id);
701 }