(see update at the end for resolution to my specific case... The problem here is given more concisely and the solution covered more thoroughly in this question: Cannot cast array to pointer)
I successfully got the first example running (the blue triangles), then refactored it from C++ to C99, also successfully. I started a new project based on this C version that generates the points algorithmically, but is otherwise functionally identical to the still-working example, and the rendered image is not consistent between different executions.
The program should draw a blue parabola in the top half of the screen, but varies between an empty black box and a horizontal line from the center to either the left or right midpoint of the window.
I started off doing things a little differently, more "correct," like not using the globals or what looks to me like an abuse of enums (NumVAOs, NumBuffers), but as things didn't turn out as I expected I changed more and more to match the example code. Everything compiles fine with -std=c99 -Wall -pedantic
, and the results are the same whether glew and freeglut are linked statically or dynamically.
I really can't see anything wrong with it. I verified that the parabola function returns the correct results, and the data is formatted in the same way as it is in the book. Because of the inconsistent results I suspected it may have something to do with malloc not clearing the vertex array's memory, but I get the same results using calloc. It seems like the screen coordinates aren't behaving properly, and I can't imagine why.
Full source is located at https://github.com/markmccormack/concentrator
I'm fairly sure the problem is with my parabola code, since that is the only real difference, so here are the relevant parts:
/* parabola.h
* generate parabolas in the form y = ax^2
* return a compact set of 2D points describing it
*/
#ifndef __PARABOLA_H__
#define __PARABOLA_H__
#include <GL/glew.h>
GLfloat**
v_parabola_by_a ( GLfloat a, GLuint num_points ) ;
#endif /* __PARABOLA_H__ */
and the implementation:
#include <stdio.h>
#include <stdlib.h>
#include "parabola.h"
GLfloat**
v_parabola_by_a ( GLfloat a, GLuint num_points ) {
/* Array of X and Y values */
GLfloat **points ;
points = calloc( num_points, sizeof(GLfloat[2]) ) ;
if( points == NULL ) {
fprintf( stderr, "Could not allocate memory, exiting.\n" ) ;
exit(EXIT_FAILURE) ;
} else {
int iter_alloc ;
for( iter_alloc = 0; iter_alloc <= num_points; iter_alloc++ ) {
points[iter_alloc] = calloc( 2, sizeof(GLfloat) ) ;
}
}
/* Each point is incremented along x by 'inc_x,' for a -1,+1 range */
GLfloat inc_x = 2.0 / (float)num_points ;
int iter_point ;
for (iter_point = 0; iter_point <= num_points; iter_point++ ) {
GLfloat x_value = (GLfloat)iter_point * inc_x - 1.0 ;
GLfloat y_value = x_value * x_value * a ; /* y = a x^2 */
points[iter_point][0] = x_value ;
points[iter_point][1] = y_value ;
}
return points ;
}
The GLfloat** array gets passed to OpenGL like this:
GLfloat **points ;
points = calloc( num_points, sizeof(GLfloat[2]) ) ;
if( points == NULL ) {
fprintf( stdout, "Could not allocate memory.\n" ) ;
exit( EXIT_FAILURE ) ;
} else {
int iter_alloc ;
for( iter_alloc = 0; iter_alloc <= num_points; iter_alloc++ ) {
points[iter_alloc] = calloc( 2, sizeof(GLfloat) ) ;
}
}
points = v_parabola_by_a( 1.0, num_points ) ;
/* Create & bind renderable asset */
glGenVertexArrays( NumVAOs, VAOs ) ;
glBindVertexArray( VAOs[Parabola] ) ;
/* Create & bind vertex array */
glGenBuffers( NumBuffers, Buffers ) ;
glBindBuffer( GL_ARRAY_BUFFER, Buffers[ArrayBuffer] ) ;
/* Populate bound vertex array with the asset */
glBufferData( GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW ) ;
Any help or suggestions are much appreciated!
The core problem I had was my lack of understanding how pointers relate to arrays, erroneously assuming the two could be used interchangeably in this instance. Specifically, using an array of pointers like an n-D array is incorrect, and while data can be stored this way it must be unwrapped into a flat array before passing it as a chunk of memory. These are the changes which fixed the program:
- The vertex arrays changed from
GLfloat**
toGLfloat*
- The size argument to glBufferData was changed from
sizeof(points)
tonum_points*2*sizeof(GLfloat)
- The function generating the vertex array was changed from
GLfloat** v_parabola_by_a( float a, int num_points ) ;
tovoid v_parabola_by_a( float a, int num_points, GLfloat* output) ;
withmemcpy(output, points, num_points*2*sizeof(GLfloat))
in place ofreturn(points)
, which corrected the multiple assignment to a pointer.
This is not strictly what was suggested but I believe it is a workable solution.