1 /*
2 * Copyright © 2008 Ben Smith
3 * Copyright © 2010-2011 Linaro Limited
4 *
5 * This file is part of the glmark2 OpenGL (ES) 2.0 benchmark.
6 *
7 * glmark2 is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later
10 * version.
11 *
12 * glmark2 is distributed in the hope that it will be useful, but WITHOUT ANY
13 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
15 * details.
16 *
17 * You should have received a copy of the GNU General Public License along with
18 * glmark2. If not, see <http://www.gnu.org/licenses/>.
19 *
20 * Authors:
21 * Ben Smith (original glmark benchmark)
22 * Alexandros Frantzis (glmark2)
23 */
24 #include "mesh.h"
25 #include "log.h"
26 #include "gl-headers.h"
27
28
Mesh()29 Mesh::Mesh() :
30 vertex_size_(0), interleave_(false), vbo_update_method_(VBOUpdateMethodMap),
31 vbo_usage_(VBOUsageStatic)
32 {
33 }
34
~Mesh()35 Mesh::~Mesh()
36 {
37 reset();
38 }
39
40 /*
41 * Sets the vertex format for this mesh.
42 *
43 * The format consists of a vector of integers, each
44 * specifying the size in floats of each vertex attribute.
45 *
46 * e.g. {4, 3, 2} => 3 attributes vec4, vec3, vec2
47 */
48 void
set_vertex_format(const std::vector<int> & format)49 Mesh::set_vertex_format(const std::vector<int> &format)
50 {
51 int pos = 0;
52 vertex_format_.clear();
53
54 for (std::vector<int>::const_iterator iter = format.begin();
55 iter != format.end();
56 iter++)
57 {
58 int n = *iter;
59 vertex_format_.push_back(std::pair<int,int>(n, pos));
60
61 pos += n;
62 }
63
64 vertex_size_ = pos;
65 }
66
67 /*
68 * Sets the attribute locations.
69 *
70 * These are the locations used in glEnableVertexAttribArray()
71 * and other related functions.
72 */
73 void
set_attrib_locations(const std::vector<int> & locations)74 Mesh::set_attrib_locations(const std::vector<int> &locations)
75 {
76 if (locations.size() != vertex_format_.size())
77 Log::error("Trying to set attribute locations using wrong size\n");
78 attrib_locations_ = locations;
79 }
80
81
82 /**
83 * Checks that an attribute is of the correct dimensionality.
84 *
85 * @param pos the position/index of the attribute to check
86 * @param dim the size of the attribute (in #floats)
87 *
88 * @return whether the check succeeded
89 */
90 bool
check_attrib(unsigned int pos,int dim)91 Mesh::check_attrib(unsigned int pos, int dim)
92 {
93 if (pos > vertex_format_.size()) {
94 Log::error("Trying to set non-existent attribute\n");
95 return false;
96 }
97
98 if (vertex_format_[pos].first != dim) {
99 Log::error("Trying to set attribute with value of invalid type\n");
100 return false;
101 }
102
103 return true;
104 }
105
106
107 /**
108 * Ensures that we have a vertex to process.
109 *
110 * @return the vertex to process
111 */
112 std::vector<float> &
ensure_vertex()113 Mesh::ensure_vertex()
114 {
115 if (vertices_.empty())
116 next_vertex();
117
118 return vertices_.back();
119 }
120
121 /*
122 * Sets the value of an attribute in the current vertex.
123 *
124 * The pos parameter refers to the position of the attribute
125 * as specified indirectly when setting the format using
126 * set_vertex_format(). e.g. 0 = first attribute, 1 = second
127 * etc
128 */
129 void
set_attrib(unsigned int pos,const LibMatrix::vec2 & v,std::vector<float> * vertex)130 Mesh::set_attrib(unsigned int pos, const LibMatrix::vec2 &v, std::vector<float> *vertex)
131 {
132 if (!check_attrib(pos, 2))
133 return;
134
135 std::vector<float> &vtx = !vertex ? ensure_vertex() : *vertex;
136
137 int offset = vertex_format_[pos].second;
138
139 vtx[offset] = v.x();
140 vtx[offset + 1] = v.y();
141 }
142
143 void
set_attrib(unsigned int pos,const LibMatrix::vec3 & v,std::vector<float> * vertex)144 Mesh::set_attrib(unsigned int pos, const LibMatrix::vec3 &v, std::vector<float> *vertex)
145 {
146 if (!check_attrib(pos, 3))
147 return;
148
149 std::vector<float> &vtx = !vertex ? ensure_vertex() : *vertex;
150
151 int offset = vertex_format_[pos].second;
152
153 vtx[offset] = v.x();
154 vtx[offset + 1] = v.y();
155 vtx[offset + 2] = v.z();
156 }
157
158 void
set_attrib(unsigned int pos,const LibMatrix::vec4 & v,std::vector<float> * vertex)159 Mesh::set_attrib(unsigned int pos, const LibMatrix::vec4 &v, std::vector<float> *vertex)
160 {
161 if (!check_attrib(pos, 4))
162 return;
163
164 std::vector<float> &vtx = !vertex ? ensure_vertex() : *vertex;
165
166 int offset = vertex_format_[pos].second;
167
168 vtx[offset] = v.x();
169 vtx[offset + 1] = v.y();
170 vtx[offset + 2] = v.z();
171 vtx[offset + 3] = v.w();
172 }
173
174 /*
175 * Adds a new vertex to the list and makes it current.
176 */
177 void
next_vertex()178 Mesh::next_vertex()
179 {
180 vertices_.push_back(std::vector<float>(vertex_size_));
181 }
182
183 /**
184 * Gets the mesh vertices.
185 *
186 * You should use the ::set_attrib() method to manipulate
187 * the vertex data.
188 *
189 * You shouldn't resize the vector (change the number of vertices)
190 * manually. Use ::next_vertex() instead.
191 */
192 std::vector<std::vector<float> >&
vertices()193 Mesh::vertices()
194 {
195 return vertices_;
196 }
197
198 /**
199 * Sets the VBO update method.
200 *
201 * The default value is VBOUpdateMethodMap.
202 */
203 void
vbo_update_method(Mesh::VBOUpdateMethod method)204 Mesh::vbo_update_method(Mesh::VBOUpdateMethod method)
205 {
206 vbo_update_method_ = method;
207 }
208
209 /**
210 * Sets the VBO usage hint.
211 *
212 * The usage hint takes effect in the next call to ::build_vbo().
213 *
214 * The default value is VBOUsageStatic.
215 */
216 void
vbo_usage(Mesh::VBOUsage usage)217 Mesh::vbo_usage(Mesh::VBOUsage usage)
218 {
219 vbo_usage_ = usage;
220 }
221
222 /**
223 * Sets the vertex attribute interleaving mode.
224 *
225 * If true the vertex attributes are going to be interleaved in a single
226 * buffer. Otherwise they will be separated into different buffers (one
227 * per attribute).
228 *
229 * Interleaving mode takes effect in the next call to ::build_array() or
230 * ::build_vbo().
231 *
232 * @param interleave whether to interleave
233 */
234 void
interleave(bool interleave)235 Mesh::interleave(bool interleave)
236 {
237 interleave_ = interleave;
238 }
239
240 /**
241 * Resets a Mesh object to its initial, empty state.
242 */
243 void
reset()244 Mesh::reset()
245 {
246 delete_array();
247 delete_vbo();
248
249 vertices_.clear();
250 vertex_format_.clear();
251 attrib_locations_.clear();
252 attrib_data_ptr_.clear();
253 vertex_size_ = 0;
254 vertex_stride_ = 0;
255 }
256
257 /**
258 * Builds a vertex array containing the mesh vertex data.
259 *
260 * The way the vertex array is constructed is affected by the current
261 * interleave value, which can set using ::interleave().
262 */
263 void
build_array()264 Mesh::build_array()
265 {
266 int nvertices = vertices_.size();
267
268 if (!interleave_) {
269 /* Create an array for each attribute */
270 for (std::vector<std::pair<int, int> >::const_iterator ai = vertex_format_.begin();
271 ai != vertex_format_.end();
272 ai++)
273 {
274 float *array = new float[nvertices * ai->first];
275 float *cur = array;
276
277 /* Fill in the array */
278 for (std::vector<std::vector<float> >::const_iterator vi = vertices_.begin();
279 vi != vertices_.end();
280 vi++)
281 {
282 for (int i = 0; i < ai->first; i++)
283 *cur++ = (*vi)[ai->second + i];
284 }
285
286 vertex_arrays_.push_back(array);
287 attrib_data_ptr_.push_back(array);
288 }
289 vertex_stride_ = 0;
290 }
291 else {
292 float *array = new float[nvertices * vertex_size_];
293 float *cur = array;
294
295 for (std::vector<std::vector<float> >::const_iterator vi = vertices_.begin();
296 vi != vertices_.end();
297 vi++)
298 {
299 /* Fill in the array */
300 for (int i = 0; i < vertex_size_; i++)
301 *cur++ = (*vi)[i];
302 }
303
304 for (size_t i = 0; i < vertex_format_.size(); i++)
305 attrib_data_ptr_.push_back(array + vertex_format_[i].second);
306
307 vertex_arrays_.push_back(array);
308 vertex_stride_ = vertex_size_ * sizeof(float);
309 }
310 }
311
312 /**
313 * Builds a vertex buffer object containing the mesh vertex data.
314 *
315 * The way the VBO is constructed is affected by the current interleave
316 * value (::interleave()) and the vbo usage hint (::vbo_usage()).
317 */
318 void
build_vbo()319 Mesh::build_vbo()
320 {
321 delete_array();
322 build_array();
323
324 int nvertices = vertices_.size();
325
326 attrib_data_ptr_.clear();
327
328 GLenum buffer_usage;
329 if (vbo_usage_ == Mesh::VBOUsageStream)
330 buffer_usage = GL_STREAM_DRAW;
331 else if (vbo_usage_ == Mesh::VBOUsageDynamic)
332 buffer_usage = GL_DYNAMIC_DRAW;
333 else /* if (vbo_usage_ == Mesh::VBOUsageStatic) */
334 buffer_usage = GL_STATIC_DRAW;
335
336 if (!interleave_) {
337 /* Create a vbo for each attribute */
338 for (std::vector<std::pair<int, int> >::const_iterator ai = vertex_format_.begin();
339 ai != vertex_format_.end();
340 ai++)
341 {
342 float *data = vertex_arrays_[ai - vertex_format_.begin()];
343 GLuint vbo;
344
345 glGenBuffers(1, &vbo);
346 glBindBuffer(GL_ARRAY_BUFFER, vbo);
347 glBufferData(GL_ARRAY_BUFFER, nvertices * ai->first * sizeof(float),
348 data, buffer_usage);
349
350 vbos_.push_back(vbo);
351 attrib_data_ptr_.push_back(0);
352 }
353
354 vertex_stride_ = 0;
355 }
356 else {
357 GLuint vbo;
358 /* Create a single vbo to store all attribute data */
359 glGenBuffers(1, &vbo);
360 glBindBuffer(GL_ARRAY_BUFFER, vbo);
361
362 glBufferData(GL_ARRAY_BUFFER, nvertices * vertex_size_ * sizeof(float),
363 vertex_arrays_[0], GL_STATIC_DRAW);
364
365 glBindBuffer(GL_ARRAY_BUFFER, 0);
366
367 for (size_t i = 0; i < vertex_format_.size(); i++) {
368 attrib_data_ptr_.push_back(reinterpret_cast<float *>(sizeof(float) * vertex_format_[i].second));
369 vbos_.push_back(vbo);
370 }
371 vertex_stride_ = vertex_size_ * sizeof(float);
372 }
373
374 delete_array();
375 }
376
377 /**
378 * Updates ranges of a single vertex array.
379 *
380 * @param ranges the ranges of vertices to update
381 * @param n the index of the vertex array to update
382 * @param nfloats how many floats to update for each vertex
383 * @param offset the offset (in floats) in the vertex data to start reading from
384 */
385 void
update_single_array(const std::vector<std::pair<size_t,size_t>> & ranges,size_t n,size_t nfloats,size_t offset)386 Mesh::update_single_array(const std::vector<std::pair<size_t, size_t> >& ranges,
387 size_t n, size_t nfloats, size_t offset)
388 {
389 float *array(vertex_arrays_[n]);
390
391 /* Update supplied ranges */
392 for (std::vector<std::pair<size_t, size_t> >::const_iterator ri = ranges.begin();
393 ri != ranges.end();
394 ri++)
395 {
396 /* Update the current range from the vertex data */
397 float *dest(array + nfloats * ri->first);
398 for (size_t n = ri->first; n <= ri->second; n++) {
399 float *src(vertices_[n].data() + offset);
400 std::copy(src, src + nfloats, dest);
401 dest += nfloats;
402 }
403
404 }
405 }
406
407 /**
408 * Updates ranges of the vertex arrays.
409 *
410 * @param ranges the ranges of vertices to update
411 */
412 void
update_array(const std::vector<std::pair<size_t,size_t>> & ranges)413 Mesh::update_array(const std::vector<std::pair<size_t, size_t> >& ranges)
414 {
415 /* If we don't have arrays to update, create them */
416 if (vertex_arrays_.empty()) {
417 build_array();
418 return;
419 }
420
421 if (!interleave_) {
422 for (size_t i = 0; i < vertex_arrays_.size(); i++) {
423 update_single_array(ranges, i, vertex_format_[i].first,
424 vertex_format_[i].second);
425 }
426 }
427 else {
428 update_single_array(ranges, 0, vertex_size_, 0);
429 }
430
431 }
432
433
434 /**
435 * Updates ranges of a single VBO.
436 *
437 * This method use either glMapBuffer or glBufferSubData to perform
438 * the update. The used method can be set with ::vbo_update_method().
439 *
440 * @param ranges the ranges of vertices to update
441 * @param n the index of the vbo to update
442 * @param nfloats how many floats to update for each vertex
443 */
444 void
update_single_vbo(const std::vector<std::pair<size_t,size_t>> & ranges,size_t n,size_t nfloats)445 Mesh::update_single_vbo(const std::vector<std::pair<size_t, size_t> >& ranges,
446 size_t n, size_t nfloats)
447 {
448 float *src_start(vertex_arrays_[n]);
449 float *dest_start(0);
450
451 glBindBuffer(GL_ARRAY_BUFFER, vbos_[n]);
452
453 if (vbo_update_method_ == VBOUpdateMethodMap) {
454 dest_start = reinterpret_cast<float *>(
455 GLExtensions::MapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)
456 );
457 }
458
459 /* Update supplied ranges */
460 for (std::vector<std::pair<size_t, size_t> >::const_iterator iter = ranges.begin();
461 iter != ranges.end();
462 iter++)
463 {
464 float *src(src_start + nfloats * iter->first);
465 float *src_end(src_start + nfloats * (iter->second + 1));
466
467 if (vbo_update_method_ == VBOUpdateMethodMap) {
468 float *dest(dest_start + nfloats * iter->first);
469 std::copy(src, src_end, dest);
470 }
471 else if (vbo_update_method_ == VBOUpdateMethodSubData) {
472 glBufferSubData(GL_ARRAY_BUFFER, nfloats * iter->first * sizeof(float),
473 (src_end - src) * sizeof(float), src);
474 }
475 }
476
477 if (vbo_update_method_ == VBOUpdateMethodMap)
478 GLExtensions::UnmapBuffer(GL_ARRAY_BUFFER);
479 }
480
481 /**
482 * Updates ranges of the VBOs.
483 *
484 * @param ranges the ranges of vertices to update
485 */
486 void
update_vbo(const std::vector<std::pair<size_t,size_t>> & ranges)487 Mesh::update_vbo(const std::vector<std::pair<size_t, size_t> >& ranges)
488 {
489 /* If we don't have VBOs to update, create them */
490 if (vbos_.empty()) {
491 build_vbo();
492 return;
493 }
494
495 update_array(ranges);
496
497 if (!interleave_) {
498 for (size_t i = 0; i < vbos_.size(); i++)
499 update_single_vbo(ranges, i, vertex_format_[i].first);
500 }
501 else {
502 update_single_vbo(ranges, 0, vertex_size_);
503 }
504
505 glBindBuffer(GL_ARRAY_BUFFER, 0);
506 }
507
508
509 /**
510 * Deletes all resources associated with built vertex arrays.
511 */
512 void
delete_array()513 Mesh::delete_array()
514 {
515 for (size_t i = 0; i < vertex_arrays_.size(); i++) {
516 delete [] vertex_arrays_[i];
517 }
518
519 vertex_arrays_.clear();
520 }
521
522 /**
523 * Deletes all resources associated with built VBOs.
524 */
525 void
delete_vbo()526 Mesh::delete_vbo()
527 {
528 for (size_t i = 0; i < vbos_.size(); i++) {
529 GLuint vbo = vbos_[i];
530 glDeleteBuffers(1, &vbo);
531 }
532
533 vbos_.clear();
534 }
535
536
537 /**
538 * Renders a mesh using vertex arrays.
539 *
540 * The vertex arrays must have been previously initialized using
541 * ::build_array().
542 */
543 void
render_array()544 Mesh::render_array()
545 {
546 for (size_t i = 0; i < vertex_format_.size(); i++) {
547 if (attrib_locations_[i] < 0)
548 continue;
549 glEnableVertexAttribArray(attrib_locations_[i]);
550 glVertexAttribPointer(attrib_locations_[i], vertex_format_[i].first,
551 GL_FLOAT, GL_FALSE, vertex_stride_,
552 attrib_data_ptr_[i]);
553 }
554
555 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
556
557 for (size_t i = 0; i < vertex_format_.size(); i++) {
558 if (attrib_locations_[i] < 0)
559 continue;
560 glDisableVertexAttribArray(attrib_locations_[i]);
561 }
562 }
563
564 /**
565 * Renders a mesh using vertex buffer objects.
566 *
567 * The vertex buffer objects must have been previously initialized using
568 * ::build_vbo().
569 */
570 void
render_vbo()571 Mesh::render_vbo()
572 {
573 for (size_t i = 0; i < vertex_format_.size(); i++) {
574 if (attrib_locations_[i] < 0)
575 continue;
576 glEnableVertexAttribArray(attrib_locations_[i]);
577 glBindBuffer(GL_ARRAY_BUFFER, vbos_[i]);
578 glVertexAttribPointer(attrib_locations_[i], vertex_format_[i].first,
579 GL_FLOAT, GL_FALSE, vertex_stride_,
580 attrib_data_ptr_[i]);
581 }
582
583 glDrawArrays(GL_TRIANGLES, 0, vertices_.size());
584
585 for (size_t i = 0; i < vertex_format_.size(); i++) {
586 if (attrib_locations_[i] < 0)
587 continue;
588 glDisableVertexAttribArray(attrib_locations_[i]);
589 }
590 }
591
592 /**
593 * Creates a grid mesh.
594 *
595 * @param n_x the number of grid cells on the X axis
596 * @param n_y the number of grid cells on the Y axis
597 * @param width the width X of the grid (normalized)
598 * @param height the height Y of the grid (normalized)
599 * @param spacing the spacing between cells (normalized)
600 * @param conf_func a function to call to configure the grid (or NULL)
601 */
602 void
make_grid(int n_x,int n_y,double width,double height,double spacing,grid_configuration_func conf_func)603 Mesh::make_grid(int n_x, int n_y, double width, double height,
604 double spacing, grid_configuration_func conf_func)
605 {
606 double side_width = (width - (n_x - 1) * spacing) / n_x;
607 double side_height = (height - (n_y - 1) * spacing) / n_y;
608
609 for (int i = 0; i < n_x; i++) {
610 for (int j = 0; j < n_y; j++) {
611 LibMatrix::vec3 a(-width / 2 + i * (side_width + spacing),
612 height / 2 - j * (side_height + spacing), 0);
613 LibMatrix::vec3 b(a.x(), a.y() - side_height, 0);
614 LibMatrix::vec3 c(a.x() + side_width, a.y(), 0);
615 LibMatrix::vec3 d(a.x() + side_width, a.y() - side_height, 0);
616
617 if (!conf_func) {
618 std::vector<float> ul(vertex_size_);
619 std::vector<float> ur(vertex_size_);
620 std::vector<float> ll(vertex_size_);
621 std::vector<float> lr(vertex_size_);
622
623 set_attrib(0, a, &ul);
624 set_attrib(0, c, &ur);
625 set_attrib(0, b, &ll);
626 set_attrib(0, d, &lr);
627
628 next_vertex(); vertices_.back() = ul;
629 next_vertex(); vertices_.back() = ll;
630 next_vertex(); vertices_.back() = ur;
631 next_vertex(); vertices_.back() = ll;
632 next_vertex(); vertices_.back() = lr;
633 next_vertex(); vertices_.back() = ur;
634 }
635 else {
636 conf_func(*this, i, j, n_x, n_y, a, b, c, d);
637 }
638 }
639 }
640 }
641