viewobj.c 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. #include <stdio.h>
  2. #include <errno.h>
  3. #include <float.h>
  4. #include <limits.h>
  5. #define OLIVEC_IMPLEMENTATION
  6. #include "olive.c"
  7. #define STB_IMAGE_WRITE_IMPLEMENTATION
  8. #include "stb_image_write.h"
  9. #define ARENA_IMPLEMENTATION
  10. #include "arena.h"
  11. #define SV_IMPLEMENTATION
  12. #include "sv.h"
  13. static Arena default_arena = {0};
  14. static Arena *context_arena = &default_arena;
  15. static void *context_alloc(size_t size)
  16. {
  17. assert(context_arena);
  18. return arena_alloc(context_arena, size);
  19. }
  20. static void *context_realloc(void *oldp, size_t oldsz, size_t newsz)
  21. {
  22. if (newsz <= oldsz) return oldp;
  23. return memcpy(context_alloc(newsz), oldp, oldsz);
  24. }
  25. #define FACTOR 120
  26. #define WIDTH (16*FACTOR)
  27. #define HEIGHT (9*FACTOR)
  28. uint32_t pixels[WIDTH*HEIGHT] = {0};
  29. float zbuffer[WIDTH*HEIGHT] = {0};
  30. #define return_defer(value) do { result = (value); goto defer; } while (0)
  31. typedef int Errno;
  32. Errno read_entire_file(const char *file_path, char **buffer, size_t *buffer_size)
  33. {
  34. Errno result = 0;
  35. FILE *f = NULL;
  36. f = fopen(file_path, "rb");
  37. if (f == NULL) return_defer(errno);
  38. if (fseek(f, 0, SEEK_END) < 0) return_defer(errno);
  39. long m = ftell(f);
  40. if (m < 0) return_defer(errno);
  41. if (fseek(f, 0, SEEK_SET) < 0) return_defer(errno);
  42. *buffer_size = m;
  43. *buffer = context_alloc(*buffer_size);
  44. fread(*buffer, *buffer_size, 1, f);
  45. if (ferror(f)) return_defer(errno);
  46. defer:
  47. if (f) fclose(f);
  48. return result;
  49. }
  50. typedef struct {
  51. float x, y;
  52. } Vector2;
  53. Vector2 make_vector2(float x, float y)
  54. {
  55. Vector2 v2;
  56. v2.x = x;
  57. v2.y = y;
  58. return v2;
  59. }
  60. typedef struct {
  61. float x, y, z;
  62. } Vector3;
  63. Vector3 make_vector3(float x, float y, float z)
  64. {
  65. Vector3 v3;
  66. v3.x = x;
  67. v3.y = y;
  68. v3.z = z;
  69. return v3;
  70. }
  71. Vector2 project_3d_2d(Vector3 v3)
  72. {
  73. return make_vector2(v3.x / v3.z, v3.y / v3.z);
  74. }
  75. Vector2 project_2d_scr(Vector2 v2)
  76. {
  77. return make_vector2((v2.x + 1)/2*WIDTH, (1 - (v2.y + 1)/2)*HEIGHT);
  78. }
  79. typedef struct {
  80. Vector3 *items;
  81. size_t capacity;
  82. size_t count;
  83. } Vertices;
  84. typedef struct {
  85. int a, b, c;
  86. } Face;
  87. Face make_face(int a, int b, int c)
  88. {
  89. Face f = {
  90. .a = a,
  91. .b = b,
  92. .c = c,
  93. };
  94. return f;
  95. }
  96. typedef struct {
  97. Face *items;
  98. size_t capacity;
  99. size_t count;
  100. } Faces;
  101. #define DA_INIT_CAPACITY 8192
  102. #define DA_REALLOC context_realloc
  103. #define da_append(da, item) \
  104. do { \
  105. if ((da)->count >= (da)->capacity) { \
  106. size_t new_capacity = (da)->capacity*2; \
  107. if (new_capacity == 0) { \
  108. new_capacity = DA_INIT_CAPACITY; \
  109. } \
  110. \
  111. (da)->items = DA_REALLOC((da)->items, \
  112. (da)->capacity*sizeof((da)->items[0]), \
  113. new_capacity*sizeof((da)->items[0])); \
  114. (da)->capacity = new_capacity; \
  115. } \
  116. \
  117. (da)->items[(da)->count++] = (item); \
  118. } while (0)
  119. #define UNUSED(x) (void)(x)
  120. Vector3 remap_teapot(Vector3 v, float lx, float hx, float ly, float hy, float lz, float hz)
  121. {
  122. v.z = (v.z - lz)/(hz - lz) + 1;
  123. v.x = (v.x - lx)/(hx - lx)*2 - 1;
  124. v.y = (v.y - ly)/(hy - ly)*2 - 1;
  125. return v;
  126. }
  127. int main(void)
  128. {
  129. int result = 0;
  130. const char *obj_file_path = "teapot.obj";
  131. char *buffer;
  132. size_t buffer_size;
  133. Errno err = read_entire_file(obj_file_path, &buffer, &buffer_size);
  134. if (err != 0) {
  135. fprintf(stderr, "ERROR: could not read file %s: %s\n", obj_file_path, strerror(errno));
  136. return_defer(1);
  137. }
  138. String_View content = sv_from_parts(buffer, buffer_size);
  139. Vertices vertices = {0};
  140. Faces faces = {0};
  141. float lx = FLT_MAX, hx = FLT_MIN;
  142. float ly = FLT_MAX, hy = FLT_MIN;
  143. float lz = FLT_MAX, hz = FLT_MIN;
  144. int lf = INT_MAX, hf = INT_MIN;
  145. for (size_t line_number = 0; content.count > 0; ++line_number) {
  146. String_View line = sv_trim_left(sv_chop_by_delim(&content, '\n'));
  147. if (line.count > 0) {
  148. String_View kind = sv_chop_by_delim(&line, ' ');
  149. if (sv_eq(kind, SV("v"))) {
  150. char *endptr;
  151. line = sv_trim_left(line);
  152. float x = strtof(line.data, &endptr);
  153. line.data = endptr;
  154. if (lx > x) lx = x;
  155. if (hx < x) hx = x;
  156. line = sv_trim_left(line);
  157. float y = strtof(line.data, &endptr);
  158. line.data = endptr;
  159. if (ly > y) ly = y;
  160. if (hy < y) hy = y;
  161. line = sv_trim_left(line);
  162. float z = strtof(line.data, &endptr);
  163. line.data = endptr;
  164. if (lz > z) lz = z;
  165. if (hz < z) hz = z;
  166. da_append(&vertices, make_vector3(x, y, z));
  167. } else if (sv_eq(kind, SV("f"))) {
  168. char *endptr;
  169. line = sv_trim_left(line);
  170. int a = strtol(line.data, &endptr, 10);
  171. line.data = endptr;
  172. if (lf > a) lf = a;
  173. if (hf < a) hf = a;
  174. line = sv_trim_left(line);
  175. int b = strtol(line.data, &endptr, 10);
  176. line.data = endptr;
  177. if (lf > b) lf = b;
  178. if (hf < b) hf = b;
  179. line = sv_trim_left(line);
  180. int c = strtol(line.data, &endptr, 10);
  181. line.data = endptr;
  182. if (lf > c) lf = c;
  183. if (hf < c) hf = c;
  184. da_append(&faces, make_face(a, b, c));
  185. } else {
  186. fprintf(stderr, "%s:%zu: unknown kind of entry `"SV_Fmt"`\n", obj_file_path, line_number, SV_Arg(kind));
  187. return_defer(1);
  188. }
  189. }
  190. }
  191. printf("Vertices: %zu (x: %f..%f, y: %f..%f, z: %f..%f)\n", vertices.count, lx, hx, ly, hy, lz, hz);
  192. printf("Faces: %zu (index: %d..%d)\n", faces.count, lf, hf);
  193. Olivec_Canvas oc = olivec_canvas(pixels, WIDTH, HEIGHT, WIDTH);
  194. olivec_fill(oc, 0xFF202020);
  195. for (size_t i = 0; i < faces.count; ++i) {
  196. Face f = faces.items[i];
  197. Vector3 v1 = remap_teapot(vertices.items[f.a - 1], lx, hx, ly, hy, lz, hz);
  198. Vector3 v2 = remap_teapot(vertices.items[f.b - 1], lx, hx, ly, hy, lz, hz);
  199. Vector3 v3 = remap_teapot(vertices.items[f.c - 1], lx, hx, ly, hy, lz, hz);
  200. Vector2 p1 = project_2d_scr(project_3d_2d(v1));
  201. Vector2 p2 = project_2d_scr(project_3d_2d(v2));
  202. Vector2 p3 = project_2d_scr(project_3d_2d(v3));
  203. Olivec_Tri tri = olivec_tri_new(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y);
  204. int y1, y2;
  205. if (olivec_tri_vert(&tri, HEIGHT, &y1, &y2)) {
  206. for (int y = y1; y <= y2; ++y) {
  207. int x1, x2;
  208. if (olivec_tri_horz(&tri, WIDTH, y, &x1, &x2)) {
  209. for (int x = x1; x <= x2; ++x) {
  210. int u1, u2, det;
  211. olivec_tri_bary(&tri, x, y, &u1, &u2, &det);
  212. float z = 1/v1.z*u1/det + 1/v2.z*u2/det + 1/v3.z*(det - u1 - u2)/det;
  213. if (z > zbuffer[y*WIDTH + x]) {
  214. zbuffer[y*WIDTH + x] = z;
  215. OLIVEC_PIXEL(oc, x, y) = mix_colors3(0xFF1818FF, 0xFF18FF18, 0xFFFF1818, u1, u2, det);
  216. z = 1.0f/z;
  217. if (z >= 1.0) {
  218. z -= 1.0;
  219. uint32_t v = z*255;
  220. if (v > 255) v = 255;
  221. olivec_blend_color(&OLIVEC_PIXEL(oc, x, y), (v<<(3*8)));
  222. }
  223. }
  224. }
  225. }
  226. }
  227. }
  228. }
  229. const char *file_path = "teapot.png";
  230. if (!stbi_write_png(file_path, WIDTH, HEIGHT, 4, pixels, sizeof(uint32_t)*WIDTH)) {
  231. fprintf(stderr, "ERROR: could not write file %s\n", file_path);
  232. return_defer(1);
  233. }
  234. defer:
  235. arena_free(&default_arena);
  236. return result;
  237. }