Browse Source

Fix barycentric overflow bug

rexim 2 years ago
parent
commit
444d40e34b
5 changed files with 40 additions and 14 deletions
  1. 31 9
      olive.c
  2. 2 3
      test.c
  3. BIN
      test/barycentric_overflow_expected.png
  4. BIN
      test/triangle_order_flip_expected.png
  5. 7 2
      viewobj.c

+ 31 - 9
olive.c

@@ -619,15 +619,37 @@ OLIVECDEF void barycentric(int x1, int y1, int x2, int y2, int x3, int y3,
 OLIVECDEF void olivec_triangle3c(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3,
 OLIVECDEF void olivec_triangle3c(Olivec_Canvas oc, int x1, int y1, int x2, int y2, int x3, int y3,
                                  uint32_t c1, uint32_t c2, uint32_t c3)
                                  uint32_t c1, uint32_t c2, uint32_t c3)
 {
 {
-    Olivec_Tri tri = olivec_tri_new(x1, y1, x2, y2, x3, y3);
-    if (olivec_tri_vert(&tri, oc.height, &y1, &y2)) {
-        for (int y = y1; y <= y2; ++y) {
-            if (olivec_tri_horz(&tri, oc.width, y, &x1, &x2)) {
-                for (int x = x1; x <= x2; ++x) {
-                    int u1, u2, det;
-                    olivec_tri_bary(&tri, x, y, &u1, &u2, &det);
-                    olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), mix_colors3(c1, c2, c3, u1, u2, det));
-                }
+    int lx = x1;
+    int hx = x1;
+    if (lx > x2) lx = x2;
+    if (lx > x3) lx = x3;
+    if (hx < x2) hx = x2;
+    if (hx < x3) hx = x3;
+    if (lx < 0) lx = 0;
+    if ((size_t) lx >= oc.width) return;
+    if (hx < 0) return;
+    if ((size_t) hx >= oc.width) hx = oc.width-1;
+
+    int ly = y1;
+    int hy = y1;
+    if (ly > y2) ly = y2;
+    if (ly > y3) ly = y3;
+    if (hy < y2) hy = y2;
+    if (hy < y3) hy = y3;
+    if (ly < 0) ly = 0;
+    if ((size_t) ly >= oc.height) return;
+    if (hy < 0) return;
+    if ((size_t) hy >= oc.height) hy = oc.height-1;
+
+    for (int y = ly; y <= hy; ++y) {
+        for (int x = lx; x <= hx; ++x) {
+            int u1, u2, det;
+            barycentric(x1, y1, x2, y2, x3, y3, x, y, &u1, &u2, &det);
+            int u3 = det - u1 - u2;
+            if ((OLIVEC_SIGN(int, u1) == OLIVEC_SIGN(int, det) || u1 == 0) &&
+                (OLIVEC_SIGN(int, u2) == OLIVEC_SIGN(int, det) || u2 == 0) &&
+                (OLIVEC_SIGN(int, u3) == OLIVEC_SIGN(int, det) || u3 == 0)) {
+                OLIVEC_PIXEL(oc, x, y) = mix_colors3(c1, c2, c3, u1, u2, det);
             }
             }
         }
         }
     }
     }

+ 2 - 3
test.c

@@ -564,7 +564,7 @@ Olivec_Canvas test_sprite_blend_vs_copy(void)
     return dst;
     return dst;
 }
 }
 
 
-Olivec_Canvas test_weird_triangle_bug(void)
+Olivec_Canvas test_barycentric_overflow(void)
 {
 {
     size_t w = 256;
     size_t w = 256;
     size_t h = 256;
     size_t h = 256;
@@ -613,8 +613,7 @@ Test_Case test_cases[] = {
     DEFINE_TEST_CASE(sprite_blend_null),
     DEFINE_TEST_CASE(sprite_blend_null),
     DEFINE_TEST_CASE(sprite_blend_vs_copy),
     DEFINE_TEST_CASE(sprite_blend_vs_copy),
     DEFINE_TEST_CASE(triangle_order_flip),
     DEFINE_TEST_CASE(triangle_order_flip),
-    // TODO: fix weird_triangle_bug
-    DEFINE_TEST_CASE(weird_triangle_bug)
+    DEFINE_TEST_CASE(barycentric_overflow)
 };
 };
 #define TEST_CASES_COUNT (sizeof(test_cases)/sizeof(test_cases[0]))
 #define TEST_CASES_COUNT (sizeof(test_cases)/sizeof(test_cases[0]))
 
 

BIN
test/barycentric_overflow_expected.png


BIN
test/triangle_order_flip_expected.png


+ 7 - 2
viewobj.c

@@ -154,11 +154,16 @@ Vector3 remap_teapot(Vector3 v, float lx, float hx, float ly, float hy, float lz
     return v;
     return v;
 }
 }
 
 
-int main(void)
+int main(int argc, char **argv)
 {
 {
     int result = 0;
     int result = 0;
 
 
-    const char *obj_file_path = "teapot.obj";
+    if (argc < 2) {
+        fprintf(stderr, "ERROR: no input file is provided\n");
+        return_defer(1);
+    }
+
+    const char *obj_file_path = argv[1];
     char *buffer;
     char *buffer;
     size_t buffer_size;
     size_t buffer_size;
     Errno err = read_entire_file(obj_file_path, &buffer, &buffer_size);
     Errno err = read_entire_file(obj_file_path, &buffer, &buffer_size);