Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » How to draw a smoother/more accurate circle?

Credits go to SiegeLord for helping out!
This thread is locked; no one can reply to it. rss feed Print
How to draw a smoother/more accurate circle?
patrickpowns
Member #14,325
May 2012

Using Allegro 5.1 build.

So I'm making a spaceflight simulator, and to draw a planet I do something like

al_draw_filled_circle(earth.x, earth.y, 6371000, earth.color)

obviously, I'm sure the devs have only tested primitive drawing for reasonably sized primitives, but when I draw this large of a circle, I get some fairly noticeable straight lines. Take a gander at some visuals if you want. I've enabled multisampling code in those pictures, but that doesn't cut it.

al_set_new_display_option(ALLEGRO_SAMPLE_BUFFERS, 1, ALLEGRO_SUGGEST);
al_set_new_display_option(ALLEGRO_SAMPLES, 8, ALLEGRO_SUGGEST);

uu: DRAWING CIRCLES SuRE IS HARD tumut

Is there some way I can make the circle draw with more lines (I guess you call them splines) with existing Allegro functionality? Or is this something that I should hack a new function or something for?

SiegeLord
Member #7,827
October 2006
avatar

There are two compile time constants which govern the quality of these things. These are ALLEGRO_VERTEX_CACHE_SIZE and ALLEGRO_PRIM_QUALITY. You could modify them and rebuild Allegro... perhaps a runtime quantity would be more pleasant?

Alternatively, you can just go into Allegro's source, yank out the circle drawing code and up the number of segments drawn. Here it is:

#SelectExpand
1#define LOCAL_VERTEX_CACHE ALLEGRO_VERTEX vertex_cache[ALLEGRO_VERTEX_CACHE_SIZE] 2 3/* Function: al_draw_filled_ellipse 4 */ 5void al_draw_filled_ellipse(float cx, float cy, float rx, float ry, 6 ALLEGRO_COLOR color) 7{ 8 LOCAL_VERTEX_CACHE; 9 int num_segments, ii; 10 float scale = get_scale(); 11 12 ASSERT(rx >= 0); 13 ASSERT(ry >= 0); 14 15 num_segments = ALLEGRO_PRIM_QUALITY * scale * sqrtf((rx + ry) / 2.0f); 16 17 /* In case rx and ry are both close to 0. If al_calculate_arc is passed 18 * 0 or 1 it will assert. 19 */ 20 if (num_segments < 2) 21 return; 22 23 if (num_segments >= ALLEGRO_VERTEX_CACHE_SIZE) { 24 num_segments = ALLEGRO_VERTEX_CACHE_SIZE - 1; 25 } 26 27 al_calculate_arc(&(vertex_cache[1].x), sizeof(ALLEGRO_VERTEX), cx, cy, rx, ry, 0, ALLEGRO_PI * 2, 0, num_segments); 28 vertex_cache[0].x = cx; vertex_cache[0].y = cy; 29 30 for (ii = 0; ii < num_segments + 1; ii++) { 31 vertex_cache[ii].color = color; 32 vertex_cache[ii].z = 0; 33 } 34 35 al_draw_prim(vertex_cache, 0, 0, 0, num_segments + 1, ALLEGRO_PRIM_TRIANGLE_FAN); 36}

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

patrickpowns
Member #14,325
May 2012

By 'compile time constants' do you mean something that I can go into /usr/local/include/allegro5/allegro_primitives.h and change the #define, and then recompile my program, or is it a recompile-A5-again kind of compile time? Because I changed ALLEGRO_PRIM_QUALITY in that directory, as well as redefined it in my main.cpp, and it gave the same num_segments.

This is my first time doing any of this, as I'm sure you can tell already.

SiegeLord
Member #7,827
October 2006
avatar

is it a recompile-A5-again kind of compile time?

This one.

Note that if you copy the function into your own code you just need to recompile your code (and use this function instead of Allegro's).

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

patrickpowns
Member #14,325
May 2012

Alright, so after having tried messing around with those values, then sleeping, then messing around some more, I've come to a conclusion:

Drawing the Earth at a close zoom requires 7055 segments. Obviously, this gets limited by some code in draw_filled_ellipse to ALLEGRO_VERTEX_CACHE_SIZE - 1. Upping PRIM_QUALITY won't do anything.

Upping VERTEX_CACHE_SIZE allows more segments, but also makes a 'cliff'. This cliff alternates between being too low and too high the more I zoom out (which draws less segments), and there's also a seam there at some zoom levels. As far as I can tell, this only happens at that specific location on the circle (0 / 2pi radians).

Lowering CACHE_SIZE below 256 just makes it look blockier, obviously.

As far as I can tell, an ALLEGRO_VERTEX_CACHE_SIZE of 256 is the optimal value. Is there any way to get more segments, while not having a cliff/seam at that specific point?

Also, I don't think having a runtime quantity would really be necessary, because it's only going to be changed by one or two people every so often, and time would better be spent on fixing up shaders and pipelines or whatever black magic you're working on right now. Allegro only takes ~10 seconds to build on my quad-core, not that big of a deal.

beoran
Member #12,636
March 2011

Pehaps you could cheat and draw a rectangle if the planet is close enough to be completely in the screen. From close up, a flat earth is not a bad approximation...

SiegeLord
Member #7,827
October 2006
avatar

beoran said:

Pehaps you could cheat and draw a rectangle if the planet is close enough to be completely in the screen. From close up, a flat earth is not a bad approximation...

Along the same lines you could draw filled pie-slices instead of full circles. You'll end up using more segments more efficiently.

Also, since you're using 5.1 perhaps you could just use a shader to draw your circles. It's a very simple shader (here's the GLSL code, untested. HLSL code is obviously very similar) (note that you'll want to use the git version of 5.1 as there have been A LOT of shader related changes recently):

Vertex shader:

#SelectExpand
1#version 120 2attribute vec4 al_pos; 3attribute vec4 al_color; 4 5uniform mat4 al_projview_matrix; 6 7varying vec2 pixel_pos; 8varying vec4 pixel_color; 9 10void main() 11{ 12 pixel_pos = al_pos.xy; 13 pixel_color = al_color; 14 gl_Position = al_projview_matrix * al_pos; 15}

Pixel shader:

#SelectExpand
1#version 120 2#ifdef GL_ES 3precision mediump float; 4#endif 5 6varying vec2 pixel_pos; 7varying vec4 pixel_color; 8 9uniform vec2 center; 10uniform float radius; 11 12void main() 13{ 14 vec2 diff = pixel_pos - center; 15 if(dot(diff) < radius * radius) 16 gl_FragColor = pixel_color; 17 else 18 gl_FragColor = vec4(0, 0, 0, 0); 19}

You'll probably still run into issues of enormous circles overflowing float though, in which case just drawing a rectangle might be good enough :P.

"For in much wisdom is much grief: and he that increases knowledge increases sorrow."-Ecclesiastes 1:18
[SiegeLord's Abode][Codes]:[DAllegro5]:[RustAllegro]

patrickpowns
Member #14,325
May 2012

So after taking a look at things like ex_prim_shader, I realised that shaders are actually pretty cool, so I've started to learn what shaders are. Hopefully this will elucidate what is going on in the mailing list when everyone is talking about shaders and stuff, which would be worth it by itself. Although I guess if it comes to it, I could just make an arc, spline, ribbon, or I don't know what. Something along those segments lines.

But there's no way I'm going to be able to implement shaders by tonight (21:20 here) so I'm just going to mark this as solved, and then continue my adventure into the exciting world of shaders. If I don't come back, just assume my brain has melted.

Go to: