Drawing many separate lines using mouse GLFW/glad

So, in order to draw a line, I track the coordinates of the mouse, then I add them to the array and capture it as GL_LINE_STRIP_ADJACENCY. However, for example, I completed drawing a line1 at P1 and decided to start drawing a different line at P2 as shown in the figure, but my two points P1 and P2 joined together, how to fix it? Need to clear the array after drawing at the point P1, actually it doesn’t help if I use glClearColor and glClear(GL_COLOR_BUFFER_BIT)… Is there any other way?
[ATTACH=CONFIG]1872[/ATTACH]

Someone can help me?

There are many options to do what you want. Here are some:

  • Create a separate array for each line. Then load each into a VBO and draw it. (probably the best solution)
  • Add a line end attribute to the array. If the first point says true, drop the line. This needs a geometry shader to work. (inefficient solution, but should work)
  • Use indexed drawing. You store all points in a buffer. The index buffer tells openGL which points are connected with a line. (Quiet wasteful because you need more memory than with the other approaches)

There are probably smarter and more efficient ways. Maybe some of the more experienced guys here can tell you about them. But for now, you can try one of the solutions above.

Note that the *_ADJACENCY primitive types are only useful if you’re using a geometry shader. Otherwise, using GL_LINE_STRIP_ADJACENCY will simply cause the first and last segments in each strip to be discarded (because those segments are missing an adjacent vertex).

Options include:

[ol]
[li] Use glDrawElements(GL_LINES, …) and provide an element array to connect vertices into line segments.
[/li][li] Use glMultiDrawArrays(GL_LINE_STRIP, …) to identify the ranges which form strips.
[/li][li] Use glDrawElements(GL_LINE_STRIP, …), glEnable(GL_PRIMITIVE_RESTART) and glPrimitiveRestartIndex().
[/li][li] Use glDrawElements(GL_LINE_STRIP, …) and glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX).
[/li][/ol]

Options 1-3 require an element (index) array. Options 2-3 require one index per line, whereas option 1 requires two indices. Option 1 requires OpenGL 1.1, option 2 requires OpenGL 2.0, option 3 requires OpenGL 3.1, option 4 requires OpenGL 4.3.

[QUOTE=GClements;1292798]Note that the *_ADJACENCY primitive types are only useful if you’re using a geometry shader. Otherwise, using GL_LINE_STRIP_ADJACENCY will simply cause the first and last segments in each strip to be discarded (because those segments are missing an adjacent vertex).

Options include:

[ol]
[li] Use glDrawElements(GL_LINES, …) and provide an element array to connect vertices into line segments.
[/li][li] Use glMultiDrawArrays(GL_LINE_STRIP, …) to identify the ranges which form strips.
[/li][li] Use glDrawElements(GL_LINE_STRIP, …), glEnable(GL_PRIMITIVE_RESTART) and glPrimitiveRestartIndex().
[/li][li] Use glDrawElements(GL_LINE_STRIP, …) and glEnable(GL_PRIMITIVE_RESTART_FIXED_INDEX).
[/li][/ol]

Options 1-3 require an element (index) array. Options 2-3 require one index per line, whereas option 1 requires two indices. Option 1 requires OpenGL 1.1, option 2 requires OpenGL 2.0, option 3 requires OpenGL 3.1, option 4 requires OpenGL 4.3.[/QUOTE]

So, can I do it using just one VBO, VAO and EBO(I need them too, yes?) ? And how I can generate indexes on line in EBO?
I have for example std::vector<std::vector<Vector3f>> arrayOfVertArrays, when I click on mouse I pushing mouse coords in some std::vector<Vector3f> and then this vector in my arrayOfVertArrays, and same every time… am i doing it right? and how than it draw?

[QUOTE=ProgrammerX;1292797][/QUOTE]
What is right way to load my separate arrays in one VBO? And how then draw it, using VAO
Smth like that:
glBindVertexArray(m_VAO);
for(const auto& i : arrayOfVertArrays)
{
glDrawArrays(GL_LINE_STRIP, 0, i.size());
}
glBindVertexArray(0);
??

The *DrawElements() functions need an EBO. glMultiDrawArrays() doesn’t. You only need one VAO and VBO.

For GL_LINES, the indices would be 0,1,1,2,2,3,… etc, with the last index of one segment equal to the first index of the next. So each index is duplicated, except for the first and last vertex of each strip. For GL_LINE_STRIP with primitive restarting, the indices would just be 0,1,2,3,… etc, but with the restart index between each strip.

[QUOTE=Kovaczboi;1292800]
I have for example std::vector<std::vector<Vector3f>> arrayOfVertArrays, when I click on mouse I pushing mouse coords in some std::vector<Vector3f> and then this vector in my arrayOfVertArrays, and same every time… am i doing it right? and how than it draw?[/QUOTE]
Concatenate all of the vectors into a single VBO. Keep track of the starting index for each strip. Then you can use glMultiDrawArrays() with the offsets and counts. E.g. to populate the VBO:


std::vector<GLint> first,
std::vector<GLsizei> count,
GLuint vbo;

GLsizeiptr total = 0;
for (const auto& a : arrayOfVertArrays)
    total += a.size();
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, total * sizeof(Vector3f), (const GLvoid*) 0, GL_STATIC_DRAW);
GLintptr offset = 0;
for (const auto& a : arrayOfVertArrays) {
    first.push_back(offset);
    count.push_back(a.size());
    glBufferSubData(GL_ARRAY_BUFFER, offset, a.size(), a.data());
    offset += a.size();
}

To render from the VBO:


glMultiDrawArrays(GL_LINE_STRIP, first.data(), count.data(), first.size());

But unless you need to be able to edit previously-created strips, it may be better to skip arrayOfVertArrays and store the data directly in the VBO as each point is entered (replacing it with a larger buffer whenever it gets full).

[QUOTE=GClements;1292803]The *DrawElements() functions need an EBO. glMultiDrawArrays() doesn’t. You only need one VAO and VBO.

For GL_LINES, the indices would be 0,1,1,2,2,3,… etc, with the last index of one segment equal to the first index of the next. So each index is duplicated, except for the first and last vertex of each strip. For GL_LINE_STRIP with primitive restarting, the indices would just be 0,1,2,3,… etc, but with the restart index between each strip.

Concatenate all of the vectors into a single VBO. Keep track of the starting index for each strip. Then you can use glMultiDrawArrays() with the offsets and counts. E.g. to populate the VBO:


std::vector<GLint> first,
std::vector<GLsizei> count,
GLuint vbo;

GLsizeiptr total = 0;
for (const auto& a : arrayOfVertArrays)
    total += a.size();
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, total * sizeof(Vector3f), (const GLvoid*) 0, GL_STATIC_DRAW);
GLintptr offset = 0;
for (const auto& a : arrayOfVertArrays) {
    first.push_back(offset);
    count.push_back(a.size());
    glBufferSubData(GL_ARRAY_BUFFER, offset, a.size(), a.data());
    offset += a.size();
}

To render from the VBO:


glMultiDrawArrays(GL_LINE_STRIP, first.data(), count.data(), first.size());

But unless you need to be able to edit previously-created strips, it may be better to skip arrayOfVertArrays and store the data directly in the VBO as each point is entered (replacing it with a larger buffer whenever it gets full).[/QUOTE]

Sry, I’m maybe stupid, but what am I doing wrong? I’m try to use glDrawElements(GL_LINE_STRIP, …), glEnable(GL_PRIMITIVE_RESTART) and glPrimitiveRestartIndex(). But get not what I needed… (see screen)


void update(GLFWwindow* window)
{
	if (g_mouseClicked)
	{
		glBindBuffer(GL_ARRAY_BUFFER, VBO);
		glBufferData(GL_ARRAY_BUFFER, static_cast<int>(g_vertices.size() * sizeof(mlg::Vector3f)), g_vertices.data(), GL_DYNAMIC_DRAW);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
		glBufferData(GL_ELEMENT_ARRAY_BUFFER, static_cast<int>(g_indexes.size() * sizeof(unsigned)), g_indexes.data(), GL_DYNAMIC_DRAW);

		glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
		glBindBuffer(GL_ARRAY_BUFFER, 0);

		glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
		glEnableVertexAttribArray(0);

		glfwGetCursorPos(window, &xpos, &ypos);
		g_vertices.push_back(get_2d_ndc_coord(static_cast<float>(xpos), static_cast<float>(ypos)));
		g_indexes.push_back(g_vertices.size());
	}
	else if (g_mouseReleased)
	{
		g_indexes.push_back(0xFFFF);
	}
}

void mouseButtonCallback(GLFWwindow* window, int scancode, int action, int mods)
{
	g_mouseClicked = true;
	glfwGetCursorPos(window, &xpos, &ypos);
	if (scancode == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS)
	{
		g_mouseClicked  = true;
		g_mouseReleased = false;		
	}
	else if (action == GLFW_RELEASE)
	{
		g_mouseClicked  = false;
		g_mouseReleased = true;
	}
}

//in main()
	glGenVertexArrays(1, &VAO);
	glGenBuffers(1, &VBO);
	glGenBuffers(1, &EBO);

 	glBindVertexArray(VAO);
  	glBindBuffer(GL_ARRAY_BUFFER, VBO);

  	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);

 	glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
 	glEnableVertexAttribArray(0);

	glBindVertexArray(0);
	glBindBuffer(GL_ARRAY_BUFFER, 0);
	glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

	// render loop
	while (!glfwWindowShouldClose(window))
	{
		// render
		glClearColor(1.f, 1.f, 1.f, 1.f);
		glClear(GL_COLOR_BUFFER_BIT);

		shaderProg.use();
		update(window);

		glBindVertexArray(VAO);
		glEnable(GL_PRIMITIVE_RESTART);
		glPrimitiveRestartIndex(0xFFFF);
		glDrawElements(GL_LINE_STRIP, g_indexes.size(), GL_UNSIGNED_INT, reinterpret_cast<void*>(0));
		glDisable(GL_PRIMITIVE_RESTART);
		glBindVertexArray(0);

		glfwSwapBuffers(window);
		glfwPollEvents();
	}

[ATTACH=CONFIG]1873[/ATTACH]

UPD.
Yea, in void update(GLFWwindow* window) if swap places from


g_vertices.push_back(get_2d_ndc_coord(static_cast<float>(xpos), static_cast<float>(ypos)));
g_indexes.push_back(g_vertices.size());

to


g_indexes.push_back(g_vertices.size());
g_vertices.push_back(get_2d_ndc_coord(static_cast<float>(xpos), static_cast<float>(ypos)));

it will slightly improve the situation, but still not good
[ATTACH=CONFIG]1874[/ATTACH]

[QUOTE=Kovaczboi;1292804]Sry, I’m maybe stupid, but what am I doing wrong? I’m try to use glDrawElements(GL_LINE_STRIP, …), glEnable(GL_PRIMITIVE_RESTART) and glPrimitiveRestartIndex(). But get not what I needed… (see screen)
[/QUOTE]
One thing that’s wrong is that you’re updating the buffers before appending to the vectors, so the last point added isn’t in the buffer. But it is included in the value of g_indexes.size(), so the draw call will read beyond the end of the buffer.

Can I ask one more question? When I, for example, painted something as I did it above, how now I may save this, as image? I try use glReadPixels and SOIL but that what I draw didn’t save, only white screen.

You need to look at the contents of the memory which should be filled by glReadPixels() to determine which part isn’t working (the reading or the saving). Also check for errors.

Also, note that a buffer swap invalidates the back buffer, so you need to call glReadPixels() before the swap.

[QUOTE=GClements;1292822]You need to look at the contents of the memory which should be filled by glReadPixels() to determine which part isn’t working (the reading or the saving). Also check for errors.

Also, note that a buffer swap invalidates the back buffer, so you need to call glReadPixels() before the swap.[/QUOTE]

Actually, all working or I’m braking… If I’m load texture and then draw over it, on my screen all is ok, I drawing, but when I saving, store only clear texture…
[ATTACH=CONFIG]1876[/ATTACH]
[ATTACH=CONFIG]1877[/ATTACH]