Browse Source

Smooth lines now draw the entire line in 1 OpenGL draw call, rather than 2.

Alex Szpakowski 10 years ago
parent
commit
bf1b409da1
2 changed files with 73 additions and 32 deletions
  1. 63 27
      src/modules/graphics/opengl/Polyline.cpp
  2. 10 5
      src/modules/graphics/opengl/Polyline.h

+ 63 - 27
src/modules/graphics/opengl/Polyline.cpp

@@ -75,12 +75,40 @@ void Polyline::render(const float *coords, size_t count, size_t size_hint, float
 	renderEdge(anchors, normals, s, len_s, ns, q, r, halfwidth);
 
 	vertex_count = normals.size();
-	vertices = new Vector[vertex_count];
+
+	size_t extra_vertices = 0;
+
+	if (draw_overdraw)
+	{
+		calc_overdraw_vertex_count(is_looping);
+
+		// When drawing overdraw lines using triangle strips, we want to add an
+		// extra degenerate triangle in between the core line and the overdraw
+		// line in order to break up the strip into two. This will let us draw
+		// everything in one draw call.
+		if (draw_mode == GL_TRIANGLE_STRIP)
+			extra_vertices = 2;
+	}
+
+	// Use a single linear array for both the regular and overdraw vertices.
+	vertices = new Vector[vertex_count + extra_vertices + overdraw_vertex_count];
+
 	for (size_t i = 0; i < vertex_count; ++i)
 		vertices[i] = anchors[i] + normals[i];
 
 	if (draw_overdraw)
+	{
+		overdraw = vertices + vertex_count + extra_vertices;
+		overdraw_vertex_start = vertex_count + extra_vertices;
 		render_overdraw(normals, pixel_size, is_looping);
+	}
+
+	// Add the degenerate triangle strip.
+	if (extra_vertices)
+	{
+		vertices[vertex_count + 0] = vertices[vertex_count - 1];
+		vertices[vertex_count + 1] = vertices[overdraw_vertex_start];
+	}
 }
 
 void NoneJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,
@@ -238,10 +266,13 @@ void BevelJoinPolyline::renderEdge(std::vector<Vector> &anchors, std::vector<Vec
 	ns    = nt;
 }
 
-void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping)
+void Polyline::calc_overdraw_vertex_count(bool is_looping)
 {
 	overdraw_vertex_count = 2 * vertex_count + (is_looping ? 0 : 2);
-	overdraw = new Vector[overdraw_vertex_count];
+}
+
+void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping)
+{
 	// upper segment
 	for (size_t i = 0; i + 1 < vertex_count; i += 2)
 	{
@@ -284,10 +315,13 @@ void Polyline::render_overdraw(const std::vector<Vector> &normals, float pixel_s
 	}
 }
 
-void NoneJoinPolyline::render_overdraw(const std::vector<Vector> &/*normals*/, float pixel_size, bool /*is_looping*/)
+void NoneJoinPolyline::calc_overdraw_vertex_count(bool /*is_looping*/)
 {
 	overdraw_vertex_count = 4 * (vertex_count-2); // less than ideal
-	overdraw = new Vector[overdraw_vertex_count];
+}
+
+void NoneJoinPolyline::render_overdraw(const std::vector<Vector> &/*normals*/, float pixel_size, bool /*is_looping*/)
+{
 	for (size_t i = 2; i + 3 < vertex_count; i += 4)
 	{
 		Vector s = vertices[i] - vertices[i+3];
@@ -322,8 +356,6 @@ Polyline::~Polyline()
 {
 	if (vertices)
 		delete[] vertices;
-	if (overdraw)
-		delete[] overdraw;
 }
 
 void Polyline::draw()
@@ -331,13 +363,16 @@ void Polyline::draw()
 	OpenGL::TempDebugGroup debuggroup("Line draw");
 
 	GLushort *indices = nullptr;
+	Color *colors = nullptr;
+
+	size_t total_vertex_count = vertex_count;
+	if (overdraw)
+		total_vertex_count = overdraw_vertex_start + overdraw_vertex_count;
 
 	// TODO: We should probably be using a reusable index buffer.
 	if (use_quad_indices)
 	{
-		size_t numindices = (vertex_count / 4) * 6;
-		if (overdraw)
-			numindices = std::max(numindices, (overdraw_vertex_count / 4) * 6);
+		size_t numindices = (total_vertex_count / 4) * 6;
 
 		try
 		{
@@ -366,39 +401,40 @@ void Polyline::draw()
 
 	gl.prepareDraw();
 
-	// draw the core line
 	gl.bindTexture(gl.getDefaultTexture());
 	glEnableVertexAttribArray(ATTRIB_POS);
-	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, vertices);
-
-	if (use_quad_indices)
-		gl.drawElements(draw_mode, (int) (vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
-	else
-		gl.drawArrays(draw_mode, 0, (int) vertex_count);
+	glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, vertices + vertex_start);
 
 	if (overdraw)
 	{
-		// prepare colors:
-		Color *colors = new Color[overdraw_vertex_count];
-		fill_color_array(colors);
+		// Prepare colors. Set the core line's colors to white, and the overdraw
+		// line's colors to white on one side and transparent on the other.
+		colors = new Color[total_vertex_count];
+		memset(colors, 255, overdraw_vertex_start * sizeof(Color));
+		fill_color_array(colors + overdraw_vertex_start);
 
 		glEnableVertexAttribArray(ATTRIB_COLOR);
 		glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, colors);
-		glVertexAttribPointer(ATTRIB_POS, 2, GL_FLOAT, GL_FALSE, 0, overdraw);
+	}
 
-		if (use_quad_indices)
-			gl.drawElements(draw_mode, (int) (overdraw_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
-		else
-			gl.drawArrays(draw_mode, 0, (int) overdraw_vertex_count);
+	// Draw the core line and the overdraw in a single draw call. We can do this
+	// because the vertex array contains both the core line and the overdraw
+	// vertices.
+	if (use_quad_indices)
+		gl.drawElements(draw_mode, (int) (total_vertex_count / 4) * 6, GL_UNSIGNED_SHORT, indices);
+	else
+		gl.drawArrays(draw_mode, 0, (int) total_vertex_count);
+
+	glDisableVertexAttribArray(ATTRIB_POS);
 
+	if (overdraw)
+	{
 		glDisableVertexAttribArray(ATTRIB_COLOR);
 		glVertexAttrib4f(ATTRIB_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
 
 		delete[] colors;
 	}
 
-	glDisableVertexAttribArray(ATTRIB_POS);
-
 	if (indices)
 		delete[] indices;
 }

+ 10 - 5
src/modules/graphics/opengl/Polyline.h

@@ -46,12 +46,14 @@ class Polyline
 {
 public:
 	Polyline(GLenum mode = GL_TRIANGLE_STRIP, bool quadindices = false)
-		: vertices(NULL)
-		, overdraw(NULL)
+		: vertices(nullptr)
+		, overdraw(nullptr)
 		, vertex_count(0)
 		, overdraw_vertex_count(0)
 		, draw_mode(mode)
 		, use_quad_indices(quadindices)
+		, vertex_start(0)
+		, overdraw_vertex_start(0)
 	{}
 	virtual ~Polyline();
 
@@ -70,6 +72,7 @@ public:
 	void draw();
 
 protected:
+	virtual void calc_overdraw_vertex_count(bool is_looping);
 	virtual void render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping);
 	virtual void fill_color_array(Color *colors);
 
@@ -94,6 +97,8 @@ protected:
 	size_t overdraw_vertex_count;
 	GLenum draw_mode;
 	bool use_quad_indices;
+	size_t vertex_start;
+	size_t overdraw_vertex_start;
 
 }; // Polyline
 
@@ -112,13 +117,13 @@ public:
 	void render(const float *vertices, size_t count, float halfwidth, float pixel_size, bool draw_overdraw)
 	{
 		Polyline::render(vertices, count, 2 * count - 4, halfwidth, pixel_size, draw_overdraw);
-		// discard the first and last two vertices. (these are redundant)
-		for (size_t i = 0; i < vertex_count - 2; ++i)
-			this->vertices[i] = this->vertices[i+2];
+		// discard the first two vertices. (these are redundant)
+		vertex_start = 2;
 		vertex_count -= 2;
 	}
 
 protected:
+	virtual void calc_overdraw_vertex_count(bool is_looping);
 	virtual void render_overdraw(const std::vector<Vector> &normals, float pixel_size, bool is_looping);
 	virtual void fill_color_array(Color *colors);
 	virtual void renderEdge(std::vector<Vector> &anchors, std::vector<Vector> &normals,