-1

I am trying to draw 2 different types of structures using 2 separate VBO's. Drawing each one individually works perfectly, but once I try and switch between the 2 something weird happens. Whichever structure I draw second keeps being drawn even once I go back to the first. There is a decent amount of background code to understand, so I will try and be as concise as possible. The project is here: https://github.com/steve3424/data_structure_visualizations/tree/avl_tree and I will put line numbers for reference.

First I set up a list of void pointers that will eventually contain pointers to the structures:

win32_main.cpp - line 463

game_state->data_structures = (void**)calloc((int)NUM_VIEWS, sizeof(void*));
game_state->current_view = (View)0;

Then this switch statement gets called in each loop. I call the corresponding Init() functions on the first pass through the switch case, then update and draw on subsequent calls. AVLTree only has init and draw for now:

engine.cpp - line 69

    switch(game_state->current_view) {
        case INSERTION_SORT: 
        {
            ISort* isort = (ISort*)game_state->data_structures[INSERTION_SORT];
            if(!isort) {
                game_state->data_structures[INSERTION_SORT] = ISort_Init();
                isort = (ISort*)game_state->data_structures[INSERTION_SORT];
            }
            UpdateCamera(&isort->camera, input);
            ISort_Update(isort, input);
            ISort_Draw(isort, (float)game_state->window_width, (float)game_state->window_height);
        } break;

        case AVL_TREE:
        {
            AVLTree* avl_tree = (AVLTree*)game_state->data_structures[AVL_TREE];
            if(!avl_tree) {
                game_state->data_structures[AVL_TREE] = AVLTree_Init();
                avl_tree = (AVLTree*)game_state->data_structures[AVL_TREE];
            }
            UpdateCamera(&avl_tree->camera, input);
            AVLTree_Draw(avl_tree, (float)game_state->window_width, (float)game_state->window_height);
        } break;

        default:
        {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glClearColor(0.694f, 0.612f, 0.851f, 1.0f);
        } break;
    }

The init functions have a lot in them, so here is the opengl portions. I generate a buffer and put it into the isort structure. Then I bind the buffer, enable the vertex attrib pointers, and unbind the buffer:

insertion_sort.cpp - line 102

    GLCall(glGenBuffers(1, &isort->vbo));
    GLCall(glBindBuffer(GL_ARRAY_BUFFER, isort->vbo));
    GLCall(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, pos))));
    GLCall(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, rgb))));
    GLCall(glEnableVertexAttribArray(0));
    GLCall(glEnableVertexAttribArray(1));

    GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));

The isort draw function. I suspect the issue is somewhere in here. I clear everything and bind the proper buffer and shader. I then call glBufferData with NULL as described here (OpenGL VBO orphaning implementation), then I call glBufferSubData for each object. I make the draw call and unbind the buffer. This works by itself perfectly well:

insertion_sort.cpp - line 484

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //ISort_DrawBackground(isort->background, window_width, window_height);

    GLCall(glBindBuffer(GL_ARRAY_BUFFER, isort->vbo));
    GLCall(glUseProgram(isort->shader));

    unsigned int buffer_size = INSERTION_SORT_SIZE * sizeof(GameCube);
    GLCall(glBufferData(GL_ARRAY_BUFFER, buffer_size, NULL, GL_DYNAMIC_DRAW));
    for(int i = 0; i < INSERTION_SORT_SIZE; ++i) {
        GLCall(glBufferSubData(GL_ARRAY_BUFFER, i * sizeof(GameCube), sizeof(GameCube), 
                               &isort->nodes[i].cube));
    }
    
    GLCall(glLineWidth(4.0f));
    GLCall(glEnable(GL_DEPTH_TEST));

    int model_location = glGetUniformLocation(isort->shader, "model");
    int view_location = glGetUniformLocation(isort->shader, "view");
    int projection_location = glGetUniformLocation(isort->shader, "projection");

    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 view = glm::mat4(1.0f);
    view = glm::translate(view, glm::vec3(isort->camera.x, 
                                          isort->camera.y, 
                                          isort->camera.z));
    glm::mat4 projection = glm::perspective(glm::radians(75.0f), 
                                            (float)window_width / (float)window_height, 
                                            0.1f, 100.0f);

    glUniformMatrix4fv(model_location, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(view_location, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projection_location, 1, GL_FALSE, glm::value_ptr(projection));

    unsigned int vertices_per_cube = sizeof(GameCube) / sizeof(Vertex);
    GLCall(glDrawArrays(GL_LINES, 0, INSERTION_SORT_SIZE * VERTICES_PER_CUBE));

    GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));

Avl_tree init fuction. Same process as isort init, but everything is put into avl_tree structure: avl_tree.cpp - line 290

    GLCall(glGenBuffers(1, &avl_tree->vbo));
    GLCall(glBindBuffer(GL_ARRAY_BUFFER, avl_tree->vbo));
    GLCall(glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, pos))));
    GLCall(glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(offsetof(Vertex, rgb))));
    GLCall(glEnableVertexAttribArray(0));
    GLCall(glEnableVertexAttribArray(1));

    GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));

Avl tree draw function. Again I clear everything, bind everything, then do the same glBufferData w/ NULL as stated above. I do a BFS to get all the geometry data from the nodes and put it into the buffer, make draw call, then unbind the buffer. This also works on its own.:

avl_tree.cpp - line 302

    GLCall(glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));

    GLCall(glBindBuffer(GL_ARRAY_BUFFER, avl_tree->vbo));
    GLCall(glUseProgram(avl_tree->shader));

    unsigned int buffer_size = avl_tree->size * sizeof(GameCube);
    GLCall(glBufferData(GL_ARRAY_BUFFER, buffer_size, NULL, GL_DYNAMIC_DRAW));

    // BFS
    int push = 0;
    int pop = 0;
    int size = 0;
    uint64_t node_indices[MAX_DIGITS] = {0};
    AVLNode* nodes[MAX_DIGITS] = {NULL};
    nodes[push] = avl_tree->root;
    ++push;
    ++size;
    unsigned int buffer_write_index = 0;
    while(size > 0) {
        int nodes_at_this_level = size;
        for(int i = 0; i < nodes_at_this_level; ++i) {
            AVLNode* current_node = nodes[pop];
            uint64_t current_index = node_indices[pop];
            pop = (pop + 1) % MAX_DIGITS;
            --size;

            GLCall(glBufferSubData(GL_ARRAY_BUFFER, buffer_write_index, sizeof(GameCube), 
                                   &current_node->cube));
            buffer_write_index += sizeof(GameCube);

            if(current_node->left) {
                nodes[push] = current_node->left;
                node_indices[push] = (2 * current_index);
                push = (push + 1) % MAX_DIGITS;
                ++size;
            }

            if(current_node->right) {
                nodes[push] = current_node->right;
                node_indices[push] = (2 * current_index) + 1;
                push = (push + 1) % MAX_DIGITS;
                ++size;
            }
        }
    }

    GLCall(glLineWidth(4.0f));
    GLCall(glEnable(GL_DEPTH_TEST));

    int model_location = glGetUniformLocation(avl_tree->shader, "model");
    int view_location = glGetUniformLocation(avl_tree->shader, "view");
    int projection_location = glGetUniformLocation(avl_tree->shader, "projection");

    glm::mat4 model = glm::mat4(1.0f);
    glm::mat4 view = glm::mat4(1.0f);
    view = glm::translate(view, glm::vec3(avl_tree->camera.x, 
                                          avl_tree->camera.y, 
                                          avl_tree->camera.z));
    glm::mat4 projection = glm::perspective(glm::radians(75.0f), 
                                            (float)window_width / (float)window_height, 
                                            0.1f, 100.0f);

    glUniformMatrix4fv(model_location, 1, GL_FALSE, glm::value_ptr(model));
    glUniformMatrix4fv(view_location, 1, GL_FALSE, glm::value_ptr(view));
    glUniformMatrix4fv(projection_location, 1, GL_FALSE, glm::value_ptr(projection));

    unsigned int vertices_per_cube = sizeof(GameCube) / sizeof(Vertex);
    GLCall(glDrawArrays(GL_LINES, 0, avl_tree->size * VERTICES_PER_CUBE));

    GLCall(glBindBuffer(GL_ARRAY_BUFFER, 0));

So the weird part. When I try and switch between the two in the switch statement, the initial view, whatever it is, gets drawn correctly. Then I switch and the second view also gets drawn correctly. Then when I switch back to view 1, the second view is still being drawn and the first view is no where to be seen. My initial thought was that I wasn't clearing properly, but I am clearing at the top of both draw functions. Then I thought maybe I wasn't binding and unbinding buffers properly, but I think I am doing that. I bind at the top, then unbind at the bottom. I am not using VAO so I don't need to unbind and index buffers. I also bind and unbind in the init functions. I suspect it has something to do with how I am buffering data in the draw functions, but I really don't know enough about opengl to troubleshoot. I have tried calling glBufferData in the init functions, then only calling glBufferSubData during the draw function.

Thanks for any help!

Steve Frazee
  • 173
  • 1
  • 7

1 Answers1

-1

So I did not realize that even though I am not using an index buffer, I still need to use a VAO along with a VBO. Seems to be working now.

Steve Frazee
  • 173
  • 1
  • 7
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 05 '21 at 05:20