OpenGL ES SDK for Android ARM Developer Center
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
GroundMeshSetup.cpp
Go to the documentation of this file.
1 /* Copyright (c) 2014-2017, ARM Limited and Contributors
2  *
3  * SPDX-License-Identifier: MIT
4  *
5  * Permission is hereby granted, free of charge,
6  * to any person obtaining a copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation the rights to
8  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
9  * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
14  * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
17  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19  */
20 
21 #include "GroundMesh.h"
22 #include "Platform.h"
23 #include <assert.h>
24 
25 using namespace MaliSDK;
26 
28 {
29  // Assume that size <= 64. Saves some vertex space since we can use 8-bit vertex coordinates.
30  assert(size <= 64);
31 
32  // The ground consists of many smaller tesselated quads.
33  // These smaller quads can be instanced to stamp out a big area (clipmap) where quads further away from camera
34  // can be larger, and hence, less detail.
35  // The grid is completely flat (XZ-plane), but they are offset in Y direction with a heightmap in vertex shader.
36  // We also need padding/fixup regions to fill the missing space which
37  // shows up when the clipmap is put together.
38 
39  // See Doxygen for an illustration on how these blocks are laid out to form the terrain.
40 
41  unsigned int num_vertices = size * size; // Regular block
42 
43  // Ring fixups (vertical and horiz). The regions are 3-by-N vertices.
44  unsigned int ring_vertices = size * 3;
45  num_vertices += 2 * ring_vertices;
46 
47  // Trim regions are thin stripes which surround blocks from the lower LOD level.
48  // Need (2 * size + 1)-by-2 vertices. One stripe for each four sides are needed.
49  unsigned int trim_vertices = (2 * size + 1) * 2;
50  num_vertices += trim_vertices * 4;
51 
52  // Degenerate triangles. These are run on the edge between clipmap levels.
53  // These are needed to avoid occasional "missing pixels" between clipmap levels as
54  // imperfections in precision can cause the terrain to not perfectly overlap at the clipmap level boundary.
55  //
56  // 5 vertices are used per vertex to create a suitable triangle strip.
57  // (This is somewhat redundant, but it simplifies the implementation).
58  // Two different strips are needed for left/right and top/bottom.
59  unsigned int degenerate_vertices = 2 * (size - 1) * 5;
60  num_vertices += degenerate_vertices * 2;
61 
62  GLubyte *vertices = new GLubyte[2 * num_vertices];
64  GLubyte *pv = vertices;
65 
66  // Block
67  for (unsigned int z = 0; z < size; z++)
68  {
69  for (unsigned int x = 0; x < size; x++)
70  {
71  pv[0] = x;
72  pv[1] = z;
73  pv += 2;
74  }
75  }
77 
78  // Vertical ring fixup
79  for (unsigned int z = 0; z < size; z++)
80  {
81  for (unsigned int x = 0; x < 3; x++)
82  {
83  pv[0] = x;
84  pv[1] = z;
85  pv += 2;
86  }
87  }
88 
89  // Horizontal ring fixup
90  for (unsigned int z = 0; z < 3; z++)
91  {
92  for (unsigned int x = 0; x < size; x++)
93  {
94  pv[0] = x;
95  pv[1] = z;
96  pv += 2;
97  }
98  }
99 
100  // Full interior trim
101  // Top
102  for (unsigned int z = 0; z < 2; z++)
103  {
104  for (unsigned int x = 0; x < 2 * size + 1; x++)
105  {
106  pv[0] = x;
107  pv[1] = z;
108  pv += 2;
109  }
110  }
111 
112  // Right
113  for (int x = 1; x >= 0; x--)
114  {
115  for (unsigned int z = 0; z < 2 * size + 1; z++)
116  {
117  pv[0] = x + 2 * size - 1;
118  pv[1] = z;
119  pv += 2;
120  }
121  }
122 
123  // Bottom
124  for (int z = 1; z >= 0; z--)
125  {
126  for (unsigned int x = 0; x < 2 * size + 1; x++)
127  {
128  pv[0] = 2 * size - x;
129  pv[1] = z + 2 * size - 1;
130  pv += 2;
131  }
132  }
133 
134  // Left
135  for (unsigned int x = 0; x < 2; x++)
136  {
137  for (unsigned int z = 0; z < 2 * size + 1; z++)
138  {
139  pv[0] = x;
140  pv[1] = 2 * size - z;
141  pv += 2;
142  }
143  }
144 
145  // Degenerate triangles.
146  // Left, right
147  for (unsigned int y = 0; y < (size - 1) * 2; y++)
148  {
149  pv[0] = 0;
150  pv[1] = y * 2;
151  pv[2] = 0;
152  pv[3] = y * 2;
153  pv[4] = 0;
154  pv[5] = y * 2 + 1;
155  pv[6] = 0;
156  pv[7] = y * 2 + 2;
157  pv[8] = 0;
158  pv[9] = y * 2 + 2;
159  pv += 10;
160  }
161 
162  // Top, bottom
163  for (unsigned int x = 0; x < (size - 1) * 2; x++)
164  {
165  pv[0] = x * 2;
166  pv[1] = 0;
167  pv[2] = x * 2;
168  pv[3] = 0;
169  pv[4] = x * 2 + 1;
170  pv[5] = 0;
171  pv[6] = x * 2 + 2;
172  pv[7] = 0;
173  pv[8] = x * 2 + 2;
174  pv[9] = 0;
175  pv += 10;
176  }
177 
178  // Right and bottom share vertices with left and top respectively.
179 
180  GL_CHECK(glGenBuffers(1, &vertex_buffer));
181  GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer));
182  GL_CHECK(glBufferData(GL_ARRAY_BUFFER, 2 * num_vertices * sizeof(GLubyte), vertices, GL_STATIC_DRAW));
183  GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, 0));
184 
185  delete[] vertices;
186 }
187 
188 // Returns number of indices needed to create a triangle stripped mesh using generate_block_indices() below.
189 static unsigned int block_index_count(unsigned int width, unsigned int height)
190 {
191  unsigned int strips = height - 1;
192  return strips * (2 * width - 1) + 1;
193 }
194 
196 static GLushort *generate_block_indices(GLushort *pi, unsigned int vertex_buffer_offset,
197  unsigned int width, unsigned int height)
198 {
199  // Stamp out triangle strips back and forth.
200  int pos = vertex_buffer_offset;
201  unsigned int strips = height - 1;
202 
203  // After even indices in a strip, always step to next strip.
204  // After odd indices in a strip, step back again and one to the right or left.
205  // Which direction we take depends on which strip we're generating.
206  // This creates a zig-zag pattern.
207  for (unsigned int z = 0; z < strips; z++)
208  {
209  int step_even = width;
210  int step_odd = ((z & 1) ? -1 : 1) - step_even;
211 
212  // We don't need the last odd index.
213  // The first index of the next strip will complete this strip.
214  for (unsigned int x = 0; x < 2 * width - 1; x++)
215  {
216  *pi++ = pos;
217  pos += (x & 1) ? step_odd : step_even;
218  }
219  }
220  // There is no new strip, so complete the block here.
221  *pi++ = pos;
222 
223  // Return updated index buffer pointer.
224  // More explicit than taking reference to pointer.
225  return pi;
226 }
228 
230 {
231  // Used for frustum culling.
232  // The range is the number of vertices covered minus 1.
233  block.range = vec2(size - 1);
234 
235  vertical.range = vec2(2, size - 1);
236  horizontal.range = vec2(size - 1, 2);
237 
238  trim_full.range = vec2(2 * size);
239  trim_top_left.range = vec2(2 * size);
240  trim_bottom_right.range = vec2(2 * size);
241  trim_top_right.range = vec2(2 * size);
242  trim_bottom_left.range = vec2(2 * size);
243 
244  degenerate_left.range = vec2(0, 4 * size - 2);
245  degenerate_right.range = vec2(0, 4 * size - 2);
246  degenerate_top.range = vec2(4 * size - 2, 0);
247  degenerate_bottom.range = vec2(4 * size - 2, 0);
248 }
249 
251 {
252  unsigned int vertex_buffer_offset = 0;
253 
254  block.count = block_index_count(size, size);
255 
256  vertical.count = block_index_count(3, size);
257  horizontal.count = block_index_count(size, 3);
258 
259  unsigned int trim_region_indices = block_index_count(2 * size + 1, 2);
260  trim_full.count = 4 * trim_region_indices;
261  trim_top_left.count = 2 * trim_region_indices;
262  trim_bottom_right = trim_bottom_left = trim_top_right = trim_top_left;
263 
264  // 6 indices are used here per vertex.
265  // Need to repeat one vertex to get correct winding when
266  // connecting the triangle strips.
267  degenerate_left.count = (size - 1) * 2 * 6;
268  degenerate_right = degenerate_bottom = degenerate_top = degenerate_left;
269 
270  num_indices = block.count + vertical.count + horizontal.count + trim_full.count +
271  4 * trim_top_left.count +
272  4 * degenerate_left.count;
273 
274  GLushort *indices = new GLushort[num_indices];
275  GLushort *pi = indices;
276 
277  // Main block
278  block.offset = pi - indices;
279  pi = generate_block_indices(pi, vertex_buffer_offset, size, size);
280  vertex_buffer_offset += size * size;
281 
282  // Vertical fixup
283  vertical.offset = pi - indices;
284  pi = generate_block_indices(pi, vertex_buffer_offset, 3, size);
285  vertex_buffer_offset += 3 * size;
286 
287  // Horizontal fixup
288  horizontal.offset = pi - indices;
289  pi = generate_block_indices(pi, vertex_buffer_offset, size, 3);
290  vertex_buffer_offset += 3 * size;
291 
292  // Full interior trim
293  // All trims can be run after each other.
294  // The vertex buffer is generated such that this creates a "ring".
295  // The full trim is only used to connect clipmap level 0 to level 1. See Doxygen for more detail.
296  trim_full.offset = pi - indices;
297  unsigned int full_trim_offset = vertex_buffer_offset;
298  unsigned int trim_vertices = (2 * size + 1) * 2;
299  pi = generate_block_indices(pi, full_trim_offset, 2 * size + 1, 2); // Top
300  full_trim_offset += trim_vertices;
301  pi = generate_block_indices(pi, full_trim_offset, 2 * size + 1, 2); // Right
302  full_trim_offset += trim_vertices;
303  pi = generate_block_indices(pi, full_trim_offset, 2 * size + 1, 2); // Bottom
304  full_trim_offset += trim_vertices;
305  pi = generate_block_indices(pi, full_trim_offset, 2 * size + 1, 2); // Left
306  full_trim_offset += trim_vertices;
307 
308  // Top-right interior trim
309  // This is a half ring (L-shaped).
310  trim_top_right.offset = pi - indices;
311  pi = generate_block_indices(pi, vertex_buffer_offset, 2 * size + 1, 2); // Top
312  pi = generate_block_indices(pi, vertex_buffer_offset + (2 * size + 1) * 2, 2 * size + 1, 2); // Right
313  vertex_buffer_offset += trim_vertices;
314 
315  // Right-bottom interior trim
316  // This is a half ring (L-shaped).
317  trim_bottom_right.offset = pi - indices;
318  pi = generate_block_indices(pi, vertex_buffer_offset, 2 * size + 1, 2); // Right
319  pi = generate_block_indices(pi, vertex_buffer_offset + (2 * size + 1) * 2, 2 * size + 1, 2); // Bottom
320  vertex_buffer_offset += trim_vertices;
321 
322  // Bottom-left interior trim
323  // This is a half ring (L-shaped).
324  trim_bottom_left.offset = pi - indices;
325  pi = generate_block_indices(pi, vertex_buffer_offset, 2 * size + 1, 2); // Bottom
326  pi = generate_block_indices(pi, vertex_buffer_offset + (2 * size + 1) * 2, 2 * size + 1, 2); // Left
327  vertex_buffer_offset += trim_vertices;
328 
329  // Left-top interior trim
330  // This is a half ring (L-shaped).
331  trim_top_left.offset = pi - indices;
332  pi = generate_block_indices(pi, vertex_buffer_offset, 2 * size + 1, 2); // Left
333  pi = generate_block_indices(pi, vertex_buffer_offset - 6 * (2 * size + 1), 2 * size + 1, 2); // Top
334  vertex_buffer_offset += trim_vertices;
335 
336  // One of the trim regions will be used to connect level N with level N + 1.
337 
338  // Degenerates. Left and right share vertices (with different offsets in vertex shader). Top and bottom share.
339  // Left
340  degenerate_left.offset = pi - indices;
341  for (unsigned int z = 0; z < (size - 1) * 2; z++)
342  {
343  pi[0] = (5 * z) + 0 + vertex_buffer_offset;
344  pi[1] = (5 * z) + 1 + vertex_buffer_offset;
345  pi[2] = (5 * z) + 2 + vertex_buffer_offset;
346  pi[3] = (5 * z) + 3 + vertex_buffer_offset;
347  pi[4] = (5 * z) + 4 + vertex_buffer_offset;
348  pi[5] = (5 * z) + 4 + vertex_buffer_offset;
349  pi += 6;
350  }
351 
352  // Right
353  degenerate_right.offset = pi - indices;
354  unsigned int start_z = (size - 1) * 2 - 1;
355  for (unsigned int z = 0; z < (size - 1) * 2; z++)
356  {
357  // Windings are in reverse order on this side.
358  pi[0] = (5 * (start_z - z)) + 4 + vertex_buffer_offset;
359  pi[1] = (5 * (start_z - z)) + 3 + vertex_buffer_offset;
360  pi[2] = (5 * (start_z - z)) + 2 + vertex_buffer_offset;
361  pi[3] = (5 * (start_z - z)) + 1 + vertex_buffer_offset;
362  pi[4] = (5 * (start_z - z)) + 0 + vertex_buffer_offset;
363  pi[5] = (5 * (start_z - z)) + 0 + vertex_buffer_offset;
364  pi += 6;
365  }
366 
367  vertex_buffer_offset += (size - 1) * 2 * 5;
368 
369  // Top
370  degenerate_top.offset = pi - indices;
371  for (unsigned int x = 0; x < (size - 1) * 2; x++)
372  {
373  pi[0] = (5 * x) + 0 + vertex_buffer_offset;
374  pi[1] = (5 * x) + 1 + vertex_buffer_offset;
375  pi[2] = (5 * x) + 2 + vertex_buffer_offset;
376  pi[3] = (5 * x) + 3 + vertex_buffer_offset;
377  pi[4] = (5 * x) + 4 + vertex_buffer_offset;
378  pi[5] = (5 * x) + 4 + vertex_buffer_offset;
379  pi += 6;
380  }
381 
382  // Bottom
383  degenerate_bottom.offset = pi - indices;
384  unsigned int start_x = (size - 1) * 2 - 1;
385  for (unsigned int x = 0; x < (size - 1) * 2; x++)
386  {
387  // Windings are in reverse order on this side.
388  pi[0] = (5 * (start_x - x)) + 4 + vertex_buffer_offset;
389  pi[1] = (5 * (start_x - x)) + 3 + vertex_buffer_offset;
390  pi[2] = (5 * (start_x - x)) + 2 + vertex_buffer_offset;
391  pi[3] = (5 * (start_x - x)) + 1 + vertex_buffer_offset;
392  pi[4] = (5 * (start_x - x)) + 0 + vertex_buffer_offset;
393  pi[5] = (5 * (start_x - x)) + 0 + vertex_buffer_offset;
394  pi += 6;
395  }
396 
397  GL_CHECK(glGenBuffers(1, &index_buffer));
398  GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer));
399  GL_CHECK(glBufferData(GL_ELEMENT_ARRAY_BUFFER, num_indices * sizeof(GLushort), indices, GL_STATIC_DRAW));
400  GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
401 
402  delete[] indices;
403 }
404 
406 {
407  GL_CHECK(glGenBuffers(1, &uniform_buffer));
408  GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, uniform_buffer));
409 
410  // Per level we can draw up to 12 regular blocks, 4 vert/horiz rings, one trim, and four degenerate strips.
411  // Double the UBO size just in case we have very high levels for UBO buffer alignment.
412  uniform_buffer_size = 2 * (12 + 4 + 1 + 4) * levels * sizeof(InstanceData);
413  GL_CHECK(glBufferData(GL_UNIFORM_BUFFER, uniform_buffer_size, NULL, GL_STREAM_DRAW));
414 
415  GL_CHECK(glBindBuffer(GL_UNIFORM_BUFFER, 0));
416 }
417 
418 // Already defined in the shader.
419 #define LOCATION_VERTEX 0
420 
422 {
423  GL_CHECK(glGenVertexArrays(1, &vertex_array));
424  GL_CHECK(glBindVertexArray(vertex_array));
425  GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer));
426  GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_buffer));
427 
428  GL_CHECK(glVertexAttribPointer(LOCATION_VERTEX, 2, GL_UNSIGNED_BYTE, GL_FALSE, 0, 0));
429  GL_CHECK(glEnableVertexAttribArray(LOCATION_VERTEX));
430 
431  GL_CHECK(glBindVertexArray(0));
432  GL_CHECK(glBindBuffer(GL_ARRAY_BUFFER, 0));
433  // Element array buffer state is part of the vertex array object, have to unbind it after the vertex array.
434  GL_CHECK(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0));
435 }
void setup_vertex_buffer(unsigned int size)
void setup_block_ranges(unsigned int size)
[Generating index buffer]
GLint GLsizei GLsizei height
Definition: gl2ext.h:179
Definition: matrix.h:28
const float vertices[]
Definition: Cube.h:30
void setup_uniform_buffer()
#define LOCATION_VERTEX
GLsizei GLenum const void * indices
Definition: gl2ext.h:322
void setup_index_buffer(unsigned int size)
#define GL_CHECK(x)
Definition: AstcTextures.h:59
GLsizei levels
Definition: gl2ext.h:1816
void setup_vertex_array()
GLenum GLuint GLintptr GLsizeiptr size
Definition: gl2ext.h:629
GLint GLint GLint GLint GLint x
Definition: gl2ext.h:574
static unsigned int block_index_count(unsigned int width, unsigned int height)
GLint GLsizei width
Definition: gl2ext.h:179
GLint y
Definition: gl2ext.h:179
static GLushort * generate_block_indices(GLushort *pi, unsigned int vertex_buffer_offset, unsigned int width, unsigned int height)
[Generating index buffer]