The Allegro Wiki is migrating to github at https://github.com/liballeg/allegro_wiki/wiki

NewAPI/Primitives/al draw triangle

From Allegro Wiki
Jump to: navigation, search

Planned variations. For now, just outlined and filled. However, once the filled one is done there will be no problem to add Goraud shaded type as well as a simple texture mapped type.

Outlined Triangle

This shall just be the al_draw_line drawn three times.

Filled Triangle

Goals:

  • Match the equivalent OpenGL calls pixel for pixel

Problems with code so far:

  • At fractional coordinates for the corners there are some pixel inaccuracies

I think this code is pretty much done, and now just needs to contain the various per-pixel operators (shading/tex mapping).

A4 test code:

<highlightSyntax language="cpp"> /* Turns out that the OpenGL renders their triangles as follows: shift the floating point coordinates by (-0.5f, +0.5f) And fill everything inside. I believe the lower and right edges are exclusive while the other two are inclusive.

To outline this triangle, pass it's vertices shifted by (0, +0.5f) to the al_draw_line function.

For some odd reason, this particular definition is still not entirely perfect, since if you test this with fractional coordinates, there will be some pixel-discrepancies between this routine and whatever the OpenGL uses... Oh well. I really don't feel like catering to their implementation dependent behaviour.

  • /

void al_fill_triangle6(BITMAP* bmp, float x1, float y1, float x2, float y2, float x3, float y3, int color) { float Coords[6] = {x1 - 0.5f, y1 + 0.5f, x2 - 0.5f, y2 + 0.5f, x3 - 0.5f, y3 + 0.5f}; float *V1 = Coords, *V2 = &Coords[2], *V3 = &Coords[4], *s;

float left_error = 0; float right_error = 0;

float left_y_delta; float right_y_delta;

float left_x_delta; float right_x_delta;

int left_x, right_x, cur_y, mid_y, end_y;

/* The reason these things are declared implicitly, is because we need to determine which of the edges is on the left, and which is on the right (because they are treated differently, as described above) We then can reuse these values in the actual calculation */ float major_x_delta, major_y_delta, minor_x_delta, minor_y_delta; int major_on_the_left;

// sort vertices so that V1 <= V2 <= V3 if(V2[1] < V1[1]) { s = V2; V2 = V1; V1 = s; } if(V3[1] < V1[1]) { s = V3; V3 = V1; V1 = s; } if(V3[1] < V2[1]) { s = V3; V3 = V2; V2 = s; }

/* We set our integer based coordinates to be above their floating point counterparts */ cur_y = ceilf(V1[1]); mid_y = ceilf(V2[1]); end_y = ceilf(V3[1]);

if(cur_y == end_y) return;

/* As per definition, we take the ceiling */ left_x = ceilf(V1[0]);

/* Determine which edge is the left one V1-V2 | / V3 When the cross product is negative, the major is on the left */

major_x_delta = V3[0] - V1[0]; major_y_delta = V3[1] - V1[1]; minor_x_delta = V2[0] - V1[0]; minor_y_delta = V2[1] - V1[1];

if(major_x_delta * minor_y_delta - major_y_delta * minor_x_delta < 0) major_on_the_left = 1; else major_on_the_left = 0;

/* Do the first segment, if it exists */ if(cur_y != mid_y) { /* As per definition, we take the floor */ right_x = floorf(V1[0]);

/* Depending on where V2 is located, choose the correct delta's */ if(major_on_the_left) { left_x_delta = major_x_delta; right_x_delta = minor_x_delta; left_y_delta = major_y_delta; right_y_delta = minor_y_delta; } else { left_x_delta = minor_x_delta; right_x_delta = major_x_delta; left_y_delta = minor_y_delta; right_y_delta = major_y_delta; }

/* Calculate the initial errors... doesn't look too pretty, but it only has to be done a couple of times per triangle drawing operation, so its not that bad */ left_error = ((float)cur_y - V1[1]) * left_x_delta - ((float)left_x - V1[0]) * left_y_delta; right_error = ((float)cur_y - V1[1]) * right_x_delta - ((float)right_x - V1[0]) * right_y_delta;

/* Calculate the first step of the edge steppers, it is potentially different from all other steps */ int left_first = ceilf((left_error) / left_y_delta); int right_first = floorf((right_error) / right_y_delta);

/* Calculate the normal steps */ int left_step = ceilf(left_x_delta / left_y_delta); float left_d_er = -float(left_step) * left_y_delta;

int right_step = ceilf(right_x_delta / right_y_delta); float right_d_er = -float(right_step) * right_y_delta;

/* Take the first step */ if(cur_y < mid_y) { left_x += left_first; left_error -= float(left_first) * left_y_delta;

right_x += right_first; right_error -= float(right_first) * right_y_delta;

if(right_x >= left_x) { hline(bmp,left_x, cur_y, right_x, color); }

cur_y++; left_error += left_x_delta; right_error += right_x_delta; }

/* ...and then continue taking normal steps until we finish the segment */ while(cur_y < mid_y) { left_error += left_d_er; left_x += left_step;

/* If we dip to the right of the line, we shift one pixel to the left If dx > 0, this corresponds to taking the minor step If dx < 0, this corresponds to taking the major step */ if(left_error + left_y_delta <= 0) { left_error += left_y_delta; left_x -= 1; }

right_error += right_d_er; right_x += right_step;

if(right_error <= 0) { right_error += right_y_delta; right_x -= 1; }

if(right_x >= left_x) { hline(bmp,left_x, cur_y, right_x, color); }

cur_y++; left_error += left_x_delta; right_error += right_x_delta; } }

/* Draw the second segment, if possible */ if(cur_y < end_y) { if(major_on_the_left) { right_x = ceilf(V2[0]);

left_x_delta = major_x_delta; right_x_delta = V3[0] - V2[0]; left_y_delta = major_y_delta; right_y_delta = V3[1] - V2[1];

left_error = ((float)cur_y - V1[1]) * left_x_delta - ((float)left_x - V1[0]) * left_y_delta; right_error = ((float)cur_y - V2[1]) * right_x_delta - ((float)right_x - V2[0]) * right_y_delta; } else { right_x = floorf(V2[0]);

left_x_delta = V3[0] - V2[0]; right_x_delta = major_x_delta; left_y_delta = V3[1] - V2[1]; right_y_delta = major_y_delta;

left_error = ((float)cur_y - V2[1]) * left_x_delta - ((float)left_x - V2[0]) * left_y_delta; right_error = ((float)cur_y - V1[1]) * right_x_delta - ((float)right_x - V1[0]) * right_y_delta; }

int left_first = ceilf((left_error) / left_y_delta); int right_first = floorf((right_error) / right_y_delta);

int left_step = ceilf(left_x_delta / left_y_delta); float left_d_er = -float(left_step) * left_y_delta;

int right_step = ceilf(right_x_delta / right_y_delta); float right_d_er = -float(right_step) * right_y_delta;

if(cur_y < end_y) { left_x += left_first; left_error -= float(left_first) * left_y_delta;

right_x += right_first; right_error -= float(right_first) * right_y_delta;

if(right_x >= left_x) { hline(bmp,left_x, cur_y, right_x, color); }

cur_y++; left_error += left_x_delta; right_error += right_x_delta; }

while(cur_y < end_y) { left_error += left_d_er; left_x += left_step;

if(left_error + left_y_delta <= 0) { left_error += left_y_delta; left_x -= 1; }

right_error += right_d_er; right_x += right_step;

if(right_error <= 0) { right_error += right_y_delta; right_x -= 1; }

if(right_x >= left_x) { hline(bmp,left_x, cur_y, right_x, color); }

cur_y++; left_error += left_x_delta; right_error += right_x_delta; } } } </highlightSyntax>