Created
November 3, 2013 20:30
-
-
Save agrif/7294460 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| static inline void draw_triangle(OILImage *im, OILImage *tex, OILVertex v0, OILVertex v1, OILVertex v2, OILTriangleFlags flags) { | |
| CPUPriv *priv = im->backend_data; | |
| /* ranges of pixels that are affected */ | |
| int xmin, xmax, ymin, ymax; | |
| /* the (signed) area of the triangle in normalized 2d space */ | |
| float area; | |
| /* constant coefficients for alpha, beta, gamma */ | |
| float a12, a20, a01; | |
| float b12, b20, b01; | |
| float c12, c20, c01; | |
| /* constant normalizers for alpha, beta, gamma */ | |
| float alpha_norm, beta_norm, gamma_norm; | |
| /* temporary variables */ | |
| int tmp; | |
| /* iteration variables */ | |
| int x, y; | |
| /* if we need to, initialize the depth buffer */ | |
| if (OIL_EXPECT(flags & OIL_DEPTH_TEST, OIL_DEPTH_TEST) && OIL_UNLIKELY(priv->depth_buffer == NULL)) { | |
| priv->depth_buffer = calloc(im->width * im->height, sizeof(unsigned short)); | |
| } | |
| /* set up draw ranges */ | |
| xmin = (int)(OIL_MIN(v0.x, OIL_MIN(v1.x, v2.x)) - 0.5); | |
| ymin = (int)(OIL_MIN(v0.y, OIL_MIN(v1.y, v2.y)) - 0.5); | |
| xmax = (int)(OIL_MAX(v0.x, OIL_MAX(v1.x, v2.x)) - 0.5) + 1; | |
| ymax = (int)(OIL_MAX(v0.y, OIL_MAX(v1.y, v2.y)) - 0.5) + 1; | |
| xmin = OIL_CLAMP(xmin, 0, im->width); | |
| ymin = OIL_CLAMP(ymin, 0, im->height); | |
| xmax = OIL_CLAMP(xmax, 0, im->width); | |
| ymax = OIL_CLAMP(ymax, 0, im->height); | |
| /* bail early if the triangle is completely outside */ | |
| if (OIL_LIKELY(ymin >= ymax || xmin >= xmax)) | |
| return; | |
| /* figure out the triangle's area */ | |
| area = 0.5 * ((v1.x - v0.x) * (v0.y - v2.y) - (v1.y - v0.y) * (v0.x - v2.x)); | |
| /* back-face culling, and don't draw anything | |
| with area less than half a pixel */ | |
| if (area < 0.5) | |
| return; | |
| /* setup coefficients */ | |
| a12 = v1.y - v2.y; b12 = v2.x - v1.x; c12 = (v1.x * v2.y) - (v2.x * v1.y); | |
| a20 = v2.y - v0.y; b20 = v0.x - v2.x; c20 = (v2.x * v0.y) - (v0.x * v2.y); | |
| a01 = v0.y - v1.y; b01 = v1.x - v0.x; c01 = (v0.x * v1.y) - (v1.x * v0.y); | |
| /* setup normalizers */ | |
| alpha_norm = 1.0f / ((a12 * v0.x) + (b12 * v0.y) + c12); | |
| beta_norm = 1.0f / ((a20 * v1.x) + (b20 * v1.y) + c20); | |
| gamma_norm = 1.0f / ((a01 * v2.x) + (b01 * v2.y) + c01); | |
| /* apply normalizers */ | |
| a12 *= alpha_norm; b12 *= alpha_norm; c12 *= alpha_norm; | |
| a20 *= beta_norm; b20 *= beta_norm; c20 *= beta_norm; | |
| a01 *= gamma_norm; b01 *= gamma_norm; c01 *= gamma_norm; | |
| /* iterate over the destination rect */ | |
| for (y = ymin; y < ymax; y++) { | |
| OILPixel *out = &(im->data[y * im->width + xmin]); | |
| for (x = xmin; x < xmax; x++) { | |
| float fx, fy; | |
| float alpha, beta, gamma; | |
| float s, t; | |
| int si, ti; | |
| OILPixel p; | |
| fx = x + 0.5; | |
| fy = y + 0.5; | |
| alpha = (a12 * fx) + (b12 * fy) + c12; | |
| beta = (a20 * fx) + (b20 * fy) + c20; | |
| gamma = (a01 * fx) + (b01 * fy) + c01; | |
| if (alpha >= -SMALLFLOAT && beta >= -SMALLFLOAT && gamma >= -SMALLFLOAT) { | |
| alpha = OIL_CLAMP(alpha, 0.0, 1.0); | |
| beta = OIL_CLAMP(beta, 0.0, 1.0); | |
| gamma = OIL_CLAMP(gamma, 0.0, 1.0); | |
| if (OIL_LIKELY(tex != NULL)) { | |
| /* have to be more careful with texture coords, to | |
| prevent bleeding */ | |
| float renorm = 1.0 / (alpha + beta + gamma); | |
| s = renorm * (alpha * v0.s + beta * v1.s + gamma * v2.s); | |
| t = renorm * (alpha * v0.t + beta * v1.t + gamma * v2.t); | |
| si = tex->width * s; | |
| ti = tex->height * -t - 1; | |
| /* using % is too slow for the common case where | |
| these are already inside the image */ | |
| while (OIL_UNLIKELY(si < 0)) | |
| si += tex->width; | |
| while (OIL_UNLIKELY(si >= tex->width)) | |
| si -= tex->width; | |
| while (ti < 0) /* this space intentionally ambiguous */ | |
| ti += tex->height; | |
| while (OIL_UNLIKELY(ti >= tex->height)) | |
| ti -= tex->height; | |
| p = tex->data[ti * tex->width + si]; | |
| if (OIL_UNLIKELY(p.a == 0)) { | |
| /* skip this, nothing to draw | |
| we want to bail before the depth test */ | |
| out++; | |
| continue; | |
| } | |
| } else { | |
| p.r = 255; | |
| p.g = 255; | |
| p.b = 255; | |
| p.a = 255; | |
| } | |
| if (OIL_EXPECT(flags & OIL_DEPTH_TEST, OIL_DEPTH_TEST)) { | |
| int depth = alpha * v0.z + beta * v1.z + gamma * v2.z; | |
| unsigned short *dbuffer = &(priv->depth_buffer[y * im->width + x]); | |
| if (depth >= *dbuffer) { | |
| /* write to our buffer */ | |
| *dbuffer = depth; | |
| } else { | |
| /* skip this, it's behind something */ | |
| out++; | |
| continue; | |
| } | |
| } | |
| if (v0.color.r != 255 || v1.color.r != 255 || v2.color.r != 255) | |
| p.r = MULDIV255(p.r, alpha * v0.color.r + beta * v1.color.r + gamma * v2.color.r, tmp); | |
| if (v0.color.g != 255 || v1.color.g != 255 || v2.color.g != 255) | |
| p.g = MULDIV255(p.g, alpha * v0.color.g + beta * v1.color.g + gamma * v2.color.g, tmp); | |
| if (v0.color.b != 255 || v1.color.b != 255 || v2.color.b != 255) | |
| p.b = MULDIV255(p.b, alpha * v0.color.b + beta * v1.color.b + gamma * v2.color.b, tmp); | |
| if (v0.color.a != 255 || v1.color.a != 255 || v2.color.a != 255) | |
| p.a = MULDIV255(p.a, alpha * v0.color.a + beta * v1.color.a + gamma * v2.color.a, tmp); | |
| composite(out, &p); | |
| } | |
| out++; | |
| } | |
| } | |
| } | |
| static void oil_backend_cpu_draw_triangles(OILImage *im, OILMatrix *matrix, OILImage *tex, OILVertex *vertices, unsigned int vertices_length, unsigned int *indices, unsigned int indices_length, OILTriangleFlags flags) { | |
| OILMatrix realmat; | |
| unsigned int i; | |
| /* first we need to take the given matrix which yields [-1, 1] coordinates | |
| to something that gives pixel x/y coordinates | |
| also, invert Y because we want +Y to be up in 3D. | |
| finally, we need [-1, 1] Z to map to [0, 2^16 - 1] for depth buffer */ | |
| oil_matrix_set_identity(&realmat); | |
| oil_matrix_scale(&realmat, im->width/2.0f, -(im->height/2.0f), (0xffff/2.0)); | |
| oil_matrix_translate(&realmat, 1.0f, -1.0f, 1.0f); | |
| oil_matrix_multiply(&realmat, &realmat, matrix); | |
| for (i = 0; i < indices_length; i += 3) { | |
| OILVertex v0, v1, v2; | |
| v0 = vertices[indices[i]]; | |
| v1 = vertices[indices[i + 1]]; | |
| v2 = vertices[indices[i + 2]]; | |
| oil_matrix_transform(&realmat, &(v0.x), &(v0.y), &(v0.z)); | |
| oil_matrix_transform(&realmat, &(v1.x), &(v1.y), &(v1.z)); | |
| oil_matrix_transform(&realmat, &(v2.x), &(v2.y), &(v2.z)); | |
| draw_triangle(im, tex, v0, v1, v2, flags); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment