Skip to content

Commit 711648c

Browse files
committed
early triangle-drawing code
1 parent b86d9a8 commit 711648c

8 files changed

+247
-3
lines changed

oil-backend-cpu.c

+128-2
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,27 @@
22
#include "oil-image-private.h"
33
#include "oil-backend-private.h"
44

5+
typedef struct {
6+
unsigned short *depth_buffer;
7+
} CPUPriv;
8+
59
/* like (a * b + 127) / 255), but fast */
610
#define MULDIV255(a, b, tmp) \
711
(tmp = (a) * (b) + 128, ((((tmp) >> 8) + (tmp)) >> 8))
812

913
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;
1118
}
1219

1320
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);
1526
}
1627

1728
static void oil_backend_cpu_load(OILImage *im) {
@@ -73,10 +84,125 @@ static int oil_backend_cpu_composite(OILImage *im, OILImage *src, unsigned char
7384
return 1;
7485
}
7586

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+
76201
OILBackend oil_backend_cpu = {
77202
oil_backend_cpu_new,
78203
oil_backend_cpu_free,
79204
oil_backend_cpu_load,
80205
oil_backend_cpu_save,
81206
oil_backend_cpu_composite,
207+
oil_backend_cpu_draw_triangles,
82208
};

oil-backend-debug.c

+6
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,16 @@ static int oil_backend_debug_composite(OILImage *im, OILImage *src, unsigned cha
3030
return oil_backend_cpu.composite(im, src, alpha, dx, dy, sx, sy, xsize, ysize);
3131
}
3232

33+
static void oil_backend_debug_draw_triangles(OILImage *im, OILMatrix *matrix, OILVertex *vertices, unsigned int *indices, unsigned int indices_length, OILTriangleFlags flags) {
34+
printf("draw_triangles(%p, %p, %p, %p, %i, %i)\n", im, matrix, vertices, indices, indices_length, flags);
35+
oil_backend_cpu.draw_triangles(im, matrix, vertices, indices, indices_length, flags);
36+
}
37+
3338
OILBackend oil_backend_debug = {
3439
oil_backend_debug_new,
3540
oil_backend_debug_free,
3641
oil_backend_debug_load,
3742
oil_backend_debug_save,
3843
oil_backend_debug_composite,
44+
oil_backend_debug_draw_triangles,
3945
};

oil-backend-private.h

+2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ typedef struct {
1717

1818
/* do a composite. */
1919
int (*composite)(OILImage *im, OILImage *src, unsigned char alpha, int dx, int dy, unsigned int sx, unsigned int sy, unsigned int xsize, unsigned int ysize);
20+
/* draw triangles */
21+
void (*draw_triangles)(OILImage *im, OILMatrix *matrix, OILVertex *vertices, unsigned int *indices, unsigned int indices_length, OILTriangleFlags flags);
2022
} OILBackend;
2123

2224
extern OILBackend *oil_backend;

oil-image-private.h

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ struct _OILImage {
1111
int locked;
1212

1313
OILBackend *backend;
14+
void *backend_data;
1415
};
1516

1617
#endif /* __OIL_IMAGE_PRIVATE_H_INCLUDED__ */

oil-image.c

+9
Original file line numberDiff line numberDiff line change
@@ -206,3 +206,12 @@ int oil_image_composite(OILImage *im, OILImage *src, unsigned char alpha, int dx
206206
/* and finally, call back into the backend if there is work to do */
207207
return im->backend->composite(im, src, alpha, dx, dy, sx, sy, xsize, ysize);
208208
}
209+
210+
void oil_image_draw_triangles(OILImage *im, OILMatrix *matrix, OILVertex *vertices, unsigned int *indices, unsigned int indices_length, OILTriangleFlags flags) {
211+
/* all of these are unhandleable */
212+
if (!im || !vertices || !matrix || !indices || indices_length % 3 != 0)
213+
return;
214+
215+
/* ok now that that's out of the way, throw it to the backend */
216+
im->backend->draw_triangles(im, matrix, vertices, indices, indices_length, flags);
217+
}

oil-matrix.c

+10
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,16 @@ int oil_matrix_is_zero(const OILMatrix *matrix)
5555
return 1;
5656
}
5757

58+
void oil_matrix_transform(const OILMatrix *matrix, float *x, float *y, float *z) {
59+
float ox, oy, oz;
60+
ox = matrix->data[0][0] * (*x) + matrix->data[0][1] * (*y) + matrix->data[0][2] * (*z) + matrix->data[0][3];
61+
oy = matrix->data[1][0] * (*x) + matrix->data[1][1] * (*y) + matrix->data[1][2] * (*z) + matrix->data[1][3];
62+
oz = matrix->data[2][0] * (*x) + matrix->data[2][1] * (*y) + matrix->data[2][2] * (*z) + matrix->data[2][3];
63+
*x = ox;
64+
*y = oy;
65+
*z = oz;
66+
}
67+
5868
/* result == a is allowed, result == b is not */
5969

6070
void oil_matrix_add(OILMatrix *result, const OILMatrix *a, const OILMatrix *b) {

oil-python.c

+80-1
Original file line numberDiff line numberDiff line change
@@ -577,7 +577,7 @@ static PyObject *PyOILImage_composite(PyOILImage *self, PyObject *args) {
577577
int dx = 0, dy = 0;
578578
unsigned int sx = 0, sy = 0, xsize = 0, ysize = 0;
579579

580-
if (!PyArg_ParseTuple(args, "O!biiIIII", &PyOILImageType, &src, &alpha, &dx, &dy, &sx, &sy, &xsize, &ysize)) {
580+
if (!PyArg_ParseTuple(args, "O!|biiIIII", &PyOILImageType, &src, &alpha, &dx, &dy, &sx, &sy, &xsize, &ysize)) {
581581
return NULL;
582582
}
583583

@@ -589,6 +589,83 @@ static PyObject *PyOILImage_composite(PyOILImage *self, PyObject *args) {
589589
Py_RETURN_NONE;
590590
}
591591

592+
static PyObject *PyOILImage_draw_triangles(PyOILImage *self, PyObject *args) {
593+
unsigned int i;
594+
PyOILMatrix *matrix = NULL;
595+
PyObject *pyvertices = NULL;
596+
PyObject *pyindices = NULL;
597+
OILVertex *vertices = NULL;
598+
unsigned int vertices_length = 0;
599+
unsigned int *indices = NULL;
600+
unsigned int indices_length = 0;
601+
602+
if (!PyArg_ParseTuple(args, "O!OO", &PyOILMatrixType, &matrix, &pyvertices, &pyindices)) {
603+
return NULL;
604+
}
605+
606+
pyvertices = PySequence_Fast(pyvertices, "vertices are not a sequence");
607+
if (pyvertices)
608+
pyindices = PySequence_Fast(pyindices, "indices are not a sequence");
609+
if (!pyvertices || !pyindices) {
610+
Py_XDECREF(pyvertices);
611+
Py_XDECREF(pyindices);
612+
return NULL;
613+
}
614+
615+
vertices_length = PySequence_Fast_GET_SIZE(pyvertices);
616+
vertices = malloc(sizeof(OILVertex) * vertices_length);
617+
indices_length = PySequence_Fast_GET_SIZE(pyindices);
618+
indices = malloc(sizeof(unsigned int) * indices_length);
619+
if (!vertices || !indices) {
620+
if (vertices)
621+
free(vertices);
622+
if (indices)
623+
free(indices);
624+
Py_DECREF(pyvertices);
625+
Py_DECREF(pyindices);
626+
PyErr_SetString(PyExc_RuntimeError, "out of memory");
627+
return NULL;
628+
}
629+
630+
for (i = 0; i < vertices_length; i++) {
631+
PyObject *vert = PySequence_Fast_GET_ITEM(pyvertices, i);
632+
if (!PyArg_ParseTuple(vert, "fff(bbbb)", &(vertices[i].x), &(vertices[i].y), &(vertices[i].z), &(vertices[i].color.r), &(vertices[i].color.g), &(vertices[i].color.b), &(vertices[i].color.a))) {
633+
free(vertices);
634+
free(indices);
635+
Py_DECREF(pyvertices);
636+
Py_DECREF(pyindices);
637+
PyErr_SetString(PyExc_ValueError, "vertex has invalid form");
638+
return NULL;
639+
}
640+
}
641+
642+
for (i = 0; i < indices_length; i++) {
643+
PyObject *pyindex = PySequence_Fast_GET_ITEM(pyindices, i);
644+
pyindex = PyNumber_Index(pyindex);
645+
if (!pyindex) {
646+
free(vertices);
647+
free(indices);
648+
Py_DECREF(pyvertices);
649+
Py_DECREF(pyindices);
650+
PyErr_SetString(PyExc_ValueError, "index is not valid");
651+
return NULL;
652+
}
653+
654+
indices[i] = PyInt_AsLong(pyindex);
655+
Py_DECREF(pyindex);
656+
}
657+
658+
Py_DECREF(pyvertices);
659+
Py_DECREF(pyindices);
660+
661+
/* FIXME flags! */
662+
oil_image_draw_triangles(self->im, &(matrix->matrix), vertices, indices, indices_length, OIL_DEPTH_TEST);
663+
664+
free(vertices);
665+
free(indices);
666+
Py_RETURN_NONE;
667+
}
668+
592669
static PyMethodDef PyOILImage_methods[] = {
593670
{"load", (PyCFunction)PyOILImage_load, METH_VARARGS | METH_CLASS,
594671
"Load the given path name into an Image object."},
@@ -598,6 +675,8 @@ static PyMethodDef PyOILImage_methods[] = {
598675
"Return a (width, height) tuple."},
599676
{"composite", (PyCFunction)PyOILImage_composite, METH_VARARGS,
600677
"Composite another image on top of this one."},
678+
{"draw_triangles", (PyCFunction)PyOILImage_draw_triangles, METH_VARARGS,
679+
"Draw 3D triangles on top of the image."},
601680
{NULL, NULL, 0, NULL}
602681
};
603682

oil.h

+11
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ void oil_matrix_set_identity(OILMatrix *matrix);
3232
void oil_matrix_set_data(OILMatrix *matrix, const float *data);
3333
int oil_matrix_is_identity(const OILMatrix *matrix);
3434
int oil_matrix_is_zero(const OILMatrix *matrix);
35+
void oil_matrix_transform(const OILMatrix *matrix, float *x, float *y, float *z);
3536
/* result == a is allowed, result == b is not */
3637
void oil_matrix_add(OILMatrix *result, const OILMatrix *a, const OILMatrix *b);
3738
void oil_matrix_subtract(OILMatrix *result, const OILMatrix *a, const OILMatrix *b);
@@ -65,6 +66,15 @@ typedef struct {
6566
unsigned int palette_size;
6667
} OILFormatOptions;
6768

69+
typedef struct {
70+
float x, y, z;
71+
OILPixel color;
72+
} OILVertex;
73+
74+
typedef enum {
75+
OIL_DEPTH_TEST = 0x1,
76+
} OILTriangleFlags;
77+
6878
OILImage *oil_image_new(unsigned int width, unsigned int height);
6979
void oil_image_free(OILImage *im);
7080
OILImage *oil_image_load(const char *path);
@@ -77,5 +87,6 @@ OILPixel *oil_image_lock(OILImage *im);
7787
void oil_image_unlock(OILImage *im);
7888

7989
int oil_image_composite(OILImage *im, OILImage *src, unsigned char alpha, int dx, int dy, unsigned int sx, unsigned int sy, unsigned int xsize, unsigned int ysize);
90+
void oil_image_draw_triangles(OILImage *im, OILMatrix *matrix, OILVertex *vertices, unsigned int *indices, unsigned int indices_length, OILTriangleFlags flags);
8091

8192
#endif /* __OIL_H_INCLUDED__ */

0 commit comments

Comments
 (0)