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