Draw a cube
authorNick Downing <nick@ndcode.org>
Mon, 18 Apr 2022 14:20:35 +0000 (00:20 +1000)
committerNick Downing <nick@ndcode.org>
Mon, 18 Apr 2022 14:20:35 +0000 (00:20 +1000)
Makefile
array.h
render.c

index 09561ca..b6b2a4e 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-CFLAGS=-g -O3 -Wall
+CFLAGS=-g -Og -Wall
 
 render: render.o
        ${CC} -o $@ $< -lSDL2 -lm
diff --git a/array.h b/array.h
index 70fa0a6..df20440 100644 (file)
--- a/array.h
+++ b/array.h
@@ -141,6 +141,9 @@ static inline __attribute__((always_inline)) void *array_index1(
   size_t i0
 ) {
   assert(self->n_dims >= 1);
+#ifndef NDEBUG
+  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
+#endif
   *size = self->stride[1];
   return
     (char *)self +
@@ -155,6 +158,10 @@ static inline __attribute__((always_inline)) void *array_index2(
   size_t i1
 ) {
   assert(self->n_dims >= 2);
+#ifndef NDEBUG
+  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
+  assert(i1 >= 0 && i1 < self->stride[1] / self->stride[2]);
+#endif
   *size = self->stride[2];
   return
     (char *)self +
@@ -171,6 +178,11 @@ static inline __attribute__((always_inline)) void *array_index3(
   size_t i2
 ) {
   assert(self->n_dims >= 3);
+#ifndef NDEBUG
+  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
+  assert(i1 >= 0 && i1 < self->stride[1] / self->stride[2]);
+  assert(i2 >= 0 && i2 < self->stride[2] / self->stride[3]);
+#endif
   *size = self->stride[3];
   return
     (char *)self +
@@ -189,6 +201,12 @@ static inline __attribute__((always_inline)) void *array_index4(
   size_t i3
 ) {
   assert(self->n_dims >= 4);
+#ifndef NDEBUG
+  assert(i0 >= 0 && i0 < self->stride[0] / self->stride[1]);
+  assert(i1 >= 0 && i1 < self->stride[1] / self->stride[2]);
+  assert(i2 >= 0 && i2 < self->stride[2] / self->stride[3]);
+  assert(i3 >= 0 && i2 < self->stride[3] / self->stride[4]);
+#endif
   *size = self->stride[4];
   return
     (char *)self +
index 11ea68d..6d0931e 100644 (file)
--- a/render.c
+++ b/render.c
 #define WINDOW_WIDTH 1024
 #define WINDOW_HEIGHT 768
 
+#define EPSILON 1e-6f
+
 SDL_Window *window;
 SDL_Renderer *renderer;
 SDL_Surface *window_surface;
 
+void print_vec30(struct array *array) {
+  for (int i = 0; i < 3; ++i) {
+    float v;
+    array_get1(array, &v, i);
+    printf("%s%7.3f", i ? ", " : "[", v);
+  }
+  printf("]");
+}
+
+void print_vec31(struct array *array, size_t i0) {
+  for (int i = 0; i < 3; ++i) {
+    float v;
+    array_get2(array, &v, i0, i);
+    printf("%s%7.3f", i ? ", " : "[", v);
+  }
+  printf("]");
+}
+
+void print_vec40(struct array *array) {
+  for (int i = 0; i < 4; ++i) {
+    float v;
+    array_get1(array, &v, i);
+    printf("%s%7.3f", i ? ", " : "[", v);
+  }
+  printf("]");
+}
+
+void print_vec41(struct array *array, size_t i0) {
+  for (int i = 0; i < 4; ++i) {
+    float v;
+    array_get2(array, &v, i0, i);
+    printf("%s%7.3f", i ? ", " : "[", v);
+  }
+  printf("]");
+}
+
+void print_mat330(struct array *array) {
+  for (int i = 0; i < 3; ++i) {
+    printf("%s", i ? " " : "[");
+    print_vec31(array, i);
+    printf("%s", i < 2 ? "\n" : "]");
+  }
+}
+
+void print_mat340(struct array *array) {
+  for (int i = 0; i < 3; ++i) {
+    printf("%s", i ? " " : "[");
+    print_vec41(array, i);
+    printf("%s", i < 2 ? "\n" : "]");
+  }
+}
+
+void print_mat440(struct array *array) {
+  for (int i = 0; i < 4; ++i) {
+    printf("%s", i ? " " : "[");
+    print_vec41(array, i);
+    printf("%s", i < 3 ? "\n" : "]");
+  }
+}
+
+// compute C = A B
+void mul_mat33_mat34(struct array *A, struct array *B, struct array *C) {
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; j < 4; ++j) {
+      float c = 0.f;
+      for (int k = 0; k < 3; ++k) {
+        float a, b;
+        array_get2(A, &a, i, k);
+        array_get2(B, &b, k, j);
+        c += a * b;
+      }
+      array_set2(C, &c, i, j);
+    }
+}
+void mul_mat34_mat44(struct array *A, struct array *B, struct array *C) {
+  for (int i = 0; i < 3; ++i)
+    for (int j = 0; j < 4; ++j) {
+      float c = 0.f;
+      for (int k = 0; k < 4; ++k) {
+        float a, b;
+        array_get2(A, &a, i, k);
+        array_get2(B, &b, k, j);
+        c += a * b;
+      }
+      array_set2(C, &c, i, j);
+    }
+}
+void mul_mat34_vec4(struct array *A, struct array *B, struct array *C) {
+  for (int i = 0; i < 3; ++i) {
+    float c = 0.f;
+    for (int j = 0; j < 4; ++j) {
+      float a, b;
+      array_get2(A, &a, i, j);
+      array_get1(B, &b, j);
+      c += a * b;
+    }
+    array_set1(C, &c, i);
+  }
+}
+
 int main(void) {
   if (SDL_Init(SDL_INIT_VIDEO) < 0) {
     fprintf(stderr, "SDL_Init(): %s\n\n", SDL_GetError());
@@ -46,24 +148,111 @@ int main(void) {
     exit(EXIT_FAILURE);
   }
 
-  SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
+  // device
+  struct array *device_transform = array_new3(3, 3, sizeof(float));
+  {
+    float v[3][3] = {
+      {WINDOW_WIDTH, 0.f, .5f * WINDOW_WIDTH},
+      {0.f, WINDOW_WIDTH, .5f * WINDOW_HEIGHT},
+      {0.f, 0.f, 1.f},
+    };
+    array_set0(device_transform, v);
+  }
+  printf("device_transform\n");
+  print_mat330(device_transform);
+  printf("\n");
 
-  struct array *a = array_new3(4, 4, sizeof(float));
-  array_clear0(a);
-  for (int i = 0; i < 4; ++i)
-    for (int j = 0; j < i; ++j) {
-      float v = i + j;
-      array_set2(a, &v, i, j);
-    }
-  for (int i = 0; i < 4; ++i) {
-    for (int j = 0; j < 4; ++j) {
-      float v;
-      array_get2(a, &v, i, j);
-      printf("%s%7.3f", j ? ", " : i ? " [" : "[[", v);
-    }
-    printf("%s\n", i < 3 ? "]" : "]]");
+  // perspective
+  struct array *perspective_transform = array_new3(3, 4, sizeof(float));
+  {
+    float v[3][4] = {
+      {1.f, 0.f, 0.f, 0.f},
+      {0.f, 0.f, 1.f, 0.f},
+      {0.f, 1.f, 0.f, 0.f},
+    };
+    array_set0(perspective_transform, v);
   }
+  printf("perspective_transform\n");
+  print_mat340(perspective_transform);
+  printf("\n");
+
+  // camera
+  struct array *camera_transform = array_new3(4, 4, sizeof(float));
+  {
+    float v[4][4] = {
+      {1.f, 0.f, 0.f, 0.f},
+      {0.f, 1.f, 0.f, 10.f},
+      {0.f, 0.f, 1.f, 0.f},
+      {0.f, 0.f, 0.f, 1.f}
+    };
+    array_set0(camera_transform, v);
+  }
+  printf("camera_transform\n");
+  print_mat440(camera_transform);
+  printf("\n");
+
+  // model
+  struct array *model_vertices = array_new3(8, 4, sizeof(float));
+  for (int i = 0; i < 2; ++i)
+    for (int j = 0; j < 2; ++j)
+      for (int k = 0; k < 2; ++k) {
+        float v[4] = {i * 2 - 1, j * 2 - 1, k * 2 - 1, 1.f};
+        array_set1(model_vertices, v, i * 4 + j * 2 + k);
+      }
+  printf("model_vertices\n");
+  for (int i = 0; i < 8; ++i) {
+    print_vec41(model_vertices, i);
+    printf("\n");
+  }
+  int model_lines[12][2] = {
+    {0, 1}, // 0, 0, 0 -> 0, 0, 1
+    {0, 2}, // 0, 0, 0 -> 0, 1, 0
+    {0, 4}, // 0, 0, 0 -> 1, 0, 0
+
+    //{1, 0}, // 0, 0, 1 -> 0, 0, 0
+    {1, 3}, // 0, 0, 1 -> 0, 1, 1
+    {1, 5}, // 0, 0, 1 -> 1, 0, 1
+
+    {2, 3}, // 0, 1, 0 -> 0, 1, 1
+    //{2, 0}, // 0, 1, 0 -> 0, 0, 0
+    {2, 6}, // 0, 1, 0 -> 1, 1, 0
+
+    //{3, 2}, // 0, 1, 1 -> 0, 1, 0
+    //{3, 1}, // 0, 1, 1 -> 0, 0, 1
+    {3, 7}, // 0, 1, 1 -> 1, 1, 1
 
+    {4, 5}, // 1, 0, 0 -> 1, 0, 1
+    {4, 6}, // 1, 0, 0 -> 1, 1, 0
+    //{4, 0}, // 1, 0, 0 -> 0, 0, 0
+
+    //{5, 4}, // 1, 0, 1 -> 1, 0, 0
+    {5, 7}, // 1, 0, 1 -> 1, 1, 1
+    //{5, 1}, // 1, 0, 1 -> 0, 0, 1
+
+    {6, 7}, // 1, 1, 0 -> 1, 1, 1
+    //{6, 4}, // 1, 1, 0 -> 1, 0, 0
+    //{6, 2}, // 1, 1, 0 -> 0, 1, 0
+
+    //{7, 6}, // 1, 1, 1 -> 1, 1, 0
+    //{7, 5}, // 1, 1, 1 -> 1, 0, 1
+    //{7, 3}, // 1, 1, 1 -> 0, 1, 1
+  };
+
+  struct array *model_transform = array_new3(4, 4, sizeof(float));
+  {
+    float v[4][4] = {
+      {1.f, 0.f, 0.f, 0.f},
+      {0.f, 1.f, 0.f, 0.f},
+      {0.f, 0.f, 1.f, 0.f},
+      {0.f, 0.f, 0.f, 1.f}
+    };
+    array_set0(model_transform, v);
+  }
+  printf("model_transform\n");
+  print_mat440(model_transform);
+  printf("\n");
+
+  // main loop
   while (true) {
     SDL_Event event;
     while (SDL_PollEvent(&event))
@@ -72,7 +261,59 @@ int main(void) {
         goto quit;
       }
 
+    SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0xff);
     SDL_RenderClear(renderer);
+
+    // compose device, perspective, camera and model transforms for speed
+    struct array *t0 = array_new3(3, 4, sizeof(float));
+    mul_mat33_mat34(device_transform, perspective_transform, t0);
+    struct array *t1 = array_new3(3, 4, sizeof(float));
+    mul_mat34_mat44(t0, camera_transform, t1);
+    struct array *t2 = array_new3(3, 4, sizeof(float));
+    mul_mat34_mat44(t1, model_transform, t2);
+
+    // transform model points
+    struct array *p = array_new3(8, 3, sizeof(float));
+    for (int i = 0; i < 8; ++i)
+      // taken from mul_mat34_vec4():
+      for (int j = 0; j < 3; ++j) {
+        float c = 0.f;
+        for (int k = 0; k < 4; ++k) {
+          float a, b;
+          array_get2(t2, &a, j, k);
+          array_get2(model_vertices, &b, i, k);
+          c += a * b;
+        }
+        array_set2(p, &c, i, j);
+      }
+
+    // unproject
+    for (int i = 0; i < 8; ++i) {
+      float v[3];
+      array_get1(p, v, i);
+      if (v[2] >= EPSILON) {
+        v[0] /= v[2];
+        v[1] /= v[2];
+        array_set1(p, v, i);
+      }
+    }
+
+    // draw
+    SDL_SetRenderDrawColor(renderer, 0, 0xff, 0, 0xff);
+    for (int i = 0; i < 12; ++i) {
+       float p0[3];
+       float p1[3];
+       array_get1(p, p0, model_lines[i][0]);
+       array_get1(p, p1, model_lines[i][1]);
+       if (p0[2] >= EPSILON && p1[2] >= EPSILON)
+         SDL_RenderDrawLine(renderer, p0[0], p0[1], p1[0], p1[1]);
+    }
+
+    array_free(p);
+    array_free(t2);
+    array_free(t1);
+    array_free(t0);
+
     SDL_RenderPresent(renderer);
     SDL_Delay(1);
   }