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