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