-1

(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** to GLfloat*
  • The size argument to glBufferData was changed from sizeof(points) to num_points*2*sizeof(GLfloat)
  • The function generating the vertex array was changed from GLfloat** v_parabola_by_a( float a, int num_points ) ; to void v_parabola_by_a( float a, int num_points, GLfloat* output) ; with memcpy(output, points, num_points*2*sizeof(GLfloat)) in place of return(points), which corrected the multiple assignment to a pointer.

This is not strictly what was suggested but I believe it is a workable solution.

Community
  • 1
  • 1
Mark
  • 21
  • 4

1 Answers1

1
glBufferData( GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW ) ;

This is wrong. glBufferData expects flat array, not array of pointers to float arrays. Also, sizeof(points) is size of just one pointer, not size of array.

Allocating array of arrays of 2 floats makes no sense. Create one big array that can hold all data, and fill it [0] = first_x, [1] = first_y, [2] = second_x, ...

keltar
  • 17,711
  • 2
  • 37
  • 42
  • I actually tried it that way first, the example I'm following gives an array "vertices[6][2]" in this fashion for two triangles and it works. I'll make the suggested changes and see how it goes. Thanks for the quick reply – Mark Apr 01 '14 at 11:46
  • That did it, the variable length array gives sizeof() for one element, so multiplying sizeof(GLfloat) by the number of elements in the call to glBufferData gives it the full set. Thanks a lot, I knew it had to be something really simple and I'm just being daft. The glBufferData call doesn't see the difference between [32] and [16][2], but I really prefer the flat array in any case. – Mark Apr 01 '14 at 12:38
  • 1
    No. There is a significant difference between `float**` memory layout and `float [N][M]`. In later case, array would actually be unrolled into one-dimentional, so you can use it as ordinary array. In first case, it would be array of pointers to arrays - which is by no mean one-dimentional, and cannot be used with `glBufferData`. What you've used is array of pointers, so what I've pointed to is actually an error. – keltar Apr 01 '14 at 12:46
  • Interesting, after some further reading on pointers and arrays I see what you mean, it's apples to oranges, or maybe directions through the produce aisle is more apt. This definitely improves my understanding of pointers, and shows me I've been going about arrays all wrong in C. Thanks for your help correcting that – Mark Apr 01 '14 at 13:44