]> Dogcows Code - chaz/rasterize/blob - model.c
add texture mapping with perspective correction
[chaz/rasterize] / model.c
1
2 /*
3 * CS5600 University of Utah
4 * Charles McGarvey
5 * mcgarvey@eng.utah.edu
6 */
7
8 #include <errno.h>
9
10 #include "array.h"
11 #include "model.h"
12 #include "raster.h"
13 #include "tri.h"
14
15
16 // create an interface for a vector array
17 DEFINE_ARRAY_TYPE(vec);
18
19
20 struct model
21 {
22 list_t* triangles;
23 mat_t model;
24 color_t specular;
25 scal_t shininess;
26 const raster_t* texture;
27 char* name;
28 int count;
29 };
30
31
32 static int _model_read_raw(model_t* m, const char* filename);
33 static int _model_read_obj(model_t* m, const char* filename);
34
35 static int _model_try_read_cache(const char* filename, list_t** l);
36 static void _model_write_cache(const char* filename, int count, list_t* l);
37
38
39 model_t* model_alloc(const char* filename)
40 {
41 int type = 0;
42 char* ext = strrchr(filename, '.');
43 if (ext == NULL) {
44 goto fail;
45 }
46 ++ext;
47
48 if (strcmp(ext, "raw") == 0) {
49 return model_alloc2(filename, MODEL_TYPE_RAW);
50 }
51 if (strcmp(ext, "obj") == 0) {
52 return model_alloc2(filename, MODEL_TYPE_OBJ);
53 }
54
55 fail:
56 fprintf(stderr, "Unknown file type: %s", filename);
57 return NULL;
58 }
59
60 model_t* model_alloc2(const char* filename, int type)
61 {
62 model_t* m = (model_t*)mem_alloc(sizeof(model_t));
63 m->triangles = NULL;
64 m->model = MAT_IDENTITY;
65 m->specular = COLOR_BLACK;
66 m->shininess = S(64.0);
67 m->texture = NULL;
68 m->name = mem_strdup(filename);
69 m->count = 0;
70
71 #if CACHE_GEOMETRY
72 m->count = _model_try_read_cache(filename, &m->triangles);
73 if (0 < m->count) {
74 return m;
75 }
76 #endif
77
78 int load;
79 switch (type) {
80 case MODEL_TYPE_RAW:
81 load = _model_read_raw(m, filename);
82 break;
83 case MODEL_TYPE_OBJ:
84 load = _model_read_obj(m, filename);
85 break;
86 }
87 if (load != 0) {
88 model_destroy(m);
89 return NULL;
90 }
91
92 #if CACHE_GEOMETRY
93 _model_write_cache(filename, m->count, m->triangles);
94 #endif
95
96 return m;
97 }
98
99 void model_destroy(model_t* m)
100 {
101 list_destroy(&m->triangles);
102 if (m->texture) {
103 raster_destroy((raster_t*)m->texture);
104 }
105 mem_free(m->name);
106 mem_free(m);
107 }
108
109
110 const list_t* model_geometry(const model_t* m)
111 {
112 return m->triangles;
113 }
114
115 int model_size(const model_t* m)
116 {
117 return m->count;
118 }
119
120 const char* model_name(const model_t* m)
121 {
122 return m->name;
123 }
124
125 color_t model_specular(const model_t* m)
126 {
127 return m->specular;
128 }
129
130 scal_t model_shininess(const model_t* m)
131 {
132 return m->shininess;
133 }
134
135 void model_transformation(const model_t* m, mat_t* transform)
136 {
137 *transform = m->model;
138 }
139
140 color_t model_tcolor(const model_t* m, vec_t pt)
141 {
142 if (m->texture) {
143 return raster_color(m->texture, pt);
144 }
145 return COLOR_WHITE;
146 }
147
148
149 void model_transform(model_t* m, const mat_t* transform)
150 {
151 m->model = mat_mult(m->model, *transform);
152 }
153
154 void model_material(model_t* m, color_t specular, scal_t shininess)
155 {
156 m->specular = specular;
157 m->shininess = shininess;
158 }
159
160 void model_texture(model_t* m, const void* p)
161 {
162 m->texture = (const raster_t*)p;
163 }
164
165
166 #if CALC_NORMALS
167
168 #include "map.h"
169 DEFINE_MAP_TYPE3(vec_t, list_t*, vnorm, vec_compare(*a, *b));
170
171
172 /*
173 * Associate a triangle with one of its vertices.
174 */
175 static void _find_normals_add_vertex(map_t* m, vec_t v, tri_t* t)
176 {
177 list_t** l = map_vnorm_search(m, v);
178 if (l == NULL) {
179 map_vnorm_data_t* d = map_vnorm_insert(m, v, NULL);
180 l = &d->val;
181 }
182 list_push(l, t);
183 }
184
185 /*
186 * Associate a triangle with all of its vertices.
187 */
188 static void _find_normals_add_triangle(map_t* m, tri_t* t)
189 {
190 _find_normals_add_vertex(m, t->a.v, t);
191 _find_normals_add_vertex(m, t->b.v, t);
192 _find_normals_add_vertex(m, t->c.v, t);
193 }
194
195 /*
196 * Calculate an averaged normal from a list of triangles that share a common
197 * vertex.
198 */
199 static void _find_normals_average(const vec_t* v, list_t** l)
200 {
201 // first, compute the average normal
202 vec_t n = VEC_ZERO;
203 for (list_t* i = *l; i; i = i->link) {
204 tri_t* t = (tri_t*)i->val;
205 n = vec_add(n, tri_normal(*t));
206 }
207 n = vec_normalize(n);
208
209 // set the normal on each triangle's vertex that is shared
210 while (*l) {
211 tri_t* t = (tri_t*)(*l)->val;
212 if (vec_isequal(*v, t->a.v)) {
213 t->a.n = n;
214 }
215 else if (vec_isequal(*v, t->b.v)) {
216 t->b.n = n;
217 }
218 else if (vec_isequal(*v, t->c.v)) {
219 t->c.n = n;
220 }
221 list_pop(l);
222 }
223 }
224
225 #endif // CALC_NORMALS
226
227
228 static int _model_read_raw(model_t* m, const char* filename)
229 {
230 FILE* file = fopen(filename, "r");
231 if (file == NULL) {
232 fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
233 return -1;
234 }
235
236 #if CALC_NORMALS
237 map_t* nlookup = map_vnorm_alloc();
238 #endif
239
240 double x1, y1, z1, x2, y2, z2, x3, y3, z3;
241 while (fscanf(file, " %lf %lf %lf %lf %lf %lf %lf %lf %lf",
242 &x1, &y1, &z1, &x2, &y2, &z2, &x3, &y3, &z3) == 9) {
243 tri_t* t = tri_alloc(
244 vert_new2((scal_t)x1, (scal_t)y1, (scal_t)z1),
245 vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
246 vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
247 );
248 list_push2(&m->triangles, t, mem_free);
249 ++m->count;
250
251 #if CALC_NORMALS
252 _find_normals_add_triangle(nlookup, t);
253 #else
254 vec_t n = vec_normalize(tri_normal(*t));
255 t->a.n = n;
256 t->b.n = n;
257 t->c.n = n;
258 #endif
259 }
260
261 #if CALC_NORMALS
262 map_vnorm_call(nlookup, _find_normals_average);
263 rbtree_destroy(nlookup);
264 #endif
265
266 fclose(file);
267 if (m->triangles == NULL) {
268 fprintf(stderr, "No triangles read from %s\n", filename);
269 return -1;
270 }
271 return 0;
272 }
273
274 static int _model_read_obj(model_t* m, const char* filename)
275 {
276 FILE* file = fopen(filename, "r");
277 if (file == NULL) {
278 fprintf(stderr, "Cannot read %s: %s\n", filename, strerror(errno));
279 return -1;
280 }
281
282 #if CALC_NORMALS
283 map_t* nlookup = map_vnorm_alloc();
284 #endif
285
286 array_t* v = array_vec_alloc();
287 array_t* vt = array_vec_alloc();
288 array_t* vn = array_vec_alloc();
289
290 array_vec_push(v, VEC_ZERO);
291 array_vec_push(vt, VEC_ZERO);
292 array_vec_push(vn, VEC_ZERO);
293
294 char line[4096];
295 while (fgets(line, 4096, file)) {
296 char name[4096];
297 double f1, f2, f3, f4;
298 int i1, i2, i3, i4, i5, i6, i7, i8, i9;
299 if (sscanf(line, "v %lf %lf %lf %lf ", &f1, &f2, &f3, &f4) == 4) {
300 array_vec_push(v, vec_new2((scal_t)f1, (scal_t)f2, (scal_t)f3, (scal_t)f4));
301 }
302 else if (sscanf(line, "v %lf %lf %lf ", &f1, &f2, &f3) == 3) {
303 array_vec_push(v, vec_new((scal_t)f1, (scal_t)f2, (scal_t)f3));
304 }
305 else if (sscanf(line, "vt %lf %lf ", &f1, &f2) == 2) {
306 array_vec_push(vt, vec_new((scal_t)f1, (scal_t)f2, S(0.0)));
307 }
308 else if (sscanf(line, "vn %lf %lf %lf ", &f1, &f2, &f3) == 3) {
309 array_vec_push(vn, vec_new((scal_t)f1, (scal_t)f2, (scal_t)f3));
310 }
311 else if (sscanf(line, "f %d %d %d ", &i1, &i2, &i3) == 3) {
312 tri_t* t = tri_alloc(
313 vert_new(*array_vec_index(v, i1)),
314 vert_new(*array_vec_index(v, i2)),
315 vert_new(*array_vec_index(v, i3))
316 );
317 list_push2(&m->triangles, t, mem_free);
318 ++m->count;
319
320 #if CALC_NORMALS
321 _find_normals_add_triangle(nlookup, t);
322 #else
323 vec_t n = vec_normalize(tri_normal(*t));
324 t->a.n = n;
325 t->b.n = n;
326 t->c.n = n;
327 #endif
328 }
329 else if (sscanf(line, "f %d/%d %d/%d %d/%d ", &i1, &i2, &i3, &i4, &i5, &i6) == 6) {
330 tri_t* t = tri_alloc(
331 vert_new(*array_vec_index(v, i1)),
332 vert_new(*array_vec_index(v, i3)),
333 vert_new(*array_vec_index(v, i5))
334 );
335 list_push2(&m->triangles, t, mem_free);
336 ++m->count;
337 t->a.t = *array_vec_index(vt, i2);
338 t->b.t = *array_vec_index(vt, i4);
339 t->c.t = *array_vec_index(vt, i6);
340 #if CALC_NORMALS
341 _find_normals_add_triangle(nlookup, t);
342 #else
343 vec_t n = vec_normalize(tri_normal(*t));
344 t->a.n = n;
345 t->b.n = n;
346 t->c.n = n;
347 #endif
348 }
349 else if (sscanf(line, "f %d//%d %d//%d %d//%d ",
350 &i1, &i2, &i3, &i4, &i5, &i6) == 6) {
351 tri_t* t = tri_alloc(
352 vert_new(*array_vec_index(v, i1)),
353 vert_new(*array_vec_index(v, i3)),
354 vert_new(*array_vec_index(v, i5))
355 );
356 list_push2(&m->triangles, t, mem_free);
357 ++m->count;
358 t->a.n = *array_vec_index(vn, i2);
359 t->b.n = *array_vec_index(vn, i4);
360 t->c.n = *array_vec_index(vn, i6);
361 }
362 else if (sscanf(line, "f %d/%d/%d %d/%d/%d %d/%d/%d ",
363 &i1, &i2, &i3, &i4, &i5, &i6, &i7, &i8, &i9) == 9) {
364 tri_t* t = tri_alloc(
365 vert_new(*array_vec_index(v, i1)),
366 vert_new(*array_vec_index(v, i4)),
367 vert_new(*array_vec_index(v, i7))
368 );
369 list_push2(&m->triangles, t, mem_free);
370 ++m->count;
371 t->a.t = *array_vec_index(vt, i2);
372 t->b.t = *array_vec_index(vt, i5);
373 t->c.t = *array_vec_index(vt, i8);
374 t->a.n = *array_vec_index(vn, i3);
375 t->b.n = *array_vec_index(vn, i6);
376 t->c.n = *array_vec_index(vn, i9);
377 }
378 }
379
380 array_destroy(v);
381 array_destroy(vt);
382 array_destroy(vn);
383
384 #if CALC_NORMALS
385 map_vnorm_call(nlookup, _find_normals_average);
386 rbtree_destroy(nlookup);
387 #endif
388
389 fclose(file);
390 if (m->triangles == NULL) {
391 fprintf(stderr, "No triangles read from %s\n", filename);
392 return -1;
393 }
394 return 0;
395 }
396
397
398 #define _DO_OR_DONE(X) if ((X) <= 0) goto done
399
400 /*
401 * Try to read the triangle geometry from the cache file.
402 */
403 static int _model_try_read_cache(const char* filename, list_t** l)
404 {
405 int count = 0;
406 char* cachename = mem_strcat(".", filename);
407 FILE* file = fopen(cachename, "rb");
408 if (file == NULL) {
409 goto done;
410 }
411
412 float x1, y1, z1, x2, y2, z2, x3, y3, z3;
413 while (1) {
414 _DO_OR_DONE(fread(&x1, sizeof(float), 1, file));
415 _DO_OR_DONE(fread(&y1, sizeof(float), 1, file));
416 _DO_OR_DONE(fread(&z1, sizeof(float), 1, file));
417 _DO_OR_DONE(fread(&x2, sizeof(float), 1, file));
418 _DO_OR_DONE(fread(&y2, sizeof(float), 1, file));
419 _DO_OR_DONE(fread(&z2, sizeof(float), 1, file));
420 _DO_OR_DONE(fread(&x3, sizeof(float), 1, file));
421 _DO_OR_DONE(fread(&y3, sizeof(float), 1, file));
422 _DO_OR_DONE(fread(&z3, sizeof(float), 1, file));
423 tri_t* t = tri_alloc(
424 vert_new2((scal_t)x1, (scal_t)y1, (scal_t)z1),
425 vert_new2((scal_t)x2, (scal_t)y2, (scal_t)z2),
426 vert_new2((scal_t)x3, (scal_t)y3, (scal_t)z3)
427 );
428 ++count;
429 list_push2(l, t, mem_free);
430 _DO_OR_DONE(fread(&x1, sizeof(float), 1, file));
431 _DO_OR_DONE(fread(&y1, sizeof(float), 1, file));
432 _DO_OR_DONE(fread(&z1, sizeof(float), 1, file));
433 _DO_OR_DONE(fread(&x2, sizeof(float), 1, file));
434 _DO_OR_DONE(fread(&y2, sizeof(float), 1, file));
435 _DO_OR_DONE(fread(&z2, sizeof(float), 1, file));
436 _DO_OR_DONE(fread(&x3, sizeof(float), 1, file));
437 _DO_OR_DONE(fread(&y3, sizeof(float), 1, file));
438 _DO_OR_DONE(fread(&z3, sizeof(float), 1, file));
439 t->a.n = vec_new((scal_t)x1, (scal_t)y1, (scal_t)z1);
440 t->b.n = vec_new((scal_t)x2, (scal_t)y2, (scal_t)z2);
441 t->c.n = vec_new((scal_t)x3, (scal_t)y3, (scal_t)z3);
442 _DO_OR_DONE(fread(&x1, sizeof(float), 1, file));
443 _DO_OR_DONE(fread(&y1, sizeof(float), 1, file));
444 _DO_OR_DONE(fread(&x2, sizeof(float), 1, file));
445 _DO_OR_DONE(fread(&y2, sizeof(float), 1, file));
446 _DO_OR_DONE(fread(&x3, sizeof(float), 1, file));
447 _DO_OR_DONE(fread(&y3, sizeof(float), 1, file));
448 t->a.t = vec_new((scal_t)x1, (scal_t)y1, S(0.0));
449 t->b.t = vec_new((scal_t)x2, (scal_t)y2, S(0.0));
450 t->c.t = vec_new((scal_t)x3, (scal_t)y3, S(0.0));
451 }
452
453 done:
454 mem_free(cachename);
455 if (file != NULL) {
456 fclose(file);
457 }
458
459 return count;
460 }
461
462 /*
463 * Write the triangle data to the cache.
464 */
465 static void _model_write_cache(const char* filename, int count, list_t* l)
466 {
467 char* cachename = mem_strcat(".", filename);
468 FILE* file = fopen(cachename, "wb");
469 if (file == NULL) {
470 fprintf(stderr, "Cannot write %s: %s\n", cachename, strerror(errno));
471 goto done;
472 }
473
474 float x1, y1, z1, x2, y2, z2, x3, y3, z3;
475 for (list_t* i = l; i; i = i->link) {
476 tri_t* t = (tri_t*)i->val;
477 x1 = (float)t->a.v.x; y1 = (float)t->a.v.y; z1 = (float)t->a.v.z;
478 x2 = (float)t->b.v.x; y2 = (float)t->b.v.y; z2 = (float)t->b.v.z;
479 x3 = (float)t->c.v.x; y3 = (float)t->c.v.y; z3 = (float)t->c.v.z;
480 _DO_OR_DONE(fwrite(&x1, sizeof(float), 1, file));
481 _DO_OR_DONE(fwrite(&y1, sizeof(float), 1, file));
482 _DO_OR_DONE(fwrite(&z1, sizeof(float), 1, file));
483 _DO_OR_DONE(fwrite(&x2, sizeof(float), 1, file));
484 _DO_OR_DONE(fwrite(&y2, sizeof(float), 1, file));
485 _DO_OR_DONE(fwrite(&z2, sizeof(float), 1, file));
486 _DO_OR_DONE(fwrite(&x3, sizeof(float), 1, file));
487 _DO_OR_DONE(fwrite(&y3, sizeof(float), 1, file));
488 _DO_OR_DONE(fwrite(&z3, sizeof(float), 1, file));
489 x1 = (float)t->a.n.x; y1 = (float)t->a.n.y; z1 = (float)t->a.n.z;
490 x2 = (float)t->b.n.x; y2 = (float)t->b.n.y; z2 = (float)t->b.n.z;
491 x3 = (float)t->c.n.x; y3 = (float)t->c.n.y; z3 = (float)t->c.n.z;
492 _DO_OR_DONE(fwrite(&x1, sizeof(float), 1, file));
493 _DO_OR_DONE(fwrite(&y1, sizeof(float), 1, file));
494 _DO_OR_DONE(fwrite(&z1, sizeof(float), 1, file));
495 _DO_OR_DONE(fwrite(&x2, sizeof(float), 1, file));
496 _DO_OR_DONE(fwrite(&y2, sizeof(float), 1, file));
497 _DO_OR_DONE(fwrite(&z2, sizeof(float), 1, file));
498 _DO_OR_DONE(fwrite(&x3, sizeof(float), 1, file));
499 _DO_OR_DONE(fwrite(&y3, sizeof(float), 1, file));
500 _DO_OR_DONE(fwrite(&z3, sizeof(float), 1, file));
501 x1 = (float)t->a.t.x; y1 = (float)t->a.t.y;
502 x2 = (float)t->b.t.x; y2 = (float)t->b.t.y;
503 x3 = (float)t->c.t.x; y3 = (float)t->c.t.y;
504 _DO_OR_DONE(fwrite(&x1, sizeof(float), 1, file));
505 _DO_OR_DONE(fwrite(&y1, sizeof(float), 1, file));
506 _DO_OR_DONE(fwrite(&x2, sizeof(float), 1, file));
507 _DO_OR_DONE(fwrite(&y2, sizeof(float), 1, file));
508 _DO_OR_DONE(fwrite(&x3, sizeof(float), 1, file));
509 _DO_OR_DONE(fwrite(&y3, sizeof(float), 1, file));
510 }
511
512 done:
513 mem_free(cachename);
514 if (file != NULL) {
515 fclose(file);
516 }
517 }
518
519 #undef _DO_OR_DIE
520
521
This page took 0.060823 seconds and 4 git commands to generate.