mirror of
https://github.com/love2d/love.git
synced 2026-08-19 20:19:42 +02:00
Fix issue 246: Add line antialiasing through 'overdraw'.
Points to consider: - Make softness customizable (`halo' in Graphics::polyline()). - Add overdraw at beginning and ending edge of the line.
This commit is contained in:
@@ -38,7 +38,7 @@ namespace opengl
|
|||||||
{
|
{
|
||||||
|
|
||||||
Graphics::Graphics()
|
Graphics::Graphics()
|
||||||
: currentFont(0), currentImageFilter(), lineWidth(1), matrixLimit(0), userMatrices(0)
|
: currentFont(0), currentImageFilter(), lineStyle(LINE_SMOOTH), lineWidth(1), matrixLimit(0), userMatrices(0)
|
||||||
{
|
{
|
||||||
currentWindow = love::window::sdl::Window::getSingleton();
|
currentWindow = love::window::sdl::Window::getSingleton();
|
||||||
}
|
}
|
||||||
@@ -78,8 +78,7 @@ namespace opengl
|
|||||||
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &mode);
|
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &mode);
|
||||||
s.colorMode = (mode == GL_MODULATE) ? Graphics::COLOR_MODULATE : Graphics::COLOR_REPLACE;
|
s.colorMode = (mode == GL_MODULATE) ? Graphics::COLOR_MODULATE : Graphics::COLOR_REPLACE;
|
||||||
//get line style
|
//get line style
|
||||||
//s.lineStyle = (glIsEnabled(GL_POLYGON_SMOOTH) == GL_TRUE) ? Graphics::LINE_SMOOTH : Graphics::LINE_ROUGH;
|
s.lineStyle = lineStyle;
|
||||||
s.lineStyle = Graphics::LINE_ROUGH;
|
|
||||||
//get the point size
|
//get the point size
|
||||||
glGetFloatv(GL_POINT_SIZE, &s.pointSize);
|
glGetFloatv(GL_POINT_SIZE, &s.pointSize);
|
||||||
//get point style
|
//get point style
|
||||||
@@ -587,18 +586,9 @@ namespace opengl
|
|||||||
lineWidth = width;
|
lineWidth = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::setLineStyle(Graphics::LineStyle/* style*/ )
|
void Graphics::setLineStyle(Graphics::LineStyle style)
|
||||||
{
|
{
|
||||||
//// XXX: actually enables antialiasing for _all_ polygons.
|
lineStyle = style;
|
||||||
//// may need investigation if wanted or not
|
|
||||||
//// maybe rename to something else?
|
|
||||||
//if (style == LINE_ROUGH)
|
|
||||||
// glDisable (GL_POLYGON_SMOOTH);
|
|
||||||
//else // type == LINE_SMOOTH
|
|
||||||
//{
|
|
||||||
// glEnable (GL_POLYGON_SMOOTH);
|
|
||||||
// glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::setLine( float width, Graphics::LineStyle style )
|
void Graphics::setLine( float width, Graphics::LineStyle style )
|
||||||
@@ -619,10 +609,7 @@ namespace opengl
|
|||||||
|
|
||||||
Graphics::LineStyle Graphics::getLineStyle()
|
Graphics::LineStyle Graphics::getLineStyle()
|
||||||
{
|
{
|
||||||
//if (glIsEnabled(GL_POLYGON_SMOOTH) == GL_TRUE)
|
return lineStyle;
|
||||||
// return LINE_SMOOTH;
|
|
||||||
//else
|
|
||||||
return LINE_ROUGH;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::setPointSize( float size )
|
void Graphics::setPointSize( float size )
|
||||||
@@ -722,75 +709,167 @@ namespace opengl
|
|||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate line boundary intersection vertices for current line
|
// Calculate line boundary points u1 and u2. Sketch:
|
||||||
// dependent on the current *and next* line segment
|
// u1
|
||||||
static void pushIntersectionPoints(Vector *vertices, int pos, float halfwidth, const Vector& p, const Vector& q, const Vector& r)
|
// -------------+---...___
|
||||||
|
// | ```'''-- ---
|
||||||
|
// p- - - - - - q- - . _ _ | w/2
|
||||||
|
// | ` ' ' r +
|
||||||
|
// -------------+---...___ | w/2
|
||||||
|
// u2 ```'''-- ---
|
||||||
|
//
|
||||||
|
// u1 and u2 depend on four things:
|
||||||
|
// - the half line width w/2
|
||||||
|
// - the previous line vertex p
|
||||||
|
// - the current line vertex q
|
||||||
|
// - the next line vertex r
|
||||||
|
//
|
||||||
|
// u1/u2 are the intersection points of the parallel lines to p-q and q-r,
|
||||||
|
// i.e. the point where
|
||||||
|
//
|
||||||
|
// (p + w/2 * n1) + mu * (q - p) = (q + w/2 * n2) + lambda * (r - q) (u1)
|
||||||
|
// (p - w/2 * n1) + mu * (q - p) = (q - w/2 * n2) + lambda * (r - q) (u2)
|
||||||
|
//
|
||||||
|
// with n1,n2 being the normals on the segments p-q and q-r:
|
||||||
|
//
|
||||||
|
// n1 = perp(q - p) / |q - p|
|
||||||
|
// n2 = perp(r - q) / |r - q|
|
||||||
|
//
|
||||||
|
// The intersection points can be calculated using cramers rule.
|
||||||
|
static void pushIntersectionPoints(Vector *vertices, int pos, float halfwidth, const Vector& p, const Vector& q, const Vector& r,
|
||||||
|
Vector* overdraw_top, Vector* overdraw_bottom, float halo)
|
||||||
{
|
{
|
||||||
// calculate line directions
|
// calculate line directions
|
||||||
Vector s = (q - p);
|
Vector s = (q - p);
|
||||||
Vector t = (r - q);
|
Vector t = (r - q);
|
||||||
|
|
||||||
// calculate vertex displacement vectors
|
// calculate vertex displacement vectors
|
||||||
Vector d1 = s.getNormal();
|
Vector n1 = s.getNormal();
|
||||||
Vector d2 = t.getNormal();
|
Vector n2 = t.getNormal();
|
||||||
d1.normalize();
|
n1.normalize();
|
||||||
d2.normalize();
|
n2.normalize();
|
||||||
float det_norm = d1 ^ d2;
|
float det_norm = n1 ^ n2; // will be close to zero if the angle between the normals is sharp
|
||||||
d1 *= halfwidth;
|
n1 *= halfwidth;
|
||||||
d2 *= halfwidth;
|
n2 *= halfwidth;
|
||||||
|
|
||||||
// lines parallel -> assume intersection at displacement points
|
// lines parallel -> assume intersection at displacement points
|
||||||
if (fabs(det_norm) <= .03)
|
if (fabs(det_norm) <= .03)
|
||||||
{
|
{
|
||||||
vertices[pos] = q - d2;
|
vertices[pos] = q - n2;
|
||||||
vertices[pos + 1] = q + d2;
|
vertices[pos + 1] = q + n2;
|
||||||
return;
|
}
|
||||||
|
else // real intersection -> calculate boundary intersection points with cramers rule
|
||||||
|
{
|
||||||
|
float det = s ^ t;
|
||||||
|
Vector d = n1 - n2;
|
||||||
|
Vector b = s - d; // s = q - p
|
||||||
|
Vector c = s + d;
|
||||||
|
float lambda = (b ^ t) / det;
|
||||||
|
float mu = (c ^ t) / det;
|
||||||
|
|
||||||
|
// ordering for GL_TRIANGLE_STRIP
|
||||||
|
vertices[pos] = p + s*mu - n1; // u1
|
||||||
|
vertices[pos+1] = p + s*lambda + n1; // u2
|
||||||
}
|
}
|
||||||
|
|
||||||
// real intersection -> calculate boundary intersection points
|
if (overdraw_top && overdraw_bottom)
|
||||||
float det = s ^ t;
|
{
|
||||||
Vector d = d1 - d2;
|
overdraw_top[pos] = vertices[pos];
|
||||||
Vector b = s - d; // s = q - p
|
overdraw_top[pos+1] = vertices[pos] * halo + q * (1. - halo);
|
||||||
Vector c = s + d;
|
|
||||||
float lambda = (b ^ t) / det;
|
|
||||||
float mu = (c ^ t) / det;
|
|
||||||
|
|
||||||
// ordering for GL_TRIANGLE_STRIP
|
overdraw_bottom[pos] = vertices[pos+1];
|
||||||
vertices[pos] = p - d1 + s * mu;
|
overdraw_bottom[pos+1] = vertices[pos+1] * halo + q * (1. - halo);
|
||||||
vertices[pos+1] = p + d1 + s * lambda;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::polyline(const float* coords, size_t count, bool looping)
|
void Graphics::polyline(const float* coords, size_t count)
|
||||||
{
|
{
|
||||||
Vector *vertices = new Vector[count]; // two vertices for every line end-point
|
Vector *vertices = new Vector[count]; // two vertices for every line end-point
|
||||||
Vector p,q,r;
|
Vector *overdraw_top = NULL;
|
||||||
|
Vector *overdraw_bottom = NULL;
|
||||||
|
|
||||||
|
Vector p,q,r;
|
||||||
|
bool looping = (coords[0] == coords[count-2]) && (coords[1] == coords[count-1]);
|
||||||
|
|
||||||
|
if (lineStyle == LINE_SMOOTH)
|
||||||
|
{
|
||||||
|
overdraw_top = new Vector[count];
|
||||||
|
overdraw_bottom = new Vector[count];
|
||||||
|
}
|
||||||
|
|
||||||
|
// get line vertex boundaries
|
||||||
|
// if not looping, extend the line at the beginning, else use last point as `p'
|
||||||
|
float halfwidth = lineWidth/2.0f;
|
||||||
|
float halo = 1. + 1./halfwidth; // XXX: customizable?
|
||||||
r = Vector(coords[0], coords[1]);
|
r = Vector(coords[0], coords[1]);
|
||||||
if (looping) q = Vector(coords[count-4], coords[count-3]);
|
if (!looping)
|
||||||
else q = r * 2 - Vector(coords[2], coords[3]);
|
q = r * 2 - Vector(coords[2], coords[3]);
|
||||||
|
else
|
||||||
|
q = Vector(coords[count-4], coords[count-3]);
|
||||||
|
|
||||||
for (size_t i = 0; i+3 < count; i += 2)
|
for (size_t i = 0; i+3 < count; i += 2)
|
||||||
{
|
{
|
||||||
p = q;
|
p = q; q = r;
|
||||||
q = r;
|
|
||||||
r = Vector(coords[i+2], coords[i+3]);
|
r = Vector(coords[i+2], coords[i+3]);
|
||||||
pushIntersectionPoints(vertices, i, lineWidth/2, p,q,r);
|
pushIntersectionPoints(vertices, i, halfwidth, p,q,r, overdraw_top, overdraw_bottom, halo);
|
||||||
}
|
}
|
||||||
|
|
||||||
p = q;
|
// if not looping, extend the line at the end, else use first point as `r'
|
||||||
q = r;
|
p = q; q = r;
|
||||||
if (looping) r = Vector(coords[2], coords[3]);
|
if (!looping)
|
||||||
else r += (q-p);
|
r += q - p;
|
||||||
pushIntersectionPoints(vertices, count-2, lineWidth/2, p,q,r);
|
else
|
||||||
|
r = Vector(coords[2], coords[3]);
|
||||||
|
pushIntersectionPoints(vertices, count-2, halfwidth, p,q,r, overdraw_top, overdraw_bottom, halo);
|
||||||
|
// end get line vertex boundaries
|
||||||
|
|
||||||
|
// draw the core line
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
glEnableClientState(GL_VERTEX_ARRAY);
|
glEnableClientState(GL_VERTEX_ARRAY);
|
||||||
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)vertices);
|
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)vertices);
|
||||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
|
||||||
|
|
||||||
|
// draw the line halo (antialiasing)
|
||||||
|
if (lineStyle == LINE_SMOOTH)
|
||||||
|
{
|
||||||
|
// prepare colors:
|
||||||
|
// even indices in overdraw* point to inner vertices => alpha = current-alpha,
|
||||||
|
// odd indices point to outer vertices => alpha = 0.
|
||||||
|
Colorf tmp;
|
||||||
|
glGetFloatv(GL_CURRENT_COLOR, (GLfloat*)(&tmp));
|
||||||
|
Color color(tmp.r * 255.0f, tmp.g * 255.0f, tmp.b * 255.0f, tmp.a * 255.0f);
|
||||||
|
|
||||||
|
Color *colors = new Color[count];
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
colors[i] = color;
|
||||||
|
colors[i].a *= int(i%2 == 0); // avoids branching. equiv to colors[i].a *= (i%2==0) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: overdraw at line start and end
|
||||||
|
|
||||||
|
// draw faded out line halos
|
||||||
|
glEnableClientState(GL_COLOR_ARRAY);
|
||||||
|
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
|
||||||
|
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)overdraw_top);
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
|
||||||
|
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)overdraw_bottom);
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
|
||||||
|
glDisableClientState(GL_COLOR_ARRAY);
|
||||||
|
|
||||||
|
delete[] colors;
|
||||||
|
}
|
||||||
|
|
||||||
glDisableClientState(GL_VERTEX_ARRAY);
|
glDisableClientState(GL_VERTEX_ARRAY);
|
||||||
glEnable(GL_TEXTURE_2D);
|
glEnable(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
// cleanup
|
||||||
delete[] vertices;
|
delete[] vertices;
|
||||||
|
if (lineStyle == LINE_SMOOTH)
|
||||||
|
{
|
||||||
|
delete[] overdraw_top;
|
||||||
|
delete[] overdraw_bottom;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::triangle(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3 )
|
void Graphics::triangle(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3 )
|
||||||
@@ -864,7 +943,9 @@ namespace opengl
|
|||||||
|
|
||||||
// GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
|
// GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
|
||||||
if (mode == DRAW_LINE)
|
if (mode == DRAW_LINE)
|
||||||
|
{
|
||||||
polyline(coords, num_coords); // Artifacts at sharp angles if set to looping.
|
polyline(coords, num_coords); // Artifacts at sharp angles if set to looping.
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
glDisable(GL_TEXTURE_2D);
|
glDisable(GL_TEXTURE_2D);
|
||||||
@@ -887,7 +968,7 @@ namespace opengl
|
|||||||
// coords[count-2] = coords[0], coords[count-1] = coords[1]
|
// coords[count-2] = coords[0], coords[count-1] = coords[1]
|
||||||
if (mode == DRAW_LINE)
|
if (mode == DRAW_LINE)
|
||||||
{
|
{
|
||||||
polyline(coords, count, true);
|
polyline(coords, count);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ namespace opengl
|
|||||||
Image::Filter currentImageFilter;
|
Image::Filter currentImageFilter;
|
||||||
love::window::Window *currentWindow;
|
love::window::Window *currentWindow;
|
||||||
|
|
||||||
|
LineStyle lineStyle;
|
||||||
float lineWidth;
|
float lineWidth;
|
||||||
GLint matrixLimit;
|
GLint matrixLimit;
|
||||||
GLint userMatrices;
|
GLint userMatrices;
|
||||||
@@ -440,11 +441,10 @@ namespace opengl
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a series of lines connecting the given vertices.
|
* Draws a series of lines connecting the given vertices.
|
||||||
* @param coords Vertex components (x1, y1, x2, y2, etc.)
|
* @param coords Vertex components (x1, y1, ..., xn, yn). If x1,y1 == xn,yn the line will be drawn closed.
|
||||||
* @param count Number of coordinates in the array.
|
* @param count Number of items in the array, i.e. count = 2 * n
|
||||||
* @param looping Whether the line joins up with itself.
|
|
||||||
**/
|
**/
|
||||||
void polyline(const float* coords, size_t count, bool looping = false);
|
void polyline(const float* coords, size_t count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Draws a triangle using the three coordinates passed.
|
* Draws a triangle using the three coordinates passed.
|
||||||
|
|||||||
@@ -78,10 +78,8 @@ namespace opengl
|
|||||||
int w_setLineWidth(lua_State * L);
|
int w_setLineWidth(lua_State * L);
|
||||||
int w_setLineStyle(lua_State * L);
|
int w_setLineStyle(lua_State * L);
|
||||||
int w_setLine(lua_State * L);
|
int w_setLine(lua_State * L);
|
||||||
int w_setLineStipple(lua_State * L);
|
|
||||||
int w_getLineWidth(lua_State * L);
|
int w_getLineWidth(lua_State * L);
|
||||||
int w_getLineStyle(lua_State * L);
|
int w_getLineStyle(lua_State * L);
|
||||||
int w_getLineStipple(lua_State * L);
|
|
||||||
int w_setPointSize(lua_State * L);
|
int w_setPointSize(lua_State * L);
|
||||||
int w_setPointStyle(lua_State * L);
|
int w_setPointStyle(lua_State * L);
|
||||||
int w_setPoint(lua_State * L);
|
int w_setPoint(lua_State * L);
|
||||||
|
|||||||
Reference in New Issue
Block a user