Replace line drawing via GL_LINE with custom line drawing (issue #153)

Allows for correctly scaleable lines, (very) big line widths. Corners in
rectangles/triangles/... join up nicely (issue #197).

--HG--
branch : minor
This commit is contained in:
vrld
2011-03-22 01:08:31 +01:00
parent 37560e36c3
commit 2a42c64a35
3 changed files with 209 additions and 370 deletions
+135 -304
View File
@@ -20,6 +20,7 @@
#include <common/config.h>
#include <common/math.h>
#include <common/Vector.h>
#include "Graphics.h"
@@ -36,7 +37,7 @@ namespace opengl
{
Graphics::Graphics()
: currentFont(0)
: currentFont(0), lineWidth(1)
{
// Indicates that there is no screen
// created yet.
@@ -100,19 +101,8 @@ namespace opengl
//get color mode
glGetTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, &mode);
s.colorMode = (mode == GL_MODULATE) ? Graphics::COLOR_MODULATE : Graphics::COLOR_REPLACE;
//get the line width (directly to corresponding variable)
glGetFloatv(GL_LINE_WIDTH, &s.lineWidth);
//get line style
s.lineStyle = (glIsEnabled(GL_LINE_SMOOTH) == GL_TRUE) ? Graphics::LINE_SMOOTH : Graphics::LINE_ROUGH;
//get line stipple
s.stipple = (glIsEnabled(GL_LINE_STIPPLE) == GL_TRUE);
if (s.stipple)
{
//get the stipple repeat
glGetIntegerv(GL_LINE_STIPPLE_REPEAT, &s.stippleRepeat);
//get the stipple pattern
glGetIntegerv(GL_LINE_STIPPLE_PATTERN, &s.stipplePattern);
}
s.lineStyle = (glIsEnabled(GL_POLYGON_SMOOTH) == GL_TRUE) ? Graphics::LINE_SMOOTH : Graphics::LINE_ROUGH;
//get the point size
glGetFloatv(GL_POINT_SIZE, &s.pointSize);
//get point style
@@ -136,11 +126,7 @@ namespace opengl
setBackgroundColor(s.backgroundColor);
setBlendMode(s.blendMode);
setColorMode(s.colorMode);
setLine(s.lineWidth, s.lineStyle);
if (s.stipple)
setLineStipple(s.stipplePattern, s.stippleRepeat);
else
setLineStipple();
setLine(lineWidth, s.lineStyle);
setPoint(s.pointSize, s.pointStyle);
if (s.scissor)
setScissor(s.scissorBox[0], s.scissorBox[1], s.scissorBox[2], s.scissorBox[3]);
@@ -260,8 +246,7 @@ namespace opengl
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Enable line/point smoothing.
glEnable(GL_LINE_SMOOTH);
glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
setLineStyle(LINE_SMOOTH);
glEnable(GL_POINT_SMOOTH);
glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
@@ -608,45 +593,30 @@ namespace opengl
void Graphics::setLineWidth( float width )
{
glLineWidth(width);
lineWidth = width;
}
void Graphics::setLineStyle(Graphics::LineStyle style )
{
// XXX: actually enables antialiasing for _all_ polygons.
// may need investigation if wanted or not
// maybe rename to something else?
if(style == LINE_ROUGH)
glDisable (GL_LINE_SMOOTH);
glDisable (GL_POLYGON_SMOOTH);
else // type == LINE_SMOOTH
{
glEnable (GL_LINE_SMOOTH);
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
glEnable (GL_POLYGON_SMOOTH);
glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
}
}
void Graphics::setLine( float width, Graphics::LineStyle style )
{
glLineWidth(width);
setLineWidth(width);
if(style == 0)
return;
if(style == LINE_ROUGH)
glDisable (GL_LINE_SMOOTH);
else // type == LINE_SMOOTH
{
glEnable (GL_LINE_SMOOTH);
glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
}
}
void Graphics::setLineStipple()
{
glDisable(GL_LINE_STIPPLE);
}
void Graphics::setLineStipple(unsigned short pattern, int repeat)
{
glEnable(GL_LINE_STIPPLE);
glLineStipple((GLint)repeat, (GLshort)pattern);
setLineStyle(style);
}
float Graphics::getLineWidth()
@@ -658,25 +628,12 @@ namespace opengl
Graphics::LineStyle Graphics::getLineStyle()
{
if(glIsEnabled(GL_LINE_SMOOTH) == GL_TRUE)
if(glIsEnabled(GL_POLYGON_SMOOTH) == GL_TRUE)
return LINE_SMOOTH;
else
return LINE_ROUGH;
}
int Graphics::getLineStipple(lua_State * L)
{
if(glIsEnabled(GL_LINE_STIPPLE) == GL_FALSE)
return 0;
GLint factor, pattern;
glGetIntegerv(GL_LINE_STIPPLE_PATTERN, &pattern);
glGetIntegerv(GL_LINE_STIPPLE_REPEAT, &factor);
lua_pushinteger(L, pattern);
lua_pushinteger(L, factor);
return 2;
}
void Graphics::setPointSize( float size )
{
glPointSize((GLfloat)size);
@@ -805,149 +762,90 @@ namespace opengl
glEnable(GL_TEXTURE_2D);
}
void Graphics::line( float x1, float y1, float x2, float y2 )
// calculate line boundary intersection vertices for current line
// dependent on the current *and next* line segment
static void pushIntersectionPoints(Vector *vertices, int pos, float halfwidth, const Vector& p, const Vector& q, const Vector& r)
{
glDisable(GL_TEXTURE_2D);
glPushMatrix();
glBegin(GL_LINES);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
glPopMatrix();
glEnable(GL_TEXTURE_2D);
// calculate line directions
Vector s = (q - p);
Vector t = (r - q);
// calculate vertex displacement vectors
Vector d1 = s.getNormal();
Vector d2 = t.getNormal();
d1.normalize();
d2.normalize();
float det_norm = d1 ^ d2;
d1 *= halfwidth;
d2 *= halfwidth;
// lines parallel -> assume intersection at displacement points
if (fabs(det_norm) <= .03) {
vertices[pos] = q - d2;
vertices[pos + 1] = q + d2;
return;
}
// real intersection -> calculate boundary intersection points
float det = s ^ t;
Vector d = d1 - d2;
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 - d1 + s * mu;
vertices[pos+1] = p + d1 + s * lambda;
}
int Graphics::polyline( lua_State * L)
void Graphics::polyline(const float* coords, size_t count, bool looping)
{
// Get number of params.
int args = lua_gettop(L);
bool table = false;
Vector *vertices = new Vector[count]; // two vertices for every line end-point
Vector p,q,r;
if (args == 1) { // we've got a table, hopefully
int type = lua_type(L, 1);
if (type != LUA_TTABLE)
return luaL_error(L, "Function requires a table or series of numbers");
table = true;
args = lua_objlen(L, 1);
r = Vector(coords[0], coords[1]);
if (looping) q = Vector(coords[count-4], coords[count-3]);
else q = r * 2 - Vector(coords[2], coords[3]);
for (size_t i = 0; i+3 < count; i += 2) {
p = q;
q = r;
r = Vector(coords[i+2], coords[i+3]);
pushIntersectionPoints(vertices, i, lineWidth/2, p,q,r);
}
if (args % 2) // an odd number of arguments, no good for a polyline
return luaL_error(L, "Number of vertices must be a multiple of two");
else if (args < 4)
return luaL_error(L, "Need at least two vertices to draw a line");
p = q;
q = r;
if (looping) r = Vector(coords[2], coords[3]);
else r += (q-p);
pushIntersectionPoints(vertices, count-2, lineWidth/2, p,q,r);
// right, let's draw this polyline, then
glDisable(GL_TEXTURE_2D);
glBegin(GL_LINE_STRIP);
if (table) {
for (int i = 1; i < args; i += 2) {
lua_pushnumber(L, i); // x coordinate
lua_rawget(L, 1);
lua_pushnumber(L, i+1); // y coordinate
lua_rawget(L, 1);
glVertex2f((GLfloat)lua_tonumber(L, -2), (GLfloat)lua_tonumber(L, -1));
lua_pop(L, 2);
}
} else {
for (int i = 1; i < args; i+=2) {
glVertex2f((GLfloat)lua_tonumber(L, i), (GLfloat)lua_tonumber(L, i+1));
}
}
glEnd();
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)vertices);
glDrawArrays(GL_TRIANGLE_STRIP, 0, count);
glDisableClientState(GL_VERTEX_ARRAY);
glEnable(GL_TEXTURE_2D);
return 0;
delete[] vertices;
}
void Graphics::triangle(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3 )
{
glDisable(GL_TEXTURE_2D);
glPushMatrix();
switch(mode)
{
case DRAW_LINE:
glBegin(GL_LINE_LOOP);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glVertex2f(x3, y3);
glEnd();
break;
default:
case DRAW_FILL:
glBegin(GL_TRIANGLES);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glVertex2f(x3, y3);
glEnd();
break;
}
glPopMatrix();
glEnable(GL_TEXTURE_2D);
float coords[] = { x1,y1, x2,y2, x3,y3, x1,y1 };
polygon(mode, coords, 4 * 2);
}
void Graphics::rectangle(DrawMode mode, float x, float y, float w, float h)
{
glDisable(GL_TEXTURE_2D);
glPushMatrix();
switch(mode)
{
case DRAW_LINE:
// offsets here because OpenGL is being a bitch about line drawings
glBegin(GL_LINE_LOOP);
glVertex2f(x, y);
glVertex2f(x, y+h-1);
glVertex2f(x+w-1, y+h-1);
glVertex2f(x+w-1, y);
glEnd();
break;
default:
case DRAW_FILL:
glBegin(GL_QUADS);
glVertex2f(x, y);
glVertex2f(x, y+h);
glVertex2f(x+w, y+h);
glVertex2f(x+w, y);
glEnd();
break;
}
glPopMatrix();
glEnable(GL_TEXTURE_2D);
quad(mode, x,y, x,y+h, x+w,y+h, x+w,y);
}
void Graphics::quad(DrawMode mode, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4 )
{
glDisable(GL_TEXTURE_2D);
glPushMatrix();
switch(mode)
{
case DRAW_LINE:
glBegin(GL_LINE_LOOP);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glVertex2f(x3, y3);
glVertex2f(x4, y4);
glEnd();
break;
default:
case DRAW_FILL:
glBegin(GL_QUADS);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glVertex2f(x3, y3);
glVertex2f(x4, y4);
glEnd();
break;
}
glPopMatrix();
glEnable(GL_TEXTURE_2D);
float coords[] = { x1,y1, x2,y2, x3,y3, x4,y4, x1,y1 };
polygon(mode, coords, 5 * 2);
}
void Graphics::circle(DrawMode mode, float x, float y, float radius, int points)
@@ -955,144 +853,77 @@ namespace opengl
float two_pi = static_cast<float>(LOVE_M_PI * 2);
if(points <= 0) points = 1;
float angle_shift = (two_pi / points);
float phi = .0f;
glDisable(GL_TEXTURE_2D);
glPushMatrix();
glTranslatef(x, y, 0.0f);
switch(mode)
{
case DRAW_LINE:
glBegin(GL_LINE_LOOP);
for(float i = 0; i < two_pi; i+= angle_shift)
glVertex2f(radius * sin(i),radius * cos(i));
glEnd();
break;
default:
case DRAW_FILL:
glBegin(GL_TRIANGLE_FAN);
for(float i = 0; i < two_pi; i+= angle_shift)
glVertex2f(radius * sin(i),radius * cos(i));
glEnd();
break;
float *coords = new float[2 * (points + 1)];
for (int i = 0; i < points; ++i, phi += angle_shift) {
coords[2*i] = x + radius * cos(phi);
coords[2*i+1] = y + radius * sin(phi);
}
glPopMatrix();
glEnable(GL_TEXTURE_2D);
coords[2*points] = coords[0];
coords[2*points+1] = coords[1];
polygon(mode, coords, (points + 1) * 2);
delete[] coords;
}
void Graphics::arc(DrawMode mode, float x, float y, float radius, float angle1, float angle2, int points)
{
float angle = angle2 - angle1;
if (angle == 0) return;
while (angle < 0) angle += LOVE_M_PI * 2;
angle1 = fmod(angle1, 2 * LOVE_M_PI);
angle2 = fmod(angle2, 2 * LOVE_M_PI);
if (angle1 == angle2)
return;
else if (angle1 > angle2)
angle2 += LOVE_M_PI * 2;
if(points <= 0) points = 1;
float angle_shift = (angle / points);
glDisable(GL_TEXTURE_2D);
glPushMatrix();
glTranslatef(x, y, 0.0f);
switch(mode)
{
case DRAW_LINE:
glBegin(GL_LINE_LOOP);
glVertex2f(0, 0);
for(float i = 0; i <= angle+angle_shift/2; i+= angle_shift)
glVertex2f(radius * cos(i+angle1),radius * sin(i+angle1));
glEnd();
break;
default:
case DRAW_FILL:
glBegin(GL_TRIANGLE_FAN);
glVertex2f(0, 0);
for(float i = 0; i <= angle+angle_shift/2; i+= angle_shift)
glVertex2f(radius * cos(i+angle1),radius * sin(i+angle1));
glEnd();
break;
float angle_shift = ((angle2 - angle1) / points);
float phi = angle1;
// GL_POLYGON can only fill-draw convex polygons, so we need to do stuff manually here
if (mode == DRAW_LINE) {
float *coords = new float[(points + 3) * 2];
coords[0] = coords[2 * points + 4] = x;
coords[1] = coords[2 * points + 5] = y;
for (int i = 0; i <= points; ++i, phi += angle_shift) {
coords[2 * (i+1)] = x + radius * cos(phi);
coords[2 * (i+1) + 1] = y - radius * sin(phi);
}
polyline(coords, (points + 3) * 2); // artifacts at sharp angles if set to looping
delete[] coords;
} else {
glDisable(GL_TEXTURE_2D);
glBegin(GL_TRIANGLE_FAN);
glVertex2f(x, y);
for (int i = 0; i <= points; ++i, phi += angle_shift)
glVertex2f(x + radius * cos(phi), y - radius * sin(phi));
glEnd();
glEnable(GL_TEXTURE_2D);
}
glPopMatrix();
glEnable(GL_TEXTURE_2D);
}
int Graphics::polygon( lua_State * L )
/// @param mode the draw mode
/// @param coords the coordinate array
/// @param count the number of coordinates/size of the array
void Graphics::polygon(DrawMode mode, const float* coords, size_t count)
{
// Get number of params.
int n = lua_gettop(L);
// Need at least two params.
if( n < 2 )
return luaL_error(L, "Error: function needs at least two parameters.");
DrawMode mode;
const char * str = luaL_checkstring(L, 1);
if(!getConstant(str, mode))
return luaL_error(L, "Invalid draw mode: %s", str);
// Get the type of the second argument.
int luatype = lua_type(L, 2);
// Perform additional type checking.
switch(luatype)
{
case LUA_TNUMBER:
if( n-1 < 6 ) return luaL_error(L, "Error: function requires at least 3 vertices.");
if( ((n-1)%2) != 0 ) return luaL_error(L, "Error: number of vertices must be a multiple of two.");
break;
case LUA_TTABLE:
if( (lua_objlen(L, 2)%2) != 0 ) return luaL_error(L, "Error: number of vertices must be a multiple of two.");
break;
default:
return luaL_error(L, "Error: number type or table expected.");
// coords is an array of a closed loop of vertices, i.e.
// coords[count-2] = coords[0], coords[count-1] = coords[1]
if (mode == DRAW_LINE) {
polyline(coords, count, true);
} else {
glDisable(GL_TEXTURE_2D);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_FLOAT, 0, (const GLvoid*)coords);
glDrawArrays(GL_POLYGON, 0, count / 2); // opengl will close the polygon for us
glDisableClientState(GL_VERTEX_ARRAY);
glEnable(GL_TEXTURE_2D);
}
glDisable(GL_TEXTURE_2D);
glBegin((mode==DRAW_LINE) ? GL_LINE_LOOP : GL_POLYGON);
switch(luatype)
{
case LUA_TNUMBER:
for(int i = 2; i<n; i+=2)
glVertex2f((GLfloat)lua_tonumber(L, i), (GLfloat)lua_tonumber(L, i+1));
break;
case LUA_TTABLE:
lua_pushnil(L);
while (true)
{
if(lua_next(L, 2) == 0) break;
GLfloat x = (GLfloat)lua_tonumber(L, -1);
lua_pop(L, 1); // pop value
if(lua_next(L, 2) == 0) break;
GLfloat y = (GLfloat)lua_tonumber(L, -1);
lua_pop(L, 1); // pop value
glVertex2f(x, y);
}
break;
}
glEnd();
glEnable(GL_TEXTURE_2D);
return 0;
}
love::image::ImageData * Graphics::newScreenshot(love::image::Image * image)
+9 -36
View File
@@ -76,11 +76,7 @@ namespace opengl
Graphics::ColorMode colorMode;
// Line.
float lineWidth;
Graphics::LineStyle lineStyle;
bool stipple;
GLint stippleRepeat;
GLint stipplePattern;
// Point.
float pointSize;
@@ -107,9 +103,7 @@ namespace opengl
backgroundColor.a = 255;
blendMode = Graphics::BLEND_ALPHA;
colorMode = Graphics::COLOR_MODULATE;
lineWidth = 1.0f;
lineStyle = Graphics::LINE_SMOOTH;
stipple = false;
pointSize = 1.0f;
pointStyle = Graphics::POINT_SMOOTH;
scissor = false;
@@ -126,6 +120,8 @@ namespace opengl
Font * currentFont;
DisplayMode currentMode;
float lineWidth;
public:
Graphics();
@@ -335,16 +331,6 @@ namespace opengl
**/
void setLine(float width, LineStyle style);
/**
* Disables line stippling.
**/
void setLineStipple();
/**
* Sets a line stipple pattern.
**/
void setLineStipple(unsigned short pattern, int repeat = 1);
/**
* Gets the line width.
**/
@@ -355,13 +341,6 @@ namespace opengl
**/
LineStyle getLineStyle();
/**
* Gets the line stipple pattern and repeat factor.
* @return pattern The stipplie bit-pattern.
* @return repeat The reapeat factor.
**/
int getLineStipple(lua_State * L);
/**
* Sets the size of points.
**/
@@ -423,20 +402,13 @@ namespace opengl
**/
void point(float x, float y);
/**
* Draws a line from (x1,y1) to (x2,y2).
* @param x1 First x-coordinate.
* @param y1 First y-coordinate.
* @param x2 Second x-coordinate.
* @param y2 Second y-coordinate.
**/
void line(float x1, float y1, float x2, float y2);
/**
* Draws a series of lines connecting the given vertices.
* @param ... Vertex components (x1, y1, x2, y2, etc.)
* @param coords Vertex components (x1, y1, x2, y2, etc.)
* @param count Coord array size
* @param looping Wether the line is joining itself
**/
int polyline(lua_State * L);
void polyline(const float* coords, size_t count, bool looping = false);
/**
* Draws a triangle using the three coordinates passed.
@@ -488,9 +460,10 @@ namespace opengl
/**
* Draws a polygon with an arbitrary number of vertices.
* @param type The type of drawing (line/filled).
* @param ... Vertex components (x1, y1, x2, y2, etc).
* @param coords Vertex components (x1, y1, x2, y2, etc.)
* @param count Coord array size
**/
int polygon(lua_State * L);
void polygon(DrawMode mode, const float* coords, size_t count);
/**
* Creates a screenshot of the view and saves it to the default folder.
+65 -30
View File
@@ -556,20 +556,6 @@ namespace opengl
return 0;
}
int w_setLineStipple(lua_State * L)
{
if(lua_gettop(L) == 0)
{
instance->setLineStipple();
return 0;
}
unsigned short pattern = (unsigned short)luaL_checkint(L, 1);
int repeat = luaL_optint(L, 2, 1);
instance->setLineStipple(pattern, repeat);
return 0;
}
int w_getLineWidth(lua_State * L)
{
lua_pushnumber(L, instance->getLineWidth());
@@ -585,11 +571,6 @@ namespace opengl
return 1;
}
int w_getLineStipple(lua_State * L)
{
return instance->getLineStipple(L);
}
int w_setPointSize(lua_State * L)
{
float size = (float)luaL_checknumber(L, 1);
@@ -776,15 +757,32 @@ namespace opengl
int w_line(lua_State * L)
{
int args = lua_gettop(L);
if( args == 1 || args > 4) {
instance->polyline(L);
} else {
float x1 = (float)luaL_checknumber(L, 1);
float y1 = (float)luaL_checknumber(L, 2);
float x2 = (float)luaL_checknumber(L, 3);
float y2 = (float)luaL_checknumber(L, 4);
instance->line(x1, y1, x2, y2);
bool is_table = false;
if (args == 1 && lua_istable(L, 1)) {
args = lua_objlen(L, 1);
is_table = true;
}
if (args % 2 != 0)
return luaL_error(L, "Number of vertices must be a multiple of two");
else if (args < 4)
return luaL_error(L, "Need at least two vertices to draw a line");
float* coords = new float[args];
if (is_table) {
for (int i = 0; i < args; ++i) {
lua_pushnumber(L, i + 1);
lua_rawget(L, 1);
coords[i] = lua_tonumber(L, -1);
lua_pop(L, 1);
}
} else {
for (int i = 0; i < args; ++i)
coords[i] = lua_tonumber(L, i + 1);
}
instance->polyline(coords, args);
delete[] coords;
return 0;
}
@@ -873,7 +871,46 @@ namespace opengl
int w_polygon(lua_State * L)
{
return instance->polygon(L);
int args = lua_gettop(L) - 1;
Graphics::DrawMode mode;
const char * str = luaL_checkstring(L, 1);
if(!Graphics::getConstant(str, mode))
return luaL_error(L, "Invalid draw mode: %s", str);
bool is_table = false;
float* coords;
if (args == 1 && lua_istable(L, 2)) {
args = lua_objlen(L, 2);
is_table = true;
}
if (args % 2 != 0)
return luaL_error(L, "Number of vertices must be a multiple of two");
else if (args < 6)
return luaL_error(L, "Need at least three vertices to draw a polygon");
// fetch coords
coords = new float[args + 2];
if (is_table) {
for (int i = 0; i < args; ++i) {
lua_pushnumber(L, i + 1);
lua_rawget(L, 2);
coords[i] = lua_tonumber(L, -1);
lua_pop(L, 1);
}
} else {
for (int i = 0; i < args; ++i)
coords[i] = lua_tonumber(L, i + 1);
}
// make a closed loop
coords[args] = coords[0];
coords[args+1] = coords[1];
instance->polygon(mode, coords, args+2);
delete[] coords;
return 0;
}
int w_push(lua_State *)
@@ -950,10 +987,8 @@ namespace opengl
{ "setLineWidth", w_setLineWidth },
{ "setLineStyle", w_setLineStyle },
{ "setLine", w_setLine },
{ "setLineStipple", w_setLineStipple },
{ "getLineWidth", w_getLineWidth },
{ "getLineStyle", w_getLineStyle },
{ "getLineStipple", w_getLineStipple },
{ "setPointSize", w_setPointSize },
{ "setPointStyle", w_setPointStyle },
{ "setPoint", w_setPoint },