/* ISC License */ #include #include #include #include #include #include #include #include #include #include #include #define PI 3.14159265358979323846 #define die(code, msg) do { fprintf(stderr, "pineapple: %s\n", msg); exit(code); } while (0); #define USAGE "pineapple [-v] [-f | -a]" #define dieusage() die(100, "usage: " USAGE) #define ASCII_CHARS "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. " typedef struct { float x, y, z; } vec3; struct __attribute__((__packed__)) triangle { vec3 normal; vec3 vertices[3]; uint16_t _attr_count; /* unused */ }; static inline vec3 vadd(vec3 a, vec3 b) { return (vec3){ a.x + b.x, a.y + b.y, a.z + b.z }; } static inline vec3 vsub(vec3 a, vec3 b) { return (vec3){ a.x - b.x, a.y - b.y, a.z - b.z }; } static inline vec3 smul(vec3 v, float s) { return (vec3){ v.x * s, v.y * s, v.z * s }; } static inline float dot(vec3 a, vec3 b) { return a.x * b.x + a.y * b.y + a.z * b.z; } static inline vec3 cross(vec3 a, vec3 b) { return (vec3){ a.y*b.z - b.y*a.z, a.z*b.x - b.z*a.x, a.x*b.y - b.x*a.y }; } static inline float magnitude(vec3 v) { return sqrt(dot(v, v)); } static inline vec3 vnorm(vec3 v) { float mag = magnitude(v); return (vec3){ v.x / mag, v.y / mag, v.z / mag }; } float ray_triangle_intersect(vec3 P, vec3 d, struct triangle tri) { vec3 n = tri.normal, A = tri.vertices[0], B = tri.vertices[1], C = tri.vertices[2]; d = vnorm(d); n = vnorm(n); if (dot(n, d) == 0.0) return NAN; float t = (dot(n, A) - dot(n, P)) / dot(n, d); vec3 Q = vadd(P, smul(d, t)); if (dot(cross(vsub(B, A), vsub(Q, A)), n) >= 0.0 && dot(cross(vsub(C, B), vsub(Q, B)), n) >= 0.0 && dot(cross(vsub(A, C), vsub(Q, C)), n) >= 0.0) return t; return NAN; } int main(int argc, char *argv[]) { int c; int verbosity = 0; enum { ASCII, FARBFELD } mode = ASCII; while ((c = getopt(argc, argv, "vfa")) > 0) { switch (c) { case 'v': verbosity++; break; case 'f': mode = FARBFELD; break; case 'a': mode = ASCII; break; default: dieusage(); } } argv += optind; argc -= optind; if (argc > 0) dieusage(); const char *stl; { int stl_fd = open("pineapple.stl", O_RDONLY); if (stl_fd < 0) die(111, "fatal: unable to open pineapple.stl"); struct stat stl_stat; if (fstat(stl_fd, &stl_stat) < 0) die(111, "fatal: unable to stat pineapple.stl"); stl = mmap(NULL, stl_stat.st_size, PROT_READ, MAP_PRIVATE, stl_fd, 0); close(stl_fd); if (!stl) die(111, "fatal: unable to mmap pineapple.stl"); } stl += 80; uint32_t len = le32toh(*(uint32_t *)stl); stl += sizeof(uint32_t); const struct triangle *triangles = (const struct triangle *)stl; vec3 camera_pos = { -70.0, 60.0, 60.0 }; float camera_phi = PI * 0.8; float camera_theta = PI * 0.5; float fov = 80.0 * PI / 180.0; vec3 light = { 0.0, 0.0, -1.0 }; uint32_t width = 106; uint32_t height = 53; if (mode == FARBFELD) { uint32_t wbe = htobe32(width); uint32_t hbe = htobe32(height); fwrite("farbfeld", 1, 8, stdout); fwrite(&wbe, 1, 4, stdout); fwrite(&hbe, 1, 4, stdout); } for (int h = height - 1; h >= 0; --h) { if (verbosity >= 1) fprintf(stderr, "row %d/%d\r", height - h - 1, height); for (uint32_t w = 0; w < width; ++w) { float theta = camera_theta - fov/2.0 + fov/height*h; float phi = camera_phi - fov/2.0 + fov/width*w; vec3 dir = { sinf(theta) * cosf(phi), sinf(theta) * sinf(phi), cosf(theta) }; dir = vnorm(dir); float shortest = INFINITY; vec3 shortest_norm = {0}; for (size_t i = 0; i < len; ++i) { float d = ray_triangle_intersect(camera_pos, dir, triangles[i]); if (d != NAN && fabs(d) < fabs(shortest)) { shortest = d; shortest_norm = triangles[i].normal; } } float brightness = 0.0; if (shortest != INFINITY) { float light_angle = acosf(dot(shortest_norm, light) / magnitude(shortest_norm) / magnitude(light)); brightness = 1 - light_angle / PI; } if (mode == ASCII) { printf("%c", ASCII_CHARS[strlen(ASCII_CHARS) - 1 - (int)(brightness * strlen(ASCII_CHARS))]); } else if (mode == FARBFELD) { uint16_t x = htobe16((uint16_t)(brightness * 0xffff)); uint16_t a = htobe16(0xffff); fwrite(&x, 1, 2, stdout); fwrite(&x, 1, 2, stdout); fwrite(&x, 1, 2, stdout); fwrite(&a, 1, 2, stdout); } } if (mode == ASCII) printf("\n"); } return 0; }