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),
¤t_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!