Add Z-buffer, fixes issue with pyramidal studs
[render.git] / render.c
1 #include <stdbool.h>
2 #include <stdlib.h>
3 #include <SDL2/SDL.h>
4 #include "array.h"
5 #include "obj.h"
6
7 #define WINDOW_WIDTH 1024
8 #define WINDOW_HEIGHT 768
9 #define CAMERA_DISTANCE 5.f
10
11 #define EPSILON 1e-6f
12
13 #define COS_5DEG .99619470f // math.cos(5 * math.pi / 180.) 
14 #define SIN_5DEG .087155743f // math.sin(5 * math.pi / 180.)
15
16 SDL_Window *window;
17 SDL_Renderer *renderer;
18 SDL_Texture *texture;
19 SDL_Surface *surface;
20
21 void print_vec(const char *prefix, struct array *array, const char *suffix) {
22   assert(array->n_dims == 2);
23   assert(array->dim[1] == sizeof(float));
24   printf("%s[", prefix);
25   for (size_t i = 0; i < array->dim[0]; ++i)
26     printf("%s%7.3f", i ? ", " : "", *(float *)array_index1(array, i));
27   printf("]%s", suffix);
28 }
29
30 void print_mat(const char *prefix, struct array *array, const char *suffix) {
31   assert(array->n_dims == 3);
32   assert(array->dim[2] == sizeof(float));
33   printf("%s[", prefix);
34   for (size_t i = 0; i < array->dim[0]; ++i) {
35     printf("%s[", i ? " " : "");
36     for (size_t j = 0; j < array->dim[1]; ++j)
37       printf("%s%7.3f", j ? ", " : "", *(float *)array_index2(array, i, j));
38     printf("]%s", i < array->dim[0] - 1 ? "\n" : "");
39   }
40   printf("]%s", suffix);
41 }
42
43
44 // compute C = A B
45 struct array *mul_mat_mat(const struct array *A, const struct array *B) {
46   assert(A->n_dims == 3);
47   assert(A->dim[2] == sizeof(float));
48   assert(B->n_dims == 3);
49   assert(B->dim[2] == sizeof(float));
50   size_t I = A->dim[0];
51   size_t J = B->dim[1];
52   size_t K = A->dim[1];
53   assert(K == B->dim[0]);
54   struct array *C = array_new3(I, J, sizeof(float));
55   for (size_t i = 0; i < I; ++i)
56     for (size_t j = 0; j < J; ++j) {
57       float v = 0.f;
58       for (size_t k = 0; k < K; ++k)
59         v += *(float *)array_index2(A, i, k) * *(float *)array_index2(B, k, j);
60       *(float *)array_index2(C, i, j) = v;
61     }
62   return C;
63 }
64
65 // compute c = A b
66 struct array *mul_mat_vec(const struct array *A, const struct array *b) {
67   assert(A->n_dims == 3);
68   assert(A->dim[2] == sizeof(float));
69   assert(b->n_dims == 2);
70   assert(b->dim[1] == sizeof(float));
71   size_t I = A->dim[0];
72   size_t J = A->dim[1];
73   assert(J == b->dim[0]);
74   struct array *c = array_new2(I, sizeof(float));
75   for (size_t i = 0; i < I; ++i) {
76     float v = 0.f;
77     for (size_t j = 0; j < J; ++j)
78       v += *(float *)array_index2(A, i, j) * *(float *)array_index1(b, j);
79     *(float *)array_index1(c, i) = v;
80   }
81   return c;
82 }
83
84 void replace_array(struct array **p, struct array *q) {
85   array_free(*p);
86   *p = q;
87 }
88
89 void vec_min_max(struct array *p, float *min, float *max) {
90   *min = 1e30f;
91   *max = -1e30f;
92   assert(p->n_dims == 2);
93   assert(p->dim[1] == sizeof(float));
94   for (size_t i = 0; i < p->dim[0]; ++i) {
95     float v = *(float *)array_index1(p, i);
96     if (v < *min)
97       *min = v;
98     if (v > *max)
99       *max = v;
100   }
101 }
102
103 void mat_min_max(struct array *p) {
104   for (size_t i = 0; i < p->dim[0]; ++i) {
105     struct array *q = array_dup1(p, i);
106     float min, max;
107     vec_min_max(q, &min, &max); 
108     printf("dim %ld min %7.3f max %7.3f\n", i, min, max);
109     array_free(q);
110   }
111 }
112
113 float vec_dot(struct array *a, struct array *b) {
114   assert(a->n_dims == 2);
115   assert(a->dim[1] == sizeof(float));
116   assert(b->n_dims == 2);
117   assert(b->dim[1] == sizeof(float));
118   size_t I = a->dim[0];
119   assert(I == b->dim[0]);
120   float v = 0.f;
121   for (size_t i = 0; i < I; ++i)
122     v += *(float *)array_index1(a, i) * *(float *)array_index1(b, i);
123   return v;
124 }
125
126 struct array *vec_cross(struct array *a, struct array *b) {
127   assert(a->n_dims == 2);
128   assert(a->dim[0] == 3);
129   assert(a->dim[1] == sizeof(float));
130   assert(b->n_dims == 2);
131   assert(b->dim[0] == 3);
132   assert(b->dim[1] == sizeof(float));
133   struct array *c = array_new2(3, sizeof(float));
134   *(float *)array_index1(c, 0) =
135     *(float *)array_index1(a, 1) * *(float *)array_index1(b, 2) -
136     *(float *)array_index1(a, 2) * *(float *)array_index1(b, 1);
137   *(float *)array_index1(c, 1) =
138     *(float *)array_index1(a, 2) * *(float *)array_index1(b, 0) -
139     *(float *)array_index1(a, 0) * *(float *)array_index1(b, 2);
140   *(float *)array_index1(c, 2) =
141     *(float *)array_index1(a, 0) * *(float *)array_index1(b, 1) -
142     *(float *)array_index1(a, 1) * *(float *)array_index1(b, 0);
143   return c;
144 }
145
146 struct array *mat_unproject(struct array *A) {
147   assert(A->n_dims == 3);
148   assert(A->dim[2] == sizeof(float));
149   size_t I = A->dim[1];
150   size_t J = A->dim[0];
151   assert(J >= 1);
152   struct array *B = array_new3(--J, I, sizeof(float));
153   for (size_t i = 0; i < I; ++i) {
154     float d = *(float *)array_index2(A, J, i);
155     assert(d < -EPSILON || d >= EPSILON);
156     d = 1.f / d;
157     for (size_t j = 0; j < J; ++j)
158       *(float *)array_index2(B, j, i) = *(float *)array_index2(A, j, i) * d;
159   }
160   return B;
161 }
162
163 struct array *mat_column(struct array *A, size_t col) {
164   assert(A->n_dims == 3);
165   assert(A->dim[2] == sizeof(float));
166   size_t I = A->dim[0];
167   struct array *B = array_new2(I, sizeof(float));
168   for (size_t i = 0; i < I; ++i)
169     *(float *)array_index1(B, i) = *(float *)array_index2(A, i, col);
170   return B; 
171 }
172
173 struct array *mat_columns(struct array *A, size_t n_cols, size_t *cols) {
174   assert(A->n_dims == 3);
175   assert(A->dim[2] == sizeof(float));
176   size_t I = A->dim[0];
177   struct array *B = array_new3(I, n_cols, sizeof(float));
178   for (size_t i = 0; i < I; ++i)
179     for (size_t j = 0; j < n_cols; ++j)
180       *(float *)array_index2(B, i, j) = *(float *)array_index2(A, i, cols[j]);
181   return B; 
182 }
183
184 struct array *vec_add(struct array *a, struct array *b) {
185   assert(a->n_dims == 2);
186   assert(a->dim[1] == sizeof(float));
187   assert(b->n_dims == 2);
188   assert(b->dim[1] == sizeof(float));
189   size_t I = a->dim[0];
190   assert(I == b->dim[0]);
191   struct array *c = array_new2(I, sizeof(float));
192   for (size_t i = 0; i < I; ++i)
193     *(float *)array_index1(c, i) =
194       *(float *)array_index1(a, i) + *(float *)array_index1(b, i);
195   return c;
196 }
197
198 struct array *vec_sub(struct array *a, struct array *b) {
199   assert(a->n_dims == 2);
200   assert(a->dim[1] == sizeof(float));
201   assert(b->n_dims == 2);
202   assert(b->dim[1] == sizeof(float));
203   size_t I = a->dim[0];
204   assert(I == b->dim[0]);
205   struct array *c = array_new2(I, sizeof(float));
206   for (size_t i = 0; i < I; ++i)
207     *(float *)array_index1(c, i) =
208       *(float *)array_index1(a, i) - *(float *)array_index1(b, i);
209   return c;
210 }
211
212 int main(void) {
213   if (SDL_Init(SDL_INIT_VIDEO) < 0) {
214     fprintf(stderr, "SDL_Init(): %s\n\n", SDL_GetError());
215     exit(EXIT_FAILURE);
216   }
217
218   if (!SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "1")) {
219     fprintf(stderr, "SDL_SetHint(): %s\n", SDL_GetError());
220     exit(EXIT_FAILURE);
221   }
222
223   window = SDL_CreateWindow(
224     "render",
225     SDL_WINDOWPOS_UNDEFINED,
226     SDL_WINDOWPOS_UNDEFINED,
227     WINDOW_WIDTH,
228     WINDOW_HEIGHT,
229     SDL_WINDOW_SHOWN
230   );
231   if (window == NULL) {
232     fprintf(stderr, "SDL_CreateWindow(): %s\n", SDL_GetError());
233     exit(EXIT_FAILURE);
234   }
235
236   renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
237   if (renderer == NULL) {
238     fprintf(stderr, "SDL_CreateRenderer(): %s\n", SDL_GetError());
239     exit(EXIT_FAILURE);
240   }
241
242   texture = SDL_CreateTexture(
243     renderer,
244     SDL_PIXELFORMAT_ARGB8888,
245     SDL_TEXTUREACCESS_STREAMING,
246     WINDOW_WIDTH,
247     WINDOW_HEIGHT
248   );
249   if (texture == NULL) {
250     fprintf(stderr, "SDL_CreateTexture(): %s\n", SDL_GetError());
251     exit(EXIT_FAILURE);
252   }
253
254   // device
255   struct array *device_transform = array_new3_init(
256     3,
257     3,
258     sizeof(float),
259     (float[3][3]){
260       {WINDOW_WIDTH, 0.f, .5f * WINDOW_WIDTH},
261       {0.f, WINDOW_WIDTH, .5f * WINDOW_HEIGHT},
262       {0.f, 0.f, 1.f},
263     }
264   );
265   print_mat("device_transform\n", device_transform, "\n");
266
267   // perspective
268   struct array *perspective_transform = array_new3_init(
269     3,
270     4,
271     sizeof(float),
272     (float[3][4]){
273       {1.f, 0.f, 0.f, 0.f},
274       {0.f, 0.f, 1.f, 0.f},
275       {0.f, 1.f, 0.f, 0.f},
276     }
277   );
278   print_mat("perspective_transform\n", perspective_transform, "\n");
279
280   // camera
281   struct array *camera_rotate = array_new3_init(
282     4,
283     4,
284     sizeof(float),
285     (float[4][4]){
286       {1.f, 0.f, 0.f, 0.f},
287       {0.f, 1.f, 0.f, 0.f},
288       {0.f, 0.f, 1.f, 0.f},
289       {0.f, 0.f, 0.f, 1.f}
290     }
291   );
292   print_mat("camera_rotate\n", camera_rotate, "\n");
293
294   struct array *camera_translate = array_new3_init(
295     4,
296     4,
297     sizeof(float),
298     (float[4][4]){
299       {1.f, 0.f, 0.f, 0.f},
300       {0.f, 1.f, 0.f, CAMERA_DISTANCE},
301       {0.f, 0.f, 1.f, 0.f},
302       {0.f, 0.f, 0.f, 1.f}
303     }
304   );
305   print_mat("camera_translate\n", camera_translate, "\n");
306
307   // model
308 #if 1
309   struct obj *obj = obj_create("Chest.obj"); //"grey_bliss_set.obj");
310   struct array *model_vertices;
311   struct array *model_vertices_t;
312   {
313     int I = obj->vc;
314     model_vertices = array_new3(4, I, sizeof(float));
315     model_vertices_t = array_new3(2, I, sizeof(float));
316     for (int i = 0; i < I; ++i) {
317       *(float *)array_index2(model_vertices, 0, i) = obj->vv[i].v[0];
318       *(float *)array_index2(model_vertices, 1, i) = obj->vv[i].v[1];
319       *(float *)array_index2(model_vertices, 2, i) = obj->vv[i].v[2];
320       *(float *)array_index2(model_vertices, 3, i) = 1.f;
321
322       *(float *)array_index2(model_vertices_t, 0, i) = obj->vv[i].t[0];
323       *(float *)array_index2(model_vertices_t, 1, i) = obj->vv[i].t[1];
324     }
325   }
326 #else
327   struct array *model_vertices = array_new3(4, 8, sizeof(float));
328   for (int i = 0; i < 2; ++i)
329     for (int j = 0; j < 2; ++j)
330       for (int k = 0; k < 2; ++k) {
331         float v[4] = {i * 2 - 1, j * 2 - 1, k * 2 - 1, 1.f};
332         int l = i * 4 + j * 2 + k;
333         *(float *)array_index2(model_vertices, 0, l) = v[0];
334         *(float *)array_index2(model_vertices, 1, l) = v[1];
335         *(float *)array_index2(model_vertices, 2, l) = v[2];
336         *(float *)array_index2(model_vertices, 3, l) = v[3];
337       }
338   size_t model_lines[12][2] = {
339     {0, 1}, // 0, 0, 0 -> 0, 0, 1
340     {0, 2}, // 0, 0, 0 -> 0, 1, 0
341     {0, 4}, // 0, 0, 0 -> 1, 0, 0
342
343     //{1, 0}, // 0, 0, 1 -> 0, 0, 0
344     {1, 3}, // 0, 0, 1 -> 0, 1, 1
345     {1, 5}, // 0, 0, 1 -> 1, 0, 1
346
347     {2, 3}, // 0, 1, 0 -> 0, 1, 1
348     //{2, 0}, // 0, 1, 0 -> 0, 0, 0
349     {2, 6}, // 0, 1, 0 -> 1, 1, 0
350
351     //{3, 2}, // 0, 1, 1 -> 0, 1, 0
352     //{3, 1}, // 0, 1, 1 -> 0, 0, 1
353     {3, 7}, // 0, 1, 1 -> 1, 1, 1
354
355     {4, 5}, // 1, 0, 0 -> 1, 0, 1
356     {4, 6}, // 1, 0, 0 -> 1, 1, 0
357     //{4, 0}, // 1, 0, 0 -> 0, 0, 0
358
359     //{5, 4}, // 1, 0, 1 -> 1, 0, 0
360     {5, 7}, // 1, 0, 1 -> 1, 1, 1
361     //{5, 1}, // 1, 0, 1 -> 0, 0, 1
362
363     {6, 7}, // 1, 1, 0 -> 1, 1, 1
364     //{6, 4}, // 1, 1, 0 -> 1, 0, 0
365     //{6, 2}, // 1, 1, 0 -> 0, 1, 0
366
367     //{7, 6}, // 1, 1, 1 -> 1, 1, 0
368     //{7, 5}, // 1, 1, 1 -> 1, 0, 1
369     //{7, 3}, // 1, 1, 1 -> 0, 1, 1
370   };
371 #endif
372
373   struct array *model_rotate = array_new3_init(
374     4,
375     4,
376     sizeof(float),
377     (float[4][4]){
378       {1.f, 0.f, 0.f, 0.f},
379       {0.f, 1.f, 0.f, 0.f},
380       {0.f, 0.f, 1.f, 0.f},
381       {0.f, 0.f, 0.f, 1.f}
382     }
383   );
384   print_mat("model_rotate\n", model_rotate, "\n");
385
386   struct array *model_translate = array_new3_init(
387     4,
388     4,
389     sizeof(float),
390     (float[4][4]){
391       {1.f, 0.f, 0.f, 0.f},
392       {0.f, 1.f, 0.f, 0.f},
393       {0.f, 0.f, 1.f, 0.f},
394       {0.f, 0.f, 0.f, 1.f}
395     }
396   );
397   print_mat("model_translate\n", model_translate, "\n");
398
399   // rotation matrices for navigation
400   struct array *rotate_xy_5deg = array_new3_init(
401     4,
402     4,
403     sizeof(float),
404     (float[4][4]){
405       {COS_5DEG, SIN_5DEG, 0.f, 0.f},
406       {-SIN_5DEG, COS_5DEG, 0.f, 0.f},
407       {0.f, 0.f, 1.f, 0.f},
408       {0.f, 0.f, 0.f, 1.f}
409     }
410   );
411   struct array *rotate_xy_m5deg = array_new3_init(
412     4,
413     4,
414     sizeof(float),
415     (float[4][4]){
416       {COS_5DEG, -SIN_5DEG, 0.f, 0.f},
417       {SIN_5DEG, COS_5DEG, 0.f, 0.f},
418       {0.f, 0.f, 1.f, 0.f},
419       {0.f, 0.f, 0.f, 1.f}
420     }
421   );
422   struct array *rotate_xz_5deg = array_new3_init(
423     4,
424     4,
425     sizeof(float),
426     (float[4][4]){
427       {COS_5DEG, 0.f, SIN_5DEG, 0.f},
428       {0.f, 1.f, 0.f, 0.f},
429       {-SIN_5DEG, 0.f, COS_5DEG, 0.f},
430       {0.f, 0.f, 0.f, 1.f}
431     }
432   );
433   struct array *rotate_xz_m5deg = array_new3_init(
434     4,
435     4,
436     sizeof(float),
437     (float[4][4]){
438       {COS_5DEG, 0.f, -SIN_5DEG, 0.f},
439       {0.f, 1.f, 0.f, 0.f},
440       {SIN_5DEG, 0.f, COS_5DEG, 0.f},
441       {0.f, 0.f, 0.f, 1.f}
442     }
443   );
444   struct array *rotate_yz_5deg = array_new3_init(
445     4,
446     4,
447     sizeof(float),
448     (float[4][4]){
449       {1.f, 0.f, 0.f, 0.f},
450       {0.f, COS_5DEG, -SIN_5DEG, 0.f},
451       {0.f, SIN_5DEG, COS_5DEG, 0.f},
452       {0.f, 0.f, 0.f, 1.f}
453     }
454   );
455   struct array *rotate_yz_m5deg = array_new3_init(
456     4,
457     4,
458     sizeof(float),
459     (float[4][4]){
460       {1.f, 0.f, 0.f, 0.f},
461       {0.f, COS_5DEG, SIN_5DEG, 0.f},
462       {0.f, -SIN_5DEG, COS_5DEG, 0.f},
463       {0.f, 0.f, 0.f, 1.f}
464     }
465   );
466
467   // helpers
468 #if 0
469   struct array *to_barycentric = array_new3_init(
470     4,
471     3,
472     sizeof(float),
473     (float[4][3]){
474       {1.f, 0.f, 0.f},
475       {0.f, 1.f, 0.f},
476       {-1.f, -1.f, 1.f},
477       {0.f, 0.f, 1.f}
478     }
479   );
480 #endif
481   struct array *barycentric = array_new3(
482     3,
483     101 * 102 / 2,
484     sizeof(float)
485   );
486   {
487     int k = 0;
488     for (int i = 0; i <= 100; ++i)
489       for (int j = 0; i + j <= 100; ++j) {
490         *(float *)array_index2(barycentric, 0, k) = i * .01f;
491         *(float *)array_index2(barycentric, 1, k) = j * .01f;
492         *(float *)array_index2(barycentric, 2, k) = (100 - i - j) * .01f;
493         ++k;
494       }
495     assert(k == 101 * 102 / 2);
496   }
497
498   // main loop
499   struct array *frame =
500     array_new3(WINDOW_HEIGHT, WINDOW_WIDTH, sizeof(uint32_t));
501   struct array *frame_z =
502     array_new3(WINDOW_HEIGHT, WINDOW_WIDTH, sizeof(float));
503   while (true) {
504     SDL_Event event;
505     while (SDL_PollEvent(&event))
506       switch (event.type) {
507       case SDL_QUIT:
508         goto quit;
509       case SDL_KEYDOWN:
510         {
511           SDL_KeyboardEvent *e = (SDL_KeyboardEvent *)&event;
512           switch (e->keysym.sym) {
513           case SDLK_u:
514             replace_array(
515               &model_rotate,
516               mul_mat_mat(rotate_xz_5deg, model_rotate)
517             );
518             break;
519           case SDLK_i:
520             replace_array(
521               &model_rotate,
522               mul_mat_mat(rotate_yz_5deg, model_rotate)
523             );
524             break;
525           case SDLK_o:
526             replace_array(
527               &model_rotate,
528               mul_mat_mat(rotate_xz_m5deg, model_rotate)
529             );
530             break;
531           case SDLK_j:
532             replace_array(
533               &model_rotate,
534               mul_mat_mat(rotate_xy_5deg, model_rotate)
535             );
536             break;
537           case SDLK_k:
538             replace_array(
539               &model_rotate,
540               mul_mat_mat(rotate_yz_m5deg, model_rotate)
541             );
542             break;
543           case SDLK_l:
544             replace_array(
545               &model_rotate,
546               mul_mat_mat(rotate_xy_m5deg, model_rotate)
547             );
548             break;
549           }
550         }
551         break;
552       }
553
554     SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
555     SDL_RenderClear(renderer);
556
557     // compose device, perspective, camera and model transforms for speed
558     struct array *t0 = mul_mat_mat(device_transform, perspective_transform);
559     struct array *t1 = mul_mat_mat(camera_rotate, camera_translate);
560     struct array *t2 = mul_mat_mat(t1, model_rotate);
561     struct array *t3 = mul_mat_mat(t2, model_translate);
562
563     // transform model points
564     struct array *p = mul_mat_mat(t3, model_vertices);
565     struct array *pu = mat_unproject(p);
566     struct array *q = mul_mat_mat(t0, p);
567     struct array *qu = mat_unproject(q);
568
569     // draw
570     array_clear0(frame);
571     for (int i = 0; i < WINDOW_HEIGHT; ++i)
572       for (int j = 0; j < WINDOW_WIDTH; ++j)
573         *(float *)array_index2(frame_z, i, j) = 1e30f;
574 #if 1
575     {
576       int I = obj->sc;
577       for (int i = 0; i < I; ++i) {
578         int J = obj->sv[i].pc;
579         for (int j = 0; j < J; ++j) {
580           size_t v[3] = {
581             obj->sv[i].pv[j].vi[0],
582             obj->sv[i].pv[j].vi[1],
583             obj->sv[i].pv[j].vi[2]
584           };
585
586 #if 1
587           // check face visibility
588           struct array *pu0 = mat_column(pu, v[0]);
589           struct array *pu1 = mat_column(pu, v[1]);
590           struct array *pu2 = mat_column(pu, v[2]);
591           struct array *dir1 = vec_sub(pu1, pu0);
592           struct array *dir2 = vec_sub(pu2, pu0);
593           struct array *norm = vec_cross(dir1, dir2); // outward pointing
594           if (
595             /*vec_dot(norm, norm) < EPSILON ||*/ // degenerate face
596             vec_dot(norm, pu0) >= -EPSILON // origin is on underside of face
597           ) {
598             array_free(norm);
599             array_free(dir2);
600             array_free(dir1);
601             array_free(pu2);
602             array_free(pu1);
603             array_free(pu0);
604             continue;
605           }
606           array_free(norm);
607           array_free(dir2);
608           array_free(dir1);
609           array_free(pu2);
610           array_free(pu1);
611           array_free(pu0);
612 #endif
613
614 #if 1 // roughly texturize
615           int mi = obj->sv[i].mi;
616           if (mi >= 0 && mi < obj->mc) {
617             struct array *map = obj->mv[mi].kv[OBJ_KD].map;
618             assert(map->dim[2] == 3);
619             struct array *tc = mat_columns(model_vertices_t, 3, v);
620             for (int i = 0; i < 3; ++i) {
621               *(float *)array_index2(tc, 0, i) *= map->dim[1];
622               *(float *)array_index2(tc, 1, i) *= map->dim[0];
623             }
624             struct array *tcb = mul_mat_mat(tc, barycentric);
625             struct array *qc = mat_columns(q, 3, v);
626             struct array *qcb = mul_mat_mat(qc, barycentric);
627             struct array *qcbu = mat_unproject(qcb);
628             size_t K = barycentric->dim[1];
629             for (size_t k = 0; k < K; ++k) {
630               float u = floorf(*(float *)array_index2(tcb, 0, k));
631               float v = floorf(*(float *)array_index2(tcb, 1, k));
632               float x = roundf(*(float *)array_index2(qcbu, 0, k));
633               float y = roundf(*(float *)array_index2(qcbu, 1, k));
634               if (
635                 u >= 0 &&
636                 u < map->dim[1] &&
637                 v >= 0 &&
638                 v < map->dim[0] &&
639                 x >= 0 &&
640                 x < WINDOW_WIDTH &&
641                 y >= 0 &&
642                 y < WINDOW_HEIGHT
643               ) {
644                 size_t xi = (size_t)x;
645                 size_t yi = (size_t)y;
646                 float z = *(float *)array_index2(qcb, 2, k);
647                 float *fz = (float *)array_index2(frame_z, yi, xi);
648                 if (z < *fz) {
649                   size_t ui = (size_t)u;
650                   size_t vi = (size_t)v;
651                   uint8_t *m = array_index2(map, vi, ui);
652                   uint8_t *f = array_index2(frame, yi, xi);
653                   f[0] = m[0]; // b
654                   f[1] = m[1]; // g
655                   f[2] = m[2]; // r
656                   f[3] = 0xff; // a
657                   *fz = z;
658                 }
659               }
660             }
661             array_free(qcbu);
662             array_free(qcb);
663             array_free(qc);
664             array_free(tcb);
665             array_free(tc);
666           }
667 #else // wireframe only
668           struct array *quc = mat_columns(qu, 3, v);
669           SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 0xff);
670           SDL_RenderDrawLine(
671             renderer,
672             *(float *)array_index2(quc, 0, 0),
673             *(float *)array_index2(quc, 1, 0),
674             *(float *)array_index2(quc, 0, 1),
675             *(float *)array_index2(quc, 1, 1)
676           );
677           SDL_RenderDrawLine(
678             renderer,
679             *(float *)array_index2(quc, 0, 1),
680             *(float *)array_index2(quc, 1, 1),
681             *(float *)array_index2(quc, 0, 2),
682             *(float *)array_index2(quc, 1, 2)
683           );
684           SDL_RenderDrawLine(
685             renderer,
686             *(float *)array_index2(quc, 0, 2),
687             *(float *)array_index2(quc, 1, 2),
688             *(float *)array_index2(quc, 0, 0),
689             *(float *)array_index2(quc, 1, 0)
690           );
691           array_free(quc);
692 #endif
693         }
694         /*int*/ J = obj->sv[i].lc;
695         for (int j = 0; j < J; ++j) {
696           size_t v[2] = {
697             obj->sv[i].lv[j].vi[0],
698             obj->sv[i].lv[j].vi[1]
699           };
700           struct array *quc = mat_columns(qu, 2, v);
701           SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 0xff);
702           SDL_RenderDrawLine(
703             renderer,
704             *(float *)array_index2(quc, 0, 0),
705             *(float *)array_index2(quc, 1, 0),
706             *(float *)array_index2(quc, 0, 1),
707             *(float *)array_index2(quc, 1, 1)
708           );
709           array_free(quc);
710         }
711       }
712     }
713 #else
714     for (int i = 0; i < 12; ++i) {
715       struct array *quc = mat_columns(qu, 2, model_lines[i]);
716       SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 0xff);
717       SDL_RenderDrawLine(
718         renderer,
719         *(float *)array_index2(quc, 0, 0),
720         *(float *)array_index2(quc, 1, 0),
721         *(float *)array_index2(quc, 0, 1),
722         *(float *)array_index2(quc, 1, 1)
723       );
724       array_free(quc);
725     }
726 #endif
727
728     array_free(qu);
729     array_free(q);
730     array_free(pu);
731     array_free(p);
732     array_free(t3);
733     array_free(t2);
734     array_free(t1);
735     array_free(t0);
736
737     SDL_UpdateTexture(texture, NULL, array_index0(frame), frame->stride[1]);
738     SDL_RenderCopy(renderer, texture, NULL, NULL);
739     SDL_RenderPresent(renderer);
740     SDL_Delay(1);
741   }
742
743 quit:
744   SDL_DestroyRenderer(renderer);
745   SDL_DestroyWindow(window);
746   SDL_Quit();
747   return 0;
748 }