Drawing multiple squares, GL_INVALID_OPERATION from glCompileShader() randomly occurs

First, I need to preface that my project is built off a pre-existing project from a class assignment. The professor provided a GLSLShader.h that handles the fragment and vertex shader compilation. I don’t really have a reason to believe his code is erroneous because the project runs fine without my modifications. The original project draws multiple glutSolidTeapots, I need to draw squares. I wrote my own Square.h class with its own draw function. For now I’m just trying to get all the squares to show. I’m having an issue where not all my squares are drawn all the time. If I try to create 4, sometimes all 4 will draw, but usually only 2 or 3, and in those cases I will get the GL_INVALID_OPERATION message from the GLSLShader class.

Square.h

#include <iostream>

#include <glm/glm.hpp>
#include <glm/ext.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_inverse.hpp>

#include "Material.h"
#include <vector>
#include <algorithm>

class Square{

public:
    glm::vec3 position;
    glm::vec3 up;
    glm::vec3 forward;
    float scale;  //scale 1 = unit square
    Material *material;
    bool visible;
    glm::vec3 velocity;

    // initialized with face facing +z direction, top edge normal pointing to at +y direction
    Square(glm::vec3 pos, float s, Material* m): position(pos), scale(s){
      material = m;
      visible = true;
      up = glm::vec3(0.0, 1.0, 0.0);
      forward = glm::vec3(0.0, 0.0, 1.0);
      init();
    }

    Square():position(glm::vec3(0, 0, 0)), scale(1.0) {
      material = new Material(glm::vec4(0.2, 0.2, 0.2, 1.0), glm::vec4(0.5, 0.5, 0.5, 1.0), glm::vec4(1.0, 1.0, 1.0, 1.0), 100.0);
      visible = true;
      up = glm::vec3(0.0, 0.0, 1.0);
      forward = glm::vec3(0.0, 1.0, 0.0);
      init();
    }

    ~Square() {
        delete material;
    }

    void draw() {
      glBindVertexArray(vao);
      glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
      //printf("xyz: %.2f, %.2f, %.2f
", position.x, position.y, position.z);
    }

private:

    unsigned int vao;
    unsigned int vbo;
    unsigned int ebo;

    void init() {
      glGenVertexArrays(1, &vao);
      glGenBuffers(1, &vbo);
      glBindVertexArray(vao);
      glBindBuffer(GL_ARRAY_BUFFER, vbo);
      glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
      glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
      glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indexData), indexData, GL_STATIC_DRAW);

      // vertex position
      glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);
      glEnableVertexAttribArray(0);
      // vertex color
      glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3* sizeof(float)));
      glEnableVertexAttribArray(1);
      // texture coord attribute
      glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
      glEnableVertexAttribArray(2);

    }

    float vertexData[32] = {
    // positions          // colors           // texture coords
       0.5f,  0.5f, 0.0f,   1.0f, 0.0f, 0.0f,   1.0f, 1.0f,   // top right
       0.5f, -0.5f, 0.0f,   0.0f, 1.0f, 0.0f,   1.0f, 0.0f,   // bottom right
      -0.5f, -0.5f, 0.0f,   0.0f, 0.0f, 1.0f,   0.0f, 0.0f,   // bottom left
      -0.5f,  0.5f, 0.0f,   1.0f, 1.0f, 0.0f,   0.0f, 1.0f    // top left 
    };

    unsigned int indexData[6] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };
};

The main cpp. The original project used two cameras but I only need one so I removed them from render().


// 
// Collision detection
// 
// Squares with different textures bounce around the scene and collide with each other.
//
//

#include <tuple>
#include <cstdlib>
#include <cstdio>
#include <iostream>
#include <string>
#include <sys/time.h>

#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/matrix_inverse.hpp>

#include "GLFWApp.h"
#include "GLSLShader.h"
#include "glut_teapot.h"

#include "SpinningLight.h"
#include "Camera.h"
#include "UtahTeapot.h"
#include "Square.h"

void msglVersion(void){
  fprintf(stderr, "OpenGL Version Information:
");
  fprintf(stderr, "	Vendor: %s
", glGetString(GL_VENDOR));
  fprintf(stderr, "	Renderer: %s
", glGetString(GL_RENDERER));
  fprintf(stderr, "	OpenGL Version: %s
", glGetString(GL_VERSION));
  fprintf(stderr, "	GLSL Version: %s
",
          glGetString(GL_SHADING_LANGUAGE_VERSION));
}


class CollisionDetectionApp : public GLFWApp{
private:
  float rotationDelta;

  glm::vec3 centerPosition;
  Camera* currentCamera;
  // main point of view camera
  Camera mainCamera;
  // Bird's eye view camera
  Camera bevCamera;

  glm::mat4 modelViewMatrix;
  glm::mat4 projectionMatrix;
  glm::mat4 normalMatrix;
  
  GLSLProgram shaderProgram;

  SpinningLight light0;
  SpinningLight light1; 

  UtahTeapot* teapots[20];
  const int teapotCount = 20;

  Square* squares[4];
  const unsigned int squareCount = 4;

  bool debugMaterialFlag;

  // Variables to set uniform params for lighting fragment shader 
  unsigned int uModelViewMatrix;
  unsigned int uProjectionMatrix;
  unsigned int uNormalMatrix;
  unsigned int uLight0_position;
  unsigned int uLight0_color;
  unsigned int uLight1_position;
  unsigned int uLight1_color;
  unsigned int uAmbient;
  unsigned int uDiffuse;
  unsigned int uSpecular;
  unsigned int uShininess;
  
public:
  CollisionDetectionApp(int argc, char* argv[]) :
    GLFWApp(argc, argv, std::string("Teapot Vision").c_str( ), 
            600, 600){ }
  
  void initCenterPosition( ){
    centerPosition = glm::vec3(0.0, 0.0, 0.0);
  }
  
  void initTeapots( ){
    std::srand(time(NULL));
    for(int i = 0; i < teapotCount; i++){
      glm::vec3 _diffuseColor = glm::linearRand(glm::vec3(0.2), glm::vec3(1.0));
      //std::cerr << glm::to_string(_diffuseColor) << std::endl;
      glm::vec4 diffuseColor = glm::vec4(_diffuseColor, 1.0);
      Material* m = new Material(glm::vec4(0.2, 0.2, 0.2, 1.0), diffuseColor, glm::vec4(1.0, 1.0, 1.0, 1.0), 100.0);
      glm::vec2 xy = glm::diskRand(30.0);
      glm::vec3 position = glm::vec3(xy, 0.0);
      teapots[i] = new UtahTeapot(position, 1.0, m);
      teapots[i]->visible = true;
    }
  }

  void initSquares() {
    std::srand(time(NULL));
    for(int i = 0; i < squareCount; i++){
      glm::vec3 _diffuseColor = glm::linearRand(glm::vec3(0.2), glm::vec3(1.0));
      std::cerr << glm::to_string(_diffuseColor) << std::endl;
      glm::vec4 diffuseColor = glm::vec4(_diffuseColor, 1.0);
      Material* m = new Material(glm::vec4(0.2, 0.2, 0.2, 1.0), diffuseColor, glm::vec4(1.0, 1.0, 1.0, 1.0), 100.0);
      glm::vec2 xy = glm::diskRand(5.0);
      glm::vec3 position = glm::vec3(xy, 0.0);
      squares[i] = new Square(position, 1.0, m);
      squares[i]->visible = true;
      printf("square #: %i, visible: %i, position: %.2f, %.2f, %.2f
", i, squares[i]->visible, position.x, position.y, position.z);
    }
  }

  void initCamera( ){
    // Main point of view camera
    mainCamera = Camera(glm::vec3(0.0, 0.0, 20.0), glm::vec3(0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 0.0), 45.0, 0.2, 50.0);
    // Bird's eye view camera
    bevCamera =  Camera(glm::vec3(0.0, 0.0, 70.0), glm::vec3(0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 0.0), 45.0, 10.0, 150.0);
    currentCamera = &mainCamera;
  }

  void initRotationDelta( ){
    rotationDelta = 0.05;
  }
   
  void initLights( ){
    glm::vec3 color0(1.0, 1.0, 1.0);
    glm::vec3 position0(0.0, 20.0, 20.0);
    glm::vec3 color1(1.0, 1.0, 1.0);
    glm::vec3 position1(0.0, 20.0, -20.0);
    light0 = SpinningLight(color0, position0, centerPosition);
    light1 = SpinningLight(color1, position1, centerPosition);
  }

  bool begin( ){
    msglError( );
    initCenterPosition( );
    initSquares( );
    initCamera( );
    initRotationDelta( );
    initLights( );
    debugMaterialFlag = false;

    // Load shader programs
    const char* vertexShaderSource = "blinn_phong.vert.glsl";
    const char* fragmentShaderSource = "blinn_phong.frag.glsl";
    FragmentShader fragmentShader(fragmentShaderSource);
    VertexShader vertexShader(vertexShaderSource);
    shaderProgram.attach(vertexShader);
    shaderProgram.attach(fragmentShader);
    shaderProgram.link( );
    shaderProgram.activate( );
    
    printf("Shader program built from %s and %s.
",
           vertexShaderSource, fragmentShaderSource);
    if( shaderProgram.isActive( ) ){
      printf("Shader program is loaded and active with id %d.
", shaderProgram.id( ) );
    }else{
      printf("Shader program is not active, id: %d
.", shaderProgram.id( ));
    }
    
    // Set up uniform variables for the shader program
    uModelViewMatrix = glGetUniformLocation(shaderProgram.id( ), "modelViewMatrix");
    uProjectionMatrix = glGetUniformLocation(shaderProgram.id( ), "projectionMatrix");
    uNormalMatrix = glGetUniformLocation(shaderProgram.id( ), "normalMatrix");
    uLight0_position = glGetUniformLocation(shaderProgram.id( ), "light0_position");
    uLight0_color = glGetUniformLocation(shaderProgram.id( ), "light0_color");
    uLight1_position = glGetUniformLocation(shaderProgram.id( ), "light1_position");
    uLight1_color = glGetUniformLocation(shaderProgram.id( ), "light1_color");
    uAmbient = glGetUniformLocation(shaderProgram.id( ), "ambient");
    uDiffuse = glGetUniformLocation(shaderProgram.id( ), "diffuse");
    uSpecular = glGetUniformLocation(shaderProgram.id( ), "specular");
    uShininess = glGetUniformLocation(shaderProgram.id( ), "shininess");

    glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

    msglVersion( );
    
    return !msglError( );
  }
  
  bool end( ){
    windowShouldClose( );
    return true;
  }
  
  void activateUniforms(glm::vec4& _light0, glm::vec4& _light1, Material* m){
    glUniformMatrix4fv(uModelViewMatrix, 1, false, glm::value_ptr(modelViewMatrix));
    glUniformMatrix4fv(uProjectionMatrix, 1, false, glm::value_ptr(projectionMatrix));
    glUniformMatrix4fv(uNormalMatrix, 1, false, glm::value_ptr(normalMatrix));

    glUniform4fv(uLight0_position, 1, glm::value_ptr(_light0));
    glUniform4fv(uLight0_color, 1, glm::value_ptr(light0.color( )));
    
    glUniform4fv(uLight1_position, 1, glm::value_ptr(_light1));
    glUniform4fv(uLight1_color, 1, glm::value_ptr(light1.color( )));

    glUniform4fv(uAmbient, 1, glm::value_ptr(m->ambient));
    glUniform4fv(uDiffuse, 1, glm::value_ptr(m->diffuse));
    glUniform4fv(uSpecular, 1, glm::value_ptr(m->specular));
    glUniform1f(uShininess, m->shininess);
  }

  bool render( ){
    glm::vec4 _light0;
    glm::vec4 _light1;
    glm::mat4 lookAtMatrix;

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    std::tuple<int, int> w = windowSize( );
    double ratio = double(std::get<0>(w)) / double(std::get<1>(w));

    mainCamera.perspectiveMatrix(projectionMatrix, ratio);

    mainCamera.lookAtMatrix(lookAtMatrix);

    // Set light & material properties for the teapot;
    // lights are transformed by current modelview matrix
    // such that they are positioned correctly in the scene.
    _light0 = lookAtMatrix * light0.position4( );
    _light1 = lookAtMatrix * light1.position4( );
    
    for(int i = 0; i < squareCount; i++){
      if(squares[i]->visible){
        modelViewMatrix = glm::translate(lookAtMatrix, squares[i]->position);
        normalMatrix = glm::inverseTranspose(modelViewMatrix);
        shaderProgram.activate( );
        activateUniforms(_light0, _light1, squares[i]->material);
        squares[i]->draw( );
        //printf("square #: %i, visible: %i, position: %.2f, %.2f, %.2f
", i, squares[i]->visible, squares[i]->position.x, squares[i]->position.y, squares[i]->position.z);
      }
    }

    return !msglError( );
  }
    
};


int main(int argc, char* argv[]){
  CollisionDetectionApp app(argc, argv);
  return app();
}

And the snippet from the professor’s GLSLShader.h where the error message comes from (msglError() on line 9):


    #define msglError( ) _msglError( stderr, __FILE__, __LINE__ )
    //...
    bool compileShader( const GLchar *src ){
    GLint compiled_ok;
    char *msg;
    GLint length = (GLint)strlen(src);
    glShaderSource( _object, 1, &src, &length );
    glCompileShader( _object );
    msglError( );
    glGetShaderiv( _object, GL_COMPILE_STATUS, &compiled_ok );
    msglError( );
    if( !compiled_ok ){
      fprintf( stderr, "Compilation failed for shader %s
",
        _srcFileName );
      msg = getInfoLog( );
      fprintf( stderr, "Info Log:
%s
", msg );
      free( msg );
    }
    return( compiled_ok == 1 );
  }

I thought maybe it was an issue with each instance of a square having its own vao identical to all the others and my VM is running out of memory or something but if I try to draw 4, I usually end up with 3, and if I try to draw 10, I get about 4-7, with variances. Since no error occurs when I change it back to instatiating 20 teapots, I’m pretty certain it has to be something wrong with my draw() method in Square.h or my vao’s.

EDIT: All seems to be well when I write the draw method with glBegin(GL_TRIANGLE_STRIP) and glEnd(), so I think I will stick with this for the purpose of the project but I’m still curious to know why my original approach was giving me bad results.

void draw() {
      glBegin(GL_TRIANGLE_STRIP);
        glColor3f(0.5f, 0.5f, 0.5f);
        glVertex3f(vertexData[0], vertexData[1], vertexData[2]);
        glColor3f(0.5f, 0.5f, 0.5f);
        glVertex3f(vertexData[8], vertexData[9], vertexData[10]);
        glColor3f(0.5f, 0.5f, 0.5f);
        glVertex3f(vertexData[24], vertexData[25], vertexData[26]);
        glColor3f(0.5f, 0.5f, 0.5f);
        glVertex3f(vertexData[8], vertexData[9], vertexData[10]);
        glColor3f(0.5f, 0.5f, 0.5f);
        glVertex3f(vertexData[16], vertexData[17], vertexData[18]);
        glColor3f(0.5f, 0.5f, 0.5f);
        glVertex3f(vertexData[24], vertexData[25], vertexData[26]);
      glEnd();
    }

You aren’t showing the code where you call the compileShader() function.

The number of squares should be irrelevant because you shouldn’t be compiling the shader more than once.