vbo_MapRender.hpp 14.2 KB
Newer Older
Pierre Kraemer's avatar
Pierre Kraemer committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*******************************************************************************
* CGoGN: Combinatorial and Geometric modeling with Generic N-dimensional Maps  *
* version 0.1                                                                  *
* Copyright (C) 2009, IGG Team, LSIIT, University of Strasbourg                *
*                                                                              *
* This library is free software; you can redistribute it and/or modify it      *
* under the terms of the GNU Lesser General Public License as published by the *
* Free Software Foundation; either version 2.1 of the License, or (at your     *
* option) any later version.                                                   *
*                                                                              *
* This library is distributed in the hope that it will be useful, but WITHOUT  *
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        *
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License  *
* for more details.                                                            *
*                                                                              *
* You should have received a copy of the GNU Lesser General Public License     *
* along with this library; if not, write to the Free Software Foundation,      *
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.           *
*                                                                              *
* Web site: https://iggservis.u-strasbg.fr/CGoGN/                              *
* Contact information: cgogn@unistra.fr                                        *
*                                                                              *
*******************************************************************************/
24
25
#include "Topology/generic/dartmarker.h"
#include "Topology/generic/cellmarker.h"
Pierre Kraemer's avatar
Pierre Kraemer committed
26
27
28
29
30
31
32
33
34
35
36
37
38
39

namespace CGoGN
{

namespace Algo
{

namespace Render
{

namespace VBO
{


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// inline functions:
inline void MapRender_VBO::enableVertexAttrib(unsigned int index)
{
	m_usedAttributes[index] = true ;
}

inline void MapRender_VBO::disableVertexAttrib(unsigned int index)
{
	m_usedAttributes[index] = false ;
}


/**
 * enable a vertex attribute for rendering (updateDate automatically enable attrib)
 */
inline void MapRender_VBO::enableVertexAttrib(const std::string& name)
{
	std::map<std::string,unsigned int>::iterator it = m_attributebyName.find(name);
	if (it != m_attributebyName.end())
		enableVertexAttrib(it->second);
	else
		std::cerr <<"enableVertexAttrib: unknown attribute "<< name << std::endl;
}



inline void MapRender_VBO::disableVertexAttrib(const std::string& name)
{
	std::map<std::string,unsigned int>::iterator it = m_attributebyName.find(name);
	if (it != m_attributebyName.end())
		disableVertexAttrib(it->second);
	else
		std::cerr <<"disableVertexAttrib: unknown attribute "<< name << std::endl;
}



inline unsigned int MapRender_VBO::useVertexAttributeName(const std::string& name, const Utils::GLSLShader& sh)
{
	unsigned int vertex_attrib =0;

	std::map<std::string,unsigned int>::iterator it = m_attributebyName.find(name);
	if (it == m_attributebyName.end())
	{
		vertex_attrib = m_nbVertexAttrib++;
		m_attributebyName.insert(std::pair<std::string,unsigned int>(name,vertex_attrib));
	}
	else
		vertex_attrib = it->second;

	sh.bindAttrib(vertex_attrib+FIRST_VERTEX_ATTRIB,name.c_str());

	return vertex_attrib;
}


template <typename ATTR_HANDLER>
void MapRender_VBO::updateVAData(unsigned int vertex_attrib, const ATTR_HANDLER& attrib, ConvertAttrib* conv)
{
	// choisit le bon buffer en fonction du paramètre upType

	unsigned int indexVBO = vertex_attrib + FIRST_ATTRIBUTE_BUFFER;

	if (! m_allocatedAttributes[vertex_attrib] )
	{
		glGenBuffersARB(1, &(m_VBOBuffers[indexVBO])) ;
		m_allocatedAttributes[vertex_attrib] = true ;
	}


	m_usedAttributes[vertex_attrib] = true ;
	m_AttributesDataSize[vertex_attrib]= sizeof(typename ATTR_HANDLER::DATA_TYPE) / sizeof(float);


	if (conv)
		fillBufferConvert(indexVBO, attrib, conv) ;
	else
		fillBufferDirect(indexVBO, attrib) ;
}






template <typename ATTR_HANDLER>
void MapRender_VBO::updateVAData(const std::string& name, const ATTR_HANDLER& attrib, ConvertAttrib* conv)
{
	unsigned int vertex_attrib = 0;

	std::map<std::string,unsigned int>::iterator it = m_attributebyName.find(name);
	if (it == m_attributebyName.end())
	{
		vertex_attrib = m_nbVertexAttrib++;
		m_attributebyName.insert(std::pair<std::string,unsigned int>(name,vertex_attrib));
		std::cerr << "warning update data with unknown name, adding vertex attribute"<< std::endl;
	}
	else
	{
		vertex_attrib = it->second;
	}

	updateVAData<ATTR_HANDLER>(vertex_attrib,attrib,conv);
}

Pierre Kraemer's avatar
Pierre Kraemer committed
145
146

template <typename ATTR_HANDLER>
147
void MapRender_VBO::updateData(int upType, const ATTR_HANDLER& attrib, ConvertAttrib* conv)
Pierre Kraemer's avatar
Pierre Kraemer committed
148
149
150
151
152
153
154
155
156
157
158
{
	// choisit le bon buffer en fonction du paramètre upType
	unsigned int indexVBO = -1 ;
	if (upType == POSITIONS)
		indexVBO = POSITIONS_BUFFER ;
	else if (upType == NORMALS)
		indexVBO = NORMALS_BUFFER ;
	else if (upType == COLORS)
		indexVBO = COLORS_BUFFER ;
	else
	{
159
		std::cout << "MapRender_VBO::updateData : should not get here.. Bad type to update.." << std::endl ;
Pierre Kraemer's avatar
Pierre Kraemer committed
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
		return ;
	}

	m_allocatedBuffers[indexVBO] = true ;
	m_usedBuffers[indexVBO] = true ;

	if (conv)
		fillBufferConvert(indexVBO, attrib, conv) ;
	else
		fillBufferDirect(indexVBO, attrib) ;
}



template <typename ATTR_HANDLER>
175
void MapRender_VBO::fillBufferDirect(unsigned int indexVBO, const ATTR_HANDLER& attrib)
Pierre Kraemer's avatar
Pierre Kraemer committed
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
{
	AttribMultiVect<typename ATTR_HANDLER::DATA_TYPE>* mv = attrib.getDataVector() ;

	std::vector<void*> addr;
	unsigned int byteTableSize;
	unsigned int nbb = mv->getStartAddresses(addr, byteTableSize);

	glBindBufferARB(GL_ARRAY_BUFFER, m_VBOBuffers[indexVBO]);
	glBufferDataARB(GL_ARRAY_BUFFER, nbb * byteTableSize, 0, GL_STREAM_DRAW);

	unsigned int offset = 0;
//	unsigned int offsetInc = byteTableSize;

	for (unsigned int i = 0; i < nbb; ++i)
	{
		glBufferSubDataARB(GL_ARRAY_BUFFER, offset, byteTableSize, addr[i]);
		offset += byteTableSize;
	}
}

196

Pierre Kraemer's avatar
Pierre Kraemer committed
197
template <typename ATTR_HANDLER>
198
void MapRender_VBO::fillBufferConvert(unsigned int indexVBO, const ATTR_HANDLER& attrib, ConvertAttrib* conv)
Pierre Kraemer's avatar
Pierre Kraemer committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
{
	AttribMultiVect<typename ATTR_HANDLER::DATA_TYPE>* mv = attrib.getDataVector() ;

	std::vector<void*> addr;
	unsigned int byteTableSize;
	unsigned int nbb = mv->getStartAddresses(addr, byteTableSize);

	// alloue la memoire pour le buffer et initialise le conv
	conv->reserve(mv->BlockSize());

	// bind buffer to update
	glBindBufferARB(GL_ARRAY_BUFFER, m_VBOBuffers[indexVBO]);
	glBufferDataARB(GL_ARRAY_BUFFER, nbb * conv->sizeBuffer(), 0, GL_STREAM_DRAW);

	unsigned int offset = 0;
//	unsigned int offsetInc = byteTableSize;

	for (unsigned int i = 0; i < nbb; ++i)
	{
		// convertit les donnees dans le buffer de conv
		conv->convert(addr[i]);
		// update sub-vbo
		glBufferSubDataARB(GL_ARRAY_BUFFER, offset, conv->sizeBuffer(), conv->buffer());
		// block suivant
		offset += conv->sizeBuffer();
	}

	// libere la memoire de la conversion
	conv->release();
}

template<typename PFP>
231
inline void MapRender_VBO::addTri(typename PFP::MAP& map, Dart d, std::vector<GLuint>& tableIndices)
Pierre Kraemer's avatar
Pierre Kraemer committed
232
233
{
	Dart a = d;
234
235
	Dart b = map.phi1(a);
	Dart c = map.phi1(b);
Pierre Kraemer's avatar
Pierre Kraemer committed
236
237
238
239
	
	// loop to cut a polygon in triangle on the fly (works only with convex faces)
	do
	{
240
241
242
		tableIndices.push_back(map.getEmbedding(d, VERTEX_ORBIT));
		tableIndices.push_back(map.getEmbedding(b, VERTEX_ORBIT));
		tableIndices.push_back(map.getEmbedding(c, VERTEX_ORBIT));
Pierre Kraemer's avatar
Pierre Kraemer committed
243
		b = c;
244
		c = map.phi1(b);
Pierre Kraemer's avatar
Pierre Kraemer committed
245
246
247
248
	} while (c != d);
}

template<typename PFP>
Sylvain Thery's avatar
Sylvain Thery committed
249
void MapRender_VBO::initTriangles(typename PFP::MAP& map, const FunctorSelect& good, std::vector<GLuint>& tableIndices, unsigned int thread)
Pierre Kraemer's avatar
Pierre Kraemer committed
250
{
Sylvain Thery's avatar
Sylvain Thery committed
251
	DartMarker m(map,thread);
252
	tableIndices.reserve(4*map.getNbDarts()/3);
Pierre Kraemer's avatar
Pierre Kraemer committed
253

254
	for(Dart dd = map.begin(); dd != map.end(); map.next(dd))
Pierre Kraemer's avatar
Pierre Kraemer committed
255
	{
256
		if(!m.isMarked(dd) && good(dd))
Pierre Kraemer's avatar
Pierre Kraemer committed
257
		{
258
			addTri<PFP>(map, dd, tableIndices);
Pierre Kraemer's avatar
Pierre Kraemer committed
259
260
261
262
263
264
			m.markOrbit(FACE_ORBIT, dd);
		}
	}
}

template<typename PFP>
Sylvain Thery's avatar
Sylvain Thery committed
265
void MapRender_VBO::initTrianglesOptimized(typename PFP::MAP& map, const FunctorSelect& good, std::vector<GLuint>& tableIndices, unsigned int thread)
Pierre Kraemer's avatar
Pierre Kraemer committed
266
267
{
#define LIST_SIZE 20
Sylvain Thery's avatar
Sylvain Thery committed
268
	DartMarker m(map,thread);
Pierre Kraemer's avatar
Pierre Kraemer committed
269
270
271
	// reserve memory for triangles ( nb indices == nb darts )
	// and a little bit more
	// if lots of polygonal faces, realloc is done by vector 
272
	tableIndices.reserve(4*map.getNbDarts()/3);
Pierre Kraemer's avatar
Pierre Kraemer committed
273

274
	for (Dart dd = map.begin(); dd != map.end(); map.next(dd))
Pierre Kraemer's avatar
Pierre Kraemer committed
275
276
277
278
279
	{
		if (!m.isMarked(dd))
		{
			std::list<Dart> bound;

280
281
			if(good(dd))
				addTri<PFP>(map,dd,tableIndices);
Pierre Kraemer's avatar
Pierre Kraemer committed
282
283
284
285
286
287
288
289
290
291
292
293
294
295
			m.markOrbit(FACE_ORBIT, dd);
			bound.push_back(dd);
			int nb = 1;
			do
			{
				Dart e = bound.back();
				Dart ee = e;
				do
				{
					Dart f = ee;
					do
					{
						if (!m.isMarked(f))
						{
296
297
							if(good(f))
								addTri<PFP>(map, f, tableIndices);
Pierre Kraemer's avatar
Pierre Kraemer committed
298
							m.markOrbit(FACE_ORBIT, f);
299
							bound.push_back(map.phi1(f));
Pierre Kraemer's avatar
Pierre Kraemer committed
300
301
302
303
304
305
306
							++nb;
							if (nb > LIST_SIZE)
							{
								bound.pop_front();
								--nb;
							}
						}
307
						f = map.phi1(map.phi2(f));
Pierre Kraemer's avatar
Pierre Kraemer committed
308
					} while (f != ee);
309
					ee = map.phi1(ee);
Pierre Kraemer's avatar
Pierre Kraemer committed
310
311
312
313
314
315
316
317
318
319
320
				} while (ee != e);

				bound.pop_back();
				--nb;
			} while (!bound.empty());
		}
	}
#undef LIST_SIZE
}

template<typename PFP>
Sylvain Thery's avatar
Sylvain Thery committed
321
void MapRender_VBO::initLines(typename PFP::MAP& map, const FunctorSelect& good, std::vector<GLuint>& tableIndices, unsigned int thread)
Pierre Kraemer's avatar
Pierre Kraemer committed
322
{
Sylvain Thery's avatar
Sylvain Thery committed
323
	DartMarker m(map,thread);
324
	tableIndices.reserve(map.getNbDarts());
Pierre Kraemer's avatar
Pierre Kraemer committed
325

326
	for(Dart d = map.begin(); d != map.end(); map.next(d))
Pierre Kraemer's avatar
Pierre Kraemer committed
327
	{
328
		if(!m.isMarked(d) && good(d))
Pierre Kraemer's avatar
Pierre Kraemer committed
329
		{
330
331
			tableIndices.push_back(map.getEmbedding(d, VERTEX_ORBIT));
			tableIndices.push_back(map.getEmbedding(map.phi2(d), VERTEX_ORBIT));
Pierre Kraemer's avatar
Pierre Kraemer committed
332
333
334
335
336
337
			m.markOrbit(EDGE_ORBIT, d);
		}
	}
}

template<typename PFP>
Sylvain Thery's avatar
Sylvain Thery committed
338
void MapRender_VBO::initLinesOptimized(typename PFP::MAP& map, const FunctorSelect& good, std::vector<GLuint>& tableIndices, unsigned int thread)
Pierre Kraemer's avatar
Pierre Kraemer committed
339
340
{
#define LIST_SIZE 20
Sylvain Thery's avatar
Sylvain Thery committed
341
342
343

	DartMarker m(map,thread);

Pierre Kraemer's avatar
Pierre Kraemer committed
344
	// reserve memory for edges indices ( nb indices == nb darts)
345
	tableIndices.reserve(map.getNbDarts());
Pierre Kraemer's avatar
Pierre Kraemer committed
346

347
	for (Dart dd = map.begin(); dd != map.end(); map.next(dd))
Pierre Kraemer's avatar
Pierre Kraemer committed
348
	{
Sylvain Thery's avatar
Sylvain Thery committed
349
		if (!m.isMarked(dd) && good(dd))
Pierre Kraemer's avatar
Pierre Kraemer committed
350
351
352
353
354
355
356
357
358
359
		{
			std::list<Dart> bound;
			bound.push_back(dd);
			int nb = 1;
			do
			{
				Dart e = bound.back();
				Dart ee = e;
				do
				{
360
					Dart f = map.phi2(ee);
Pierre Kraemer's avatar
Pierre Kraemer committed
361
362
					if (!m.isMarked(ee))
					{
363
364
365
366
						if(good(ee))
							tableIndices.push_back(map.getEmbedding(ee, VERTEX_ORBIT));
						if(good(f))
							tableIndices.push_back(map.getEmbedding(map.phi1(ee), VERTEX_ORBIT));
Pierre Kraemer's avatar
Pierre Kraemer committed
367
368
369
370
371
372
373
374
375
376
						m.markOrbit(EDGE_ORBIT, f);

						bound.push_back(f);
						++nb;
						if (nb > LIST_SIZE)
						{
							bound.pop_front();
							--nb;
						}
					}
377
					ee = map.phi1(f);
Pierre Kraemer's avatar
Pierre Kraemer committed
378
379
380
381
382
383
384
385
386
387
				} while (ee != e);
				bound.pop_back();
				--nb;
			} while (!bound.empty());
		}
	}
#undef LIST_SIZE
}

template<typename PFP>
Sylvain Thery's avatar
Sylvain Thery committed
388
void MapRender_VBO::initPoints(typename PFP::MAP& map, const FunctorSelect& good, std::vector<GLuint>& tableIndices, unsigned int thread)
Pierre Kraemer's avatar
Pierre Kraemer committed
389
{
Sylvain Thery's avatar
Sylvain Thery committed
390
	CellMarker m(map, VERTEX_ORBIT,thread) ;
391
	tableIndices.reserve(map.getNbDarts()/5);
Pierre Kraemer's avatar
Pierre Kraemer committed
392

393
	for(Dart d = map.begin(); d != map.end(); map.next(d))
Pierre Kraemer's avatar
Pierre Kraemer committed
394
	{
395
		if(!m.isMarked(d) && good(d))
Pierre Kraemer's avatar
Pierre Kraemer committed
396
		{
397
			tableIndices.push_back(map.getEmbedding(d, VERTEX_ORBIT));
Pierre Kraemer's avatar
Pierre Kraemer committed
398
399
400
401
402
403
			m.mark(d) ;
		}
	}
}

template<typename PFP>
Sylvain Thery's avatar
Sylvain Thery committed
404
void MapRender_VBO::initPrimitives(typename PFP::MAP& map, const FunctorSelect& good, int prim, bool optimized, unsigned int thread)
Pierre Kraemer's avatar
Pierre Kraemer committed
405
406
407
408
409
410
411
412
{
	std::vector<GLuint> tableIndices;

	// indice du VBO a utiliser
	int vbo_ind = 0;
	switch(prim)
	{
		case FLAT_TRIANGLES:
Sylvain Thery's avatar
Sylvain Thery committed
413
			initFlatTriangles<PFP>(map,good,thread);
Pierre Kraemer's avatar
Pierre Kraemer committed
414
415
416
417
			break;

		case TRIANGLES:
			if(optimized)
Sylvain Thery's avatar
Sylvain Thery committed
418
				initTrianglesOptimized<PFP>(map,good,tableIndices,thread);
Pierre Kraemer's avatar
Pierre Kraemer committed
419
			else
Sylvain Thery's avatar
Sylvain Thery committed
420
				initTriangles<PFP>(map,good,tableIndices,thread) ;
Pierre Kraemer's avatar
Pierre Kraemer committed
421
422
423
424
425
			m_nbIndicesTri = tableIndices.size();
			vbo_ind = m_VBOBuffers[TRIANGLE_INDICES];
			break;
		case LINES:
			if(optimized)
Sylvain Thery's avatar
Sylvain Thery committed
426
				initLinesOptimized<PFP>(map,good,tableIndices,thread);
Pierre Kraemer's avatar
Pierre Kraemer committed
427
			else
Sylvain Thery's avatar
Sylvain Thery committed
428
				initLines<PFP>(map,good,tableIndices,thread) ;
Pierre Kraemer's avatar
Pierre Kraemer committed
429
430
431
432
			m_nbIndicesLines = tableIndices.size();
			vbo_ind = m_VBOBuffers[LINE_INDICES];
			break;
		case POINTS:
Sylvain Thery's avatar
Sylvain Thery committed
433
			initPoints<PFP>(map,good,tableIndices,thread);
Pierre Kraemer's avatar
Pierre Kraemer committed
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
			m_nbIndicesPoints = tableIndices.size();
			vbo_ind = m_VBOBuffers[POINT_INDICES];
			break;
		default:
			std::cerr << "problem initializing VBO indices" << std::endl;
			break;
	}
	int size = tableIndices.size();

	// setup du buffer d'indices
	glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, vbo_ind);
	glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, size*sizeof(GLuint), &(tableIndices[0]), GL_STREAM_DRAW);
}




template<typename PFP>
Sylvain Thery's avatar
Sylvain Thery committed
452
void MapRender_VBO::initFlatTriangles(typename PFP::MAP& map, const FunctorSelect& good, unsigned int thread)
Pierre Kraemer's avatar
Pierre Kraemer committed
453
454
{
	std::vector<Geom::Vec3f> tableFlat;
455
	tableFlat.reserve(3*map.getNbDarts()); // 3 in case of polygonal faces (less chance of realloc, but bigger allocation)
Pierre Kraemer's avatar
Pierre Kraemer committed
456
457
458
459
460
461
462

	// map VBO of points for vertices positions
	glBindBufferARB(GL_ARRAY_BUFFER, m_VBOBuffers[POSITIONS_BUFFER]);
	Geom::Vec3f* tablePos = reinterpret_cast<Geom::Vec3f*>(glMapBuffer(GL_ARRAY_BUFFER,GL_READ_ONLY));

	m_nbFlatElts=0;
	// traversal of map for creating buffers
Sylvain Thery's avatar
Sylvain Thery committed
463
	DartMarker m(map,thread);
464
	for(Dart dd = map.begin(); dd != map.end(); map.next(dd))
Pierre Kraemer's avatar
Pierre Kraemer committed
465
	{
466
		if(!m.isMarked(dd) && good(dd))
Pierre Kraemer's avatar
Pierre Kraemer committed
467
468
		{
			Dart a = dd;
469
470
			Dart b = map.phi1(a);
			Dart c = map.phi1(b);
Pierre Kraemer's avatar
Pierre Kraemer committed
471

472
473
474
			Geom::Vec3f& P = tablePos[map.getEmbedding(a, VERTEX_ORBIT)];
			Geom::Vec3f& Q = tablePos[map.getEmbedding(b, VERTEX_ORBIT)];
			Geom::Vec3f& R = tablePos[map.getEmbedding(c, VERTEX_ORBIT)];
Pierre Kraemer's avatar
Pierre Kraemer committed
475
476
477
478
479
480
481
482
483

			Geom::Vec3f U = Q-P;
			Geom::Vec3f V = R-P;
			Geom::Vec3f N = U^V;
			N.normalize();

			// loop to cut a polygon in triangle on the fly (works only with convex faces)
			do
			{
484
				tableFlat.push_back(tablePos[map.getEmbedding(a, VERTEX_ORBIT)]);
Pierre Kraemer's avatar
Pierre Kraemer committed
485
				tableFlat.push_back(N);
486
				tableFlat.push_back(tablePos[map.getEmbedding(b, VERTEX_ORBIT)]);
Pierre Kraemer's avatar
Pierre Kraemer committed
487
				tableFlat.push_back(N);
488
				tableFlat.push_back(tablePos[map.getEmbedding(c, VERTEX_ORBIT)]);
Pierre Kraemer's avatar
Pierre Kraemer committed
489
490
				tableFlat.push_back(N);
				b = c;
491
				c = map.phi1(b);
Pierre Kraemer's avatar
Pierre Kraemer committed
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
			} while (c != dd);
			m.markOrbit(FACE_ORBIT, dd);
		}
	}
	glUnmapBuffer(GL_ARRAY_BUFFER);

	m_nbFlatElts = tableFlat.size()/2;

	//creating VBO for flat
	glBindBufferARB(GL_ARRAY_BUFFER, m_VBOBuffers[FLAT_BUFFER]);
	glBufferDataARB(GL_ARRAY_BUFFER, tableFlat.size() * sizeof(Geom::Vec3f), (char*)(&(tableFlat[0])), GL_STREAM_DRAW);

}





} // namespace VBO

} // namespace Render

} // namespace Algo

} // namespace CGoGN