diff options
Diffstat (limited to '')
| -rw-r--r-- | tb/models/gfx.py | 128 |
1 files changed, 128 insertions, 0 deletions
diff --git a/tb/models/gfx.py b/tb/models/gfx.py new file mode 100644 index 0000000..c43a315 --- /dev/null +++ b/tb/models/gfx.py @@ -0,0 +1,128 @@ +import math, sys +from PIL import Image + +W, H = 640, 480 +FILL = (0, 0, 0) + +fb = None + +def fixed_color(color): + for c in color[:3]: + c = abs(int(c * (1 << 8))) + yield (c & 255) | (255 if (c & (1 << 8)) else 0) + +def fixed(p): + pos, color = p + w = pos + pos = tuple(int(c / pos[3] * (1 << 13)) for c in pos[:3]) + return (pos, tuple(fixed_color(color))) + +def bounding(p0, p1, p2): + minx = max(min(p0[0][0], p1[0][0], p2[0][0]), (-W // 2) << 4) + maxx = min(max(p0[0][0], p1[0][0], p2[0][0]), (W // 2) << 4) + miny = max(min(p0[0][1], p1[0][1], p2[0][1]), (-H // 2) << 4) + maxy = min(max(p0[0][1], p1[0][1], p2[0][1]), (H // 2) << 4) + return (minx, miny, maxx, maxy) + +def edge_fn(p, q, sx, sy): + p, q = p[0], q[0] + + # https://www.scratchapixel.com/lessons/3d-basic-rendering/rasterization-practical-implementation/rasterization-stage.html + a, b = p[1] - q[1], -(p[0] - q[0]) + r = (sx - q[0]) * a + (sy - q[1]) * b + return r, r, a, b + +def raster(p0, p1, p2, msaa=False): + global fb + + minx, miny, maxx, maxy = bounding(p0, p1, p2) + + sx, sy = minx // 16 * 16, miny // 16 * 16 + + base_x_a, base_y_a, add_x_a, add_y_a = edge_fn(p0, p1, sx, sy) + base_x_b, base_y_b, add_x_b, add_y_b = edge_fn(p1, p2, sx, sy) + base_x_c, base_y_c, add_x_c, add_y_c = edge_fn(p2, p0, sx, sy) + + int_x_a = add_x_a << 4 + int_x_b = add_x_b << 4 + int_x_c = add_x_c << 4 + int_y_a = add_y_a << 4 + int_y_b = add_y_b << 4 + int_y_c = add_y_c << 4 + + if msaa: + samples = ((-5, 10), (11, 2), (-7, -4)) + else: + samples = ((0, 0),) + + samples_a = tuple(add_x_a * sx + add_y_a * sy for sx, sy in samples) + samples_b = tuple(add_x_b * sx + add_y_b * sy for sx, sy in samples) + samples_c = tuple(add_x_c * sx + add_y_c * sy for sx, sy in samples) + + for x in range(minx // 16 * 16, maxx + 16, 16): + for y in range(miny // 16 * 16, maxy + 16, 16): + count = 0 + + for a, b, c in zip(samples_a, samples_b, samples_c): + b0 = base_y_a + a + b1 = base_y_b + b + b2 = base_y_c + c + if b0 >= 0 and b1 >= 0 and b2 >= 0: + count += 1 + + if count > 0: + s = b0 + b1 + b2 + b0 /= s + b1 /= s + b2 /= s + yield (x >> 4, y >> 4, b0, b1, b2) + + base_y_a += int_y_a + base_y_b += int_y_b + base_y_c += int_y_c + + base_x_a += int_x_a + base_x_b += int_x_b + base_x_c += int_x_c + + base_y_a = base_x_a + base_y_b = base_x_b + base_y_c = base_x_c + +def backend(): + tri = (((-5 / 320, -5 / 240, 1.0, 1.0), (0.0, 0.0, 0.0, 1.0)), + ((-50/320, -5/240, 1.0, 1.0), (0.0, 1.0, 0.0, 0.0)), + ((-20/320, -70/240, 1.0, 1.0), (0.0, 0.0, 1.0, 0.0))) + + t = tri + + tri = tuple(fixed(p) for p in tri) + for x, y, b0, b1, b2 in raster(*tri): + if -W // 2 <= x < W // 2 and -H // 2 <= y < H // 2: + r = b0 * t[0][1][1] + b1 * t[1][1][1] + b2 * t[2][1][1] + g = b0 * t[0][1][2] + b1 * t[1][1][2] + b2 * t[2][1][2] + b = b0 * t[0][1][3] + b1 * t[1][1][3] + b2 * t[2][1][3] + + x += W // 2 + y = H // 2 - y - 1 + fb[y * W + x] = (int(r * 255), int(g * 255), int(b * 255)) + +imgs = [] + +def do_frame(): + global fb, imgs + fb = [FILL] * W * H + backend() + + image = Image.new('RGB', (W, H)) + image.putdata(fb) + imgs.append(image) + +for n in range(1): + do_frame() + +imgs[0].save('out.gif', + save_all=True, + append_images=imgs[1:], + duration=100, + loop=0) |
