|
2 | 2 | #include "oil-image-private.h"
|
3 | 3 | #include "oil-backend-private.h"
|
4 | 4 |
|
| 5 | +typedef struct { |
| 6 | + unsigned short *depth_buffer; |
| 7 | +} CPUPriv; |
| 8 | + |
5 | 9 | /* like (a * b + 127) / 255), but fast */
|
6 | 10 | #define MULDIV255(a, b, tmp) \
|
7 | 11 | (tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
|
8 | 12 |
|
9 | 13 | static void oil_backend_cpu_new(OILImage *im) {
|
10 |
| - /* nothing to do */ |
| 14 | + /* add our data struct. FIXME fail if any of these are NULL */ |
| 15 | + CPUPriv *priv; |
| 16 | + priv = im->backend_data = malloc(sizeof(CPUPriv)); |
| 17 | + priv->depth_buffer = NULL; |
11 | 18 | }
|
12 | 19 |
|
13 | 20 | static void oil_backend_cpu_free(OILImage *im) {
|
14 |
| - /* nothing to do */ |
| 21 | + /* free our data struct */ |
| 22 | + CPUPriv *priv = im->backend_data; |
| 23 | + if (priv->depth_buffer) |
| 24 | + free(priv->depth_buffer); |
| 25 | + free(priv); |
15 | 26 | }
|
16 | 27 |
|
17 | 28 | static void oil_backend_cpu_load(OILImage *im) {
|
@@ -73,10 +84,125 @@ static int oil_backend_cpu_composite(OILImage *im, OILImage *src, unsigned char
|
73 | 84 | return 1;
|
74 | 85 | }
|
75 | 86 |
|
| 87 | +/* draws a triangle on the destination image, multiplicatively! |
| 88 | + * used for smooth lighting |
| 89 | + * (excuse the ridiculous number of parameters!) |
| 90 | + * |
| 91 | + * Algorithm adapted from _Fundamentals_of_Computer_Graphics_ |
| 92 | + * by Peter Shirley, Michael Ashikhmin |
| 93 | + * (or at least, the version poorly reproduced here: |
| 94 | + * http://www.gidforums.com/t-20838.html ) |
| 95 | + */ |
| 96 | + |
| 97 | +static inline void draw_triangle(OILImage *im, int inclusive, OILVertex v0, OILVertex v1, OILVertex v2, OILTriangleFlags flags) { |
| 98 | + CPUPriv *priv = im->backend_data; |
| 99 | + /* ranges of pixels that are affected */ |
| 100 | + int xmin, xmax, ymin, ymax; |
| 101 | + /* constant coefficients for alpha, beta, gamma */ |
| 102 | + int a12, a20, a01; |
| 103 | + int b12, b20, b01; |
| 104 | + int c12, c20, c01; |
| 105 | + /* constant normalizers for alpha, beta, gamma */ |
| 106 | + float alpha_norm, beta_norm, gamma_norm; |
| 107 | + /* temporary variables */ |
| 108 | + /*int tmp;*/ |
| 109 | + /* iteration variables */ |
| 110 | + int x, y; |
| 111 | + |
| 112 | + /* if we need to, initialize the depth buffer */ |
| 113 | + if (flags & OIL_DEPTH_TEST && priv->depth_buffer == NULL) { |
| 114 | + priv->depth_buffer = calloc(im->width * im->height, sizeof(unsigned short)); |
| 115 | + } |
| 116 | + |
| 117 | + /* set up draw ranges */ |
| 118 | + xmin = OIL_MIN((int)(v0.x), OIL_MIN((int)(v1.x), (int)(v2.x))); |
| 119 | + ymin = OIL_MIN((int)(v0.y), OIL_MIN((int)(v1.y), (int)(v2.y))); |
| 120 | + xmax = OIL_MAX((int)(v0.x), OIL_MAX((int)(v1.x), (int)(v2.x))) + 1; |
| 121 | + ymax = OIL_MAX((int)(v0.y), OIL_MAX((int)(v1.y), (int)(v2.y))) + 1; |
| 122 | + |
| 123 | + xmin = OIL_MAX(xmin, 0); |
| 124 | + ymin = OIL_MAX(ymin, 0); |
| 125 | + xmax = OIL_MIN(xmax, im->width); |
| 126 | + ymax = OIL_MIN(ymax, im->height); |
| 127 | + |
| 128 | + /* setup coefficients */ |
| 129 | + a12 = (int)(v1.y) - (int)(v2.y); b12 = (int)(v2.x) - (int)(v1.x); c12 = ((int)(v1.x) * (int)(v2.y)) - ((int)(v2.x) * (int)(v1.y)); |
| 130 | + a20 = (int)(v2.y) - (int)(v0.y); b20 = (int)(v0.x) - (int)(v2.x); c20 = ((int)(v2.x) * (int)(v0.y)) - ((int)(v0.x) * (int)(v2.y)); |
| 131 | + a01 = (int)(v0.y) - (int)(v1.y); b01 = (int)(v1.x) - (int)(v0.x); c01 = ((int)(v0.x) * (int)(v1.y)) - ((int)(v1.x) * (int)(v0.y)); |
| 132 | + |
| 133 | + /* setup normalizers */ |
| 134 | + alpha_norm = 1.0f / ((a12 * (int)(v0.x)) + (b12 * (int)(v0.y)) + c12); |
| 135 | + beta_norm = 1.0f / ((a20 * (int)(v1.x)) + (b20 * (int)(v1.y)) + c20); |
| 136 | + gamma_norm = 1.0f / ((a01 * (int)(v2.x)) + (b01 * (int)(v2.y)) + c01); |
| 137 | + |
| 138 | + /* iterate over the destination rect */ |
| 139 | + for (y = ymin; y < ymax; y++) { |
| 140 | + OILPixel *out = &(im->data[y * im->width + xmin]); |
| 141 | + |
| 142 | + for (x = xmin; x < xmax; x++) { |
| 143 | + float alpha, beta, gamma; |
| 144 | + alpha = alpha_norm * ((a12 * x) + (b12 * y) + c12); |
| 145 | + beta = beta_norm * ((a20 * x) + (b20 * y) + c20); |
| 146 | + gamma = gamma_norm * ((a01 * x) + (b01 * y) + c01); |
| 147 | + |
| 148 | + if (alpha >= 0 && beta >= 0 && gamma >= 0 && |
| 149 | + (inclusive || (alpha * beta * gamma > 0))) { |
| 150 | + if (flags & OIL_DEPTH_TEST) { |
| 151 | + int depth = alpha * v0.z + beta * v1.z + gamma * v2.z; |
| 152 | + unsigned short *dbuffer = &(priv->depth_buffer[y * im->width + x]); |
| 153 | + if (depth >= *dbuffer) { |
| 154 | + /* write to our buffer */ |
| 155 | + *dbuffer = depth; |
| 156 | + } else { |
| 157 | + /* skip this, it's behind something */ |
| 158 | + out++; |
| 159 | + continue; |
| 160 | + } |
| 161 | + } |
| 162 | + out->r = alpha * v0.color.r + beta * v1.color.r + gamma * v2.color.r; |
| 163 | + out->g = alpha * v0.color.g + beta * v1.color.g + gamma * v2.color.g; |
| 164 | + out->b = alpha * v0.color.b + beta * v1.color.b + gamma * v2.color.b; |
| 165 | + out->a = 255; /* FIXME */ |
| 166 | + } |
| 167 | + |
| 168 | + out++; |
| 169 | + } |
| 170 | + } |
| 171 | +} |
| 172 | + |
| 173 | +static void oil_backend_cpu_draw_triangles(OILImage *im, OILMatrix *matrix, OILVertex *vertices, unsigned int *indices, unsigned int indices_length, OILTriangleFlags flags) { |
| 174 | + OILMatrix realmat; |
| 175 | + unsigned int i; |
| 176 | + |
| 177 | + /* first we need to take the given matrix which yields [-1, 1] coordinates |
| 178 | + to something that gives pixel x/y coordinates |
| 179 | + also, invert Y because we want +Y to be up in 3D. |
| 180 | + finally, we need [-1, 1] Z to map to [0, 2^16 - 1] for depth buffer */ |
| 181 | + oil_matrix_set_identity(&realmat); |
| 182 | + oil_matrix_scale(&realmat, im->width/2.0f, -(im->height/2.0f), (0xffff/2.0)); |
| 183 | + oil_matrix_translate(&realmat, 1.0f, -1.0f, 1.0f); |
| 184 | + oil_matrix_multiply(&realmat, &realmat, matrix); |
| 185 | + |
| 186 | + |
| 187 | + for (i = 0; i < indices_length; i += 3) { |
| 188 | + OILVertex v0, v1, v2; |
| 189 | + v0 = vertices[indices[i]]; |
| 190 | + v1 = vertices[indices[i + 1]]; |
| 191 | + v2 = vertices[indices[i + 2]]; |
| 192 | + |
| 193 | + oil_matrix_transform(&realmat, &(v0.x), &(v0.y), &(v0.z)); |
| 194 | + oil_matrix_transform(&realmat, &(v1.x), &(v1.y), &(v1.z)); |
| 195 | + oil_matrix_transform(&realmat, &(v2.x), &(v2.y), &(v2.z)); |
| 196 | + |
| 197 | + draw_triangle(im, 1, v0, v1, v2, flags); |
| 198 | + } |
| 199 | +} |
| 200 | + |
76 | 201 | OILBackend oil_backend_cpu = {
|
77 | 202 | oil_backend_cpu_new,
|
78 | 203 | oil_backend_cpu_free,
|
79 | 204 | oil_backend_cpu_load,
|
80 | 205 | oil_backend_cpu_save,
|
81 | 206 | oil_backend_cpu_composite,
|
| 207 | + oil_backend_cpu_draw_triangles, |
82 | 208 | };
|
0 commit comments