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