Add Z-buffer, fixes issue with pyramidal studs
[render.git] / obj.c
1 /* Copyright (c) 2005,2013,2014 Robert Kooima                                 */
2 /*                                                                            */
3 /* Permission is hereby granted, free of charge, to any person obtaining a    */
4 /* copy of this software and associated documentation files (the "Software"), */
5 /* to deal in the Software without restriction, including without limitation  */
6 /* the rights to use, copy, modify, merge, publish, distribute, sublicense,   */
7 /* and/or sell copies of the Software, and to permit persons to whom the      */
8 /* Software is furnished to do so, subject to the following conditions:       */
9 /*                                                                            */
10 /* The above copyright notice and this permission notice shall be included in */
11 /* all copies or substantial portions of the Software.                       */
12 /*                                                                            */
13 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR */
14 /* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   */
15 /* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    */
16 /* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER */
17 /* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING    */
18 /* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER        */
19 /* DEALINGS IN THE SOFTWARE.                                                 */
20
21 #include <stdio.h>
22 #include <ctype.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <math.h>
27 #include "obj.h"
28 #include "tga.h"
29
30 #ifndef CONF_NO_GL
31 #ifdef __APPLE__
32 #  include <OpenGL/gl3.h>
33 #else
34 #  include <GL/glew.h>
35 #endif
36
37 #ifdef OBJ_INDEX_IS_INT
38 typedef GL_UNSIGNED_INT GL_INDEX_T;
39 #else
40 typedef GL_UNSIGNED_SHORT GL_INDEX_T;
41 #endif
42 #endif
43
44 #define MAXSTR 1024
45 #define OPT_CLAMP 1
46
47 static void invalidate(struct obj *);
48
49 /*----------------------------------------------------------------------------*/
50
51 #define assert_surf(O, i) \
52       { assert(O); assert(0 <= i && i < O->sc); }
53 #define assert_vert(O, i) \
54       { assert(O); assert(0 <= i && i < O->vc); }
55 #define assert_mtrl(O, i) \
56       { assert(O); assert(0 <= i && i < O->mc); }
57
58 #define assert_line(O, i, j) \
59       { assert_surf(O, i); assert(0 <= j && j < O->sv[i].lc); }
60 #define assert_poly(O, i, j) \
61       { assert_surf(O, i); assert(0 <= j && j < O->sv[i].pc); }
62 #define assert_prop(O, i, j) \
63       { assert_mtrl(O, i); assert(0 <= j && j < OBJ_PROP_COUNT); }
64
65 /*============================================================================*/
66 /* Vector cache                                                               */
67
68 struct vec2
69 {
70     float v[2];
71     int _ii;
72 };
73
74 struct vec3
75 {
76     float v[3];
77     int _ii;
78 };
79
80 struct iset
81 {
82     int vi;
83     int gi;
84
85     int _vi;
86     int _ti;
87     int _ni;
88     int _ii;
89 };
90
91 static int _vc, _vm;
92 static int _tc, _tm;
93 static int _nc, _nm;
94 static int _ic, _im;
95
96 static struct vec3 *_vv;
97 static struct vec2 *_tv;
98 static struct vec3 *_nv;
99 static struct iset *_iv;
100
101 /*----------------------------------------------------------------------------*/
102
103 static int add__(void **_v, int *_c, int *_m, size_t _s)
104 {
105     int   m = (*_m > 0) ? *_m * 2 : 2;
106     void *v;
107
108     /* If space remains in the current block, return it. */
109
110     if (*_m > *_c)
111         return (*_c)++;
112
113     /* Else, try to increase the size of the block. */
114
115     else if ((v = realloc(*_v, _s * m)))
116     {
117         *_v = v;
118         *_m = m;
119         return (*_c)++;
120     }
121
122     /* Else, indicate failure. */
123
124     else return -1;
125 }
126
127 static int add_v(void)
128 {
129     return add__((void **) &_vv, &_vc, &_vm, sizeof (struct vec3));
130 }
131
132 static int add_t(void)
133 {
134     return add__((void **) &_tv, &_tc, &_tm, sizeof (struct vec2));
135 }
136
137 static int add_n(void)
138 {
139     return add__((void **) &_nv, &_nc, &_nm, sizeof (struct vec3));
140 }
141
142 static int add_i(void)
143 {
144     return add__((void **) &_iv, &_ic, &_im, sizeof (struct iset));
145 }
146
147 /*============================================================================*/
148 /* Handy functions                                                            */
149
150 static void cross(float *z, const float *x, const float *y)
151 {
152     float t[3];
153
154     t[0] = x[1] * y[2] - x[2] * y[1];
155     t[1] = x[2] * y[0] - x[0] * y[2];
156     t[2] = x[0] * y[1] - x[1] * y[0];
157
158     z[0] = t[0];
159     z[1] = t[1];
160     z[2] = t[2];
161 }
162
163 static void normalize(float *v)
164 {
165     float k = 1.0f / (float) sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
166
167     v[0] *= k;
168     v[1] *= k;
169     v[2] *= k;
170 }
171
172 static void normal(float *n, const float *a,
173                              const float *b,
174                              const float *c)
175 {
176     float u[3];
177     float v[3];
178
179     u[0] = b[0] - a[0];
180     u[1] = b[1] - a[1];
181     u[2] = b[2] - a[2];
182
183     v[0] = c[0] - a[0];
184     v[1] = c[1] - a[1];
185     v[2] = c[2] - a[2];
186
187     cross(n, u, v);
188     normalize(n);
189 }
190
191 /*============================================================================*/
192
193 #ifdef CONF_NO_GL
194 struct array *obj_load_image(const char *filename)
195 #else
196 unsigned int obj_load_image(const char *filename)
197 #endif
198 {
199 #ifdef CONF_NO_GL
200     struct array *o = NULL;
201 #else
202     unsigned int o = 0;
203 #endif
204
205     if (filename)
206     {
207         /* Read the image data from the named file to a new pixel buffer. */
208         struct tga *tga = tga_read(filename);
209
210 #ifdef CONF_NO_GL
211         /* We really do not need the TGA header, since coordinates are based */
212         /* on the data, rather than using the anchor position from header. */
213         o = tga->data;
214         tga->data = NULL;
215 #else
216         int   h = (int)tga->data->dim[0];
217         int   w = (int)tga->data->dim[1];
218         int   d = (int)tga->data->dim[2];
219         void *p = array_index0(tga->data);
220
221         /* Create an OpenGL texture object using these pixels. */
222         glGenTextures(1, &o);
223         glBindTexture(GL_TEXTURE_2D, o);
224
225         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
226         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
227
228         if (d == 4)
229             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0,
230                          GL_BGRA, GL_UNSIGNED_BYTE, p);
231         if (d == 3)
232             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,  w, h, 0,
233                          GL_BGR,  GL_UNSIGNED_BYTE, p);
234
235         glGenerateMipmap(GL_TEXTURE_2D);
236 #endif
237
238         /* Discard the unnecessary pixel buffer. */
239         tga_free(tga);
240     }
241
242     return o;
243 }
244
245 static void dirpath(char *pathname)
246 {
247     int i;
248
249     /* Find the path by cutting a file name at the last directory delimiter. */
250
251     for (i = (int) strlen(pathname) - 1; i >= 0; --i)
252         if (pathname[i] == '/' || pathname[i] == '\\')
253         {
254             pathname[i] = '\0';
255             return;
256         }
257
258     /* If no delimiter was found, return the current directory. */
259
260     strcpy(pathname, ".");
261 }
262
263 /*----------------------------------------------------------------------------*/
264
265 static void read_image(struct obj *O, int mi, int ki, const char *line,
266                                                const char *path)
267 {
268     unsigned int clamp  = 0;
269
270     float o[3] = { 0.0f, 0.0f, 0.0f };
271     float s[3] = { 1.0f, 1.0f, 1.0f };
272
273     char pathname[MAXSTR];
274
275     char map[MAXSTR];
276     char val[MAXSTR];
277
278     const char *end;
279
280     memset(map, 0, MAXSTR);
281     memset(val, 0, MAXSTR);
282
283     while (line[0] != '\0' && line[0] != '\r' && line[0] != '\n')
284     {
285         int n = 0;
286
287         while(isspace(line[0])) line++;
288
289         /* Parse property map options. */
290
291         if (sscanf(line, "-clamp %s%n", val, &n) >= 1)
292         {
293             clamp  = (strcmp(val, "on") == 0) ? OPT_CLAMP : 0;
294             line  += n;
295         }
296
297         /* Parse property map scale. */
298
299         else if (sscanf(line, "-s %f %f %f%n", s + 0, s + 1, s + 2, &n) >= 3)
300             line += n;
301         else if (sscanf(line, "-s %f %f%n",    s + 0, s + 1,        &n) >= 2)
302             line += n;
303         else if (sscanf(line, "-s %f%n",       s + 0,               &n) >= 1)
304             line += n;
305
306         /* Parse property map offset. */
307
308         else if (sscanf(line, "-o %f %f %f%n", o + 0, o + 1, o + 2, &n) >= 3)
309             line += n;
310         else if (sscanf(line, "-o %f %f%n",    o + 0, o + 1,        &n) >= 2)
311             line += n;
312         else if (sscanf(line, "-o %f%n",       o + 0,               &n) >= 1)
313             line += n;
314
315         /* Check for a file name */
316
317         else if ((end = strstr(line, ".tga"))) { strncpy(map, line, end - line + 4); break; }
318         else if ((end = strstr(line, ".TGA"))) { strncpy(map, line, end - line + 4); break; }
319
320         /* If we see something we don't recognize, stop looking. */
321
322         else break;
323     }
324
325     /* Apply all parsed property attributes to the material. */
326
327     sprintf(pathname, "%s/%s", path, map);
328
329     obj_set_mtrl_opt(O, mi, ki, clamp);
330     obj_set_mtrl_map(O, mi, ki, pathname);
331     obj_set_mtrl_o  (O, mi, ki, o);
332     obj_set_mtrl_s  (O, mi, ki, s);
333 }
334
335 static void read_color(struct obj *O, int mi, int ki, const char *line)
336 {
337     float c[4];
338
339     /* Merge incoming color components with existing defaults. */
340
341     obj_get_mtrl_c(O, mi, ki, c);
342     sscanf(line, "%f %f %f", c, c + 1, c + 2);
343     obj_set_mtrl_c(O, mi, ki, c);
344 }
345
346 static void read_alpha(struct obj *O, int mi, int ki, const char *line)
347 {
348     float c[4];
349     float t;
350
351     /* Merge incoming color components with existing defaults. */
352
353     obj_get_mtrl_c(O, mi, ki, c);
354     sscanf(line, "%f", &t);
355     c[3] = 1.0 - t;
356     obj_set_mtrl_c(O, mi, ki, c);
357 }
358
359 static void read_mtl(const char *path,
360                      const char *file,
361                      const char *name, struct obj *O, int mi)
362 {
363     char pathname[MAXSTR];
364
365     char buf[MAXSTR];
366     char key[MAXSTR];
367     char arg[MAXSTR];
368
369     FILE *fin;
370
371     int scanning = 1;
372     int n        = 0;
373
374     sprintf(pathname, "%s/%s", path, file);
375
376     if ((fin = fopen(pathname, "r")))
377     {
378         /* Process each line of the MTL file. */
379
380         while  (fgets (buf, MAXSTR, fin))
381             if (sscanf(buf, "%s%n", key, &n) >= 1)
382             {
383                 const char *c = buf + n;
384
385                 if (scanning)
386                 {
387                     /* Determine if we've found the MTL we're looking for. */
388
389                     if (!strcmp(key, "newmtl"))
390                     {
391                         sscanf(c, "%s", arg);
392
393                         if ((scanning = strcmp(arg, name)) == 0)
394                             obj_set_mtrl_name(O, mi, name);
395                     }
396                 }
397                 else
398                 {
399                     /* Stop scanning when the next MTL begins. */
400
401                     if (!strcmp(key, "newmtl"))
402                         break;
403
404                     /* Parse this material's properties. */
405
406                     else if (!strcmp(key, "map_Kd"))
407                         read_image(O, mi, OBJ_KD, c, path);
408                     else if (!strcmp(key, "map_Ka"))
409                         read_image(O, mi, OBJ_KA, c, path);
410                     else if (!strcmp(key, "map_Ke"))
411                         read_image(O, mi, OBJ_KE, c, path);
412                     else if (!strcmp(key, "map_Ks"))
413                         read_image(O, mi, OBJ_KS, c, path);
414                     else if (!strcmp(key, "map_Ns"))
415                         read_image(O, mi, OBJ_NS, c, path);
416                     else if (!strcmp(key, "map_Kn"))
417                         read_image(O, mi, OBJ_KN, c, path);
418
419                     else if (!strcmp(key, "Kd"))
420                         read_color(O, mi, OBJ_KD, c);
421                     else if (!strcmp(key, "Ka"))
422                         read_color(O, mi, OBJ_KA, c);
423                     else if (!strcmp(key, "Ke"))
424                         read_color(O, mi, OBJ_KE, c);
425                     else if (!strcmp(key, "Ks"))
426                         read_color(O, mi, OBJ_KS, c);
427                     else if (!strcmp(key, "Ns"))
428                         read_color(O, mi, OBJ_NS, c);
429
430                     else if (!strcmp(key, "d"))
431                         read_alpha(O, mi, OBJ_KD, c);
432                     else if (!strcmp(key, "Tr"))
433                         read_alpha(O, mi, OBJ_KD, c);
434                 }
435             }
436         fclose(fin);
437     }
438 }
439
440 static void read_mtllib(char *file, const char *line)
441 {
442     /* Parse the first file name from the given line. */
443
444     sscanf(line, "%s", file);
445 }
446
447 static int read_usemtl(const char *path,
448                        const char *file,
449                        const char *line, struct obj *O)
450 {
451     char name[MAXSTR];
452
453     int si;
454     int mi;
455
456     sscanf(line, "%s", name);
457
458     /* Create a new material for the incoming definition. */
459
460     if ((mi = obj_add_mtrl(O)) >= 0)
461     {
462         /* Create a new surface to contain geometry with the new material. */
463
464         if ((si = obj_add_surf(O)) >= 0)
465         {
466             /* Read the material definition and apply it to the new surface. */
467
468             read_mtl(path, file, name, O, mi);
469             obj_set_surf(O, si, mi);
470
471             /* Return the surface so that new geometry may be added to it. */
472
473             return si;
474         }
475     }
476
477     /* On failure, return the default surface. */
478
479     return 0;
480 }
481
482 /*----------------------------------------------------------------------------*/
483
484 static int read_poly_indices(const char *line, int *_vi, int *_ti, int *_ni)
485 {
486     int n;
487
488     *_vi = 0;
489     *_ti = 0;
490     *_ni = 0;
491
492     /* Parse a face vertex specification from the given line. */
493
494     if (sscanf(line, "%d/%d/%d%n", _vi, _ti, _ni, &n) >= 3) return n;
495     if (sscanf(line, "%d/%d%n",    _vi, _ti,      &n) >= 2) return n;
496     if (sscanf(line, "%d//%d%n",   _vi,      _ni, &n) >= 2) return n;
497     if (sscanf(line, "%d%n",       _vi,           &n) >= 1) return n;
498
499     return 0;
500 }
501
502 static int read_poly_vertices(const char *line, struct obj *O, int gi)
503 {
504     const char *c = line;
505
506     int _vi;
507     int _ti;
508     int _ni;
509     int _ii;
510     int _ij;
511
512     int  dc;
513     int  vi;
514     int  ic = 0;
515
516     /* Scan the face string, converting index sets to vertices. */
517
518     while ((dc = read_poly_indices(c, &_vi, &_ti, &_ni)))
519     {
520         /* Convert face indices to vector cache indices. */
521
522         _vi += (_vi < 0) ? _vc : -1;
523         _ti += (_ti < 0) ? _tc : -1;
524         _ni += (_ni < 0) ? _nc : -1;
525
526         /* Initialize a new index set. */
527
528         if ((_ii = add_i()) >= 0)
529         {
530             _iv[_ii]._vi = _vi;
531             _iv[_ii]._ni = _ni;
532             _iv[_ii]._ti = _ti;
533
534             /* Search the vector reference list for a repeated index set. */
535
536             for (_ij = _vv[_vi]._ii; _ij >= 0; _ij = _iv[_ij]._ii)
537                 if (_iv[_ij]._vi == _vi &&
538                     _iv[_ij]._ti == _ti &&
539                     _iv[_ij]._ni == _ni &&
540                     _iv[_ij]. gi ==  gi)
541                 {
542                     /* A repeat has been found. Link new to old. */
543
544                     _vv[_vi]._ii = _ii;
545                     _iv[_ii]._ii = _ij;
546                     _iv[_ii]. vi = _iv[_ij].vi;
547                     _iv[_ii]. gi = _iv[_ij].gi;
548
549                     break;
550                 }
551
552             /* If no repeat was found, add a new vertex. */
553
554             if ((_ij < 0) && (vi = obj_add_vert(O)) >= 0)
555             {
556                 _vv[_vi]._ii = _ii;
557                 _iv[_ii]._ii =  -1;
558                 _iv[_ii]. vi =  vi;
559                 _iv[_ii]. gi =  gi;
560
561                 /* Initialize the new vertex using valid cache references. */
562
563                 if (0 <= _vi && _vi < _vc) obj_set_vert_v(O, vi, _vv[_vi].v);
564                 if (0 <= _ni && _ni < _nc) obj_set_vert_n(O, vi, _nv[_ni].v);
565                 if (0 <= _ti && _ti < _tc) obj_set_vert_t(O, vi, _tv[_ti].v);
566             }
567             ic++;
568         }
569         c  += dc;
570     }
571     return ic;
572 }
573
574 static void read_f(const char *line, struct obj *O, int si, int gi)
575 {
576     float n[3];
577     float t[3];
578     int i, pi;
579
580     /* Create new vertex references for this face. */
581
582     int i0 = _ic;
583     int ic = read_poly_vertices(line, O, gi);
584
585     /* If smoothing, apply this face's normal to vertices that need it. */
586
587     if (gi)
588     {
589         normal(n, _vv[_iv[i0 + 0]._vi].v,
590                   _vv[_iv[i0 + 1]._vi].v,
591                   _vv[_iv[i0 + 2]._vi].v);
592
593         for (i = 0; i < ic; ++i)
594             if (_iv[i0 + 0]._ni < 0)
595             {
596                 obj_get_vert_n(O, _iv[i0 + i]._vi, t);
597                 t[0] += n[0];
598                 t[1] += n[1];
599                 t[2] += n[2];
600                 obj_set_vert_n(O, _iv[i0 + i]._vi, t);
601             }
602     }
603
604     /* Convert our N new vertex references into N-2 new triangles. */
605
606     for (i = 0; i < ic - 2; ++i)
607
608         if ((pi = obj_add_poly(O, si)) >= 0)
609         {
610             int vi[3];
611
612             vi[0] = _iv[i0        ].vi;
613             vi[1] = _iv[i0 + i + 1].vi;
614             vi[2] = _iv[i0 + i + 2].vi;
615
616             obj_set_poly(O, si, pi, vi);
617         }
618 }
619
620 /*----------------------------------------------------------------------------*/
621
622 static int read_line_indices(const char *line, int *_vi, int *_ti)
623 {
624     int n;
625
626     *_vi = 0;
627     *_ti = 0;
628
629     /* Parse a line vertex specification from the given line. */
630
631     if (sscanf(line, "%d/%d%n", _vi, _ti, &n) >= 2) return n;
632     if (sscanf(line, "%d%n",    _vi,      &n) >= 1) return n;
633
634     return 0;
635 }
636
637 static int read_line_vertices(const char *line, struct obj *O)
638 {
639     const char *c = line;
640
641     int _vi;
642     int _ti;
643     int _ii;
644     int _ij;
645
646     int  dc;
647     int  vi;
648     int  ic = 0;
649
650     /* Scan the line string, converting index sets to vertices. */
651
652     while ((dc = read_line_indices(c, &_vi, &_ti)))
653     {
654         /* Convert line indices to vector cache indices. */
655
656         _vi += (_vi < 0) ? _vc : -1;
657         _ti += (_ti < 0) ? _tc : -1;
658
659         /* Initialize a new index set. */
660
661         if ((_ii = add_i()) >= 0)
662         {
663             _iv[_ii]._vi = _vi;
664             _iv[_ii]._ti = _ti;
665
666             /* Search the vector reference list for a repeated index set. */
667
668             for (_ij = _vv[_vi]._ii; _ij >= 0; _ij = _iv[_ij]._ii)
669                 if (_iv[_ij]._vi == _vi &&
670                     _iv[_ij]._ti == _ti)
671                 {
672                     /* A repeat has been found. Link new to old. */
673
674                     _vv[_vi]._ii = _ii;
675                     _iv[_ii]._ii = _ij;
676                     _iv[_ii]. vi = _iv[_ij].vi;
677
678                     break;
679                 }
680
681             /* If no repeat was found, add a new vertex. */
682
683             if ((_ij < 0) && (vi = obj_add_vert(O)) >= 0)
684             {
685                 _vv[_vi]._ii = _ii;
686                 _iv[_ii]._ii =  -1;
687                 _iv[_ii]. vi =  vi;
688
689                 /* Initialize the new vertex using valid cache references. */
690
691                 if (0 <= _vi && _vi < _vc) obj_set_vert_v(O, vi, _vv[_vi].v);
692                 if (0 <= _ti && _ti < _tc) obj_set_vert_t(O, vi, _tv[_ti].v);
693             }
694             ic++;
695         }
696         c  += dc;
697     }
698     return ic;
699 }
700
701 static void read_l(const char *line, struct obj *O, int si)
702 {
703     int i, li;
704
705     /* Create new vertices for this line. */
706
707     int i0 = _ic;
708     int ic = read_line_vertices(line, O);
709
710     /* Convert our N new vertices into N-1 new lines. */
711
712     for (i = 0; i < ic - 1; ++i)
713
714         if ((li = obj_add_line(O, si)) >= 0)
715         {
716             int vi[2];
717
718             vi[0] = _iv[i0 + i    ].vi;
719             vi[1] = _iv[i0 + i + 1].vi;
720
721             obj_set_line(O, si, li, vi);
722         }
723 }
724
725 /*----------------------------------------------------------------------------*/
726
727 static void read_v(const char *line)
728 {
729     int _vi;
730
731     /* Parse a vertex position. */
732
733     if ((_vi = add_v()) >= 0)
734     {
735         sscanf(line, "%f %f %f", _vv[_vi].v + 0,
736                                  _vv[_vi].v + 1,
737                                  _vv[_vi].v + 2);
738         _vv[_vi]._ii = -1;
739     }
740 }
741
742 static void read_vt(const char *line)
743 {
744     int _ti;
745
746     /* Parse a texture coordinate. */
747
748     if ((_ti = add_t()) >= 0)
749     {
750         sscanf(line, "%f %f", _tv[_ti].v + 0,
751                               _tv[_ti].v + 1);
752         _tv[_ti]._ii = -1;
753     }
754 }
755
756 static void read_vn(const char *line)
757 {
758     int _ni;
759
760     /* Parse a normal. */
761
762     if ((_ni = add_n()) >= 0)
763     {
764         sscanf(line, "%f %f %f", _nv[_ni].v + 0,
765                                  _nv[_ni].v + 1,
766                                  _nv[_ni].v + 2);
767         _nv[_ni]._ii = -1;
768     }
769 }
770
771 /*----------------------------------------------------------------------------*/
772
773 static void read_obj(struct obj *O, const char *filename)
774 {
775     char buf[MAXSTR];
776     char key[MAXSTR];
777
778     char L[MAXSTR];
779     char D[MAXSTR];
780
781     FILE *fin;
782
783     /* Flush the vector caches. */
784
785     _vc = 0;
786     _tc = 0;
787     _nc = 0;
788     _ic = 0;
789
790     /* Add the named file to the given object. */
791
792     if ((fin = fopen(filename, "r")))
793     {
794         /* Ensure there exists a default surface 0 and default material 0. */
795
796         int si = obj_add_surf(O);
797         int mi = obj_add_mtrl(O);
798         int gi = 0;
799         int n;
800
801         obj_set_surf(O, si, mi);
802
803         /* Extract the directory from the filename for use in MTL loading. */
804
805         strncpy(D, filename, MAXSTR);
806         dirpath(D);
807
808         /* Process each line of the OBJ file, invoking the handler for each. */
809
810         while  (fgets (buf, MAXSTR, fin))
811             if (sscanf(buf, "%s%n", key, &n) >= 1)
812             {
813                 const char *c = buf + n;
814
815                 if      (!strcmp(key, "f" )) read_f (c, O, si, gi);
816                 else if (!strcmp(key, "l" )) read_l (c, O, si);
817                 else if (!strcmp(key, "vt")) read_vt(c);
818                 else if (!strcmp(key, "vn")) read_vn(c);
819                 else if (!strcmp(key, "v" )) read_v (c);
820
821                 else if (!strcmp(key, "mtllib"))      read_mtllib(   L, c   );
822                 else if (!strcmp(key, "usemtl")) si = read_usemtl(D, L, c, O);
823                 else if (!strcmp(key, "s"     )) gi = atoi(c);
824             }
825
826         fclose(fin);
827     }
828 }
829
830 /*----------------------------------------------------------------------------*/
831
832 static void obj_rel_mtrl(struct obj_mtrl *mp)
833 {
834     /* Release any resources held by this material. */
835
836     int ki;
837
838     for (ki = 0; ki < OBJ_PROP_COUNT; ki++)
839     {
840         if (mp->kv[ki].str) free(mp->kv[ki].str);
841 #ifndef CONF_NO_GL
842         if (mp->kv[ki].map) glDeleteTextures(1, &mp->kv[ki].map);
843 #endif
844     }
845 }
846
847 static void obj_rel_surf(struct obj_surf *sp)
848 {
849 #ifndef CONF_NO_GL
850     if (sp->pibo) glDeleteBuffers(1, &sp->pibo);
851     if (sp->libo) glDeleteBuffers(1, &sp->libo);
852 #endif
853
854     sp->pibo = 0;
855     sp->libo = 0;
856
857     /* Release this surface's polygon and line vectors. */
858
859     if (sp->pv) free(sp->pv);
860     if (sp->lv) free(sp->lv);
861 }
862
863 static void obj_rel(struct obj *O)
864 {
865     int si;
866     int mi;
867
868     /* Release resources held by this file and it's materials and surfaces. */
869
870 #ifndef CONF_NO_GL
871     if (O->vbo) glDeleteBuffers     (1, &O->vbo);
872     if (O->vao) glDeleteVertexArrays(1, &O->vao);
873 #endif
874
875     O->vbo = 0;
876
877     for (mi = 0; mi < O->mc; ++mi) obj_rel_mtrl(O->mv + mi);
878     for (si = 0; si < O->sc; ++si) obj_rel_surf(O->sv + si);
879 }
880
881 /*============================================================================*/
882
883 struct obj *obj_create(const char *filename)
884 {
885     struct obj *O;
886     int  i;
887
888     /* Allocate and initialize a new file. */
889
890     if ((O = (struct obj *) calloc(1, sizeof (struct obj))))
891     {
892         if (filename)
893         {
894             /* Read the named file. */
895
896             read_obj(O, filename);
897
898             /* Post-process the loaded object. */
899
900             obj_mini(O);
901             obj_proc(O);
902         }
903
904         /* Set default shader locations. */
905
906         for (i = 0; i < OBJ_PROP_COUNT; i++)
907         {
908             O->cloc[i] = -1;
909             O->oloc[i] = -1;
910             O->Mloc[i] = -1;
911         }
912         O->uloc = -1;
913         O->nloc = -1;
914         O->tloc = -1;
915         O->vloc = -1;
916     }
917     return O;
918 }
919
920 void obj_delete(struct obj *O)
921 {
922     assert(O);
923
924     obj_rel(O);
925
926     free(O);
927 }
928
929 /*----------------------------------------------------------------------------*/
930
931 int obj_add_mtrl(struct obj *O)
932 {
933     unsigned int opt = 0;
934
935     const float Kd[4] = { 0.8f, 0.8f, 0.8f, 1.0f };
936     const float Ka[4] = { 0.2f, 0.2f, 0.2f, 1.0f };
937     const float Ke[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
938     const float Ks[4] = { 0.0f, 0.0f, 0.0f, 1.0f };
939     const float Ns[4] = { 8.0f, 0.0f, 0.0f, 0.0f };
940     const float  s[3] = { 1.0f, 1.0f, 1.0f       };
941
942     int mi;
943
944     assert(O);
945
946     /* Allocate and initialize a new material. */
947
948     if ((mi = add__((void **) &O->mv,
949                               &O->mc,
950                               &O->mm, sizeof (struct obj_mtrl))) >= 0)
951     {
952         memset(O->mv + mi, 0, sizeof (struct obj_mtrl));
953
954         obj_set_mtrl_opt(O, mi, OBJ_KD, opt);
955         obj_set_mtrl_opt(O, mi, OBJ_KA, opt);
956         obj_set_mtrl_opt(O, mi, OBJ_KE, opt);
957         obj_set_mtrl_opt(O, mi, OBJ_KS, opt);
958         obj_set_mtrl_opt(O, mi, OBJ_NS, opt);
959         obj_set_mtrl_opt(O, mi, OBJ_KN, opt);
960
961         obj_set_mtrl_c  (O, mi, OBJ_KD, Kd);
962         obj_set_mtrl_c  (O, mi, OBJ_KA, Ka);
963         obj_set_mtrl_c  (O, mi, OBJ_KE, Ke);
964         obj_set_mtrl_c  (O, mi, OBJ_KS, Ks);
965         obj_set_mtrl_c  (O, mi, OBJ_NS, Ns);
966
967         obj_set_mtrl_s  (O, mi, OBJ_KD, s);
968         obj_set_mtrl_s  (O, mi, OBJ_KA, s);
969         obj_set_mtrl_s  (O, mi, OBJ_KE, s);
970         obj_set_mtrl_s  (O, mi, OBJ_KS, s);
971         obj_set_mtrl_s  (O, mi, OBJ_NS, s);
972         obj_set_mtrl_s  (O, mi, OBJ_KN, s);
973     }
974     return mi;
975 }
976
977 int obj_add_vert(struct obj *O)
978 {
979     int vi;
980
981     assert(O);
982
983     /* Allocate and initialize a new vertex. */
984
985     if ((vi = add__((void **) &O->vv,
986                               &O->vc,
987                               &O->vm, sizeof (struct obj_vert))) >= 0)
988
989         memset(O->vv + vi, 0, sizeof (struct obj_vert));
990
991     return vi;
992 }
993
994 int obj_add_poly(struct obj *O, int si)
995 {
996     int pi;
997
998     assert_surf(O, si);
999
1000     /* Allocate and initialize a new polygon. */
1001
1002     if ((pi = add__((void **) &O->sv[si].pv,
1003                               &O->sv[si].pc,
1004                               &O->sv[si].pm, sizeof (struct obj_poly)))>=0)
1005
1006         memset(O->sv[si].pv + pi, 0, sizeof (struct obj_poly));
1007
1008     return pi;
1009 }
1010
1011 int obj_add_line(struct obj *O, int si)
1012 {
1013     int li;
1014
1015     assert_surf(O, si);
1016
1017     /* Allocate and initialize a new line. */
1018
1019     if ((li = add__((void **) &O->sv[si].lv,
1020                               &O->sv[si].lc,
1021                               &O->sv[si].lm, sizeof (struct obj_line)))>=0)
1022
1023         memset(O->sv[si].lv + li, 0, sizeof (struct obj_line));
1024
1025     return li;
1026 }
1027
1028 int obj_add_surf(struct obj *O)
1029 {
1030     int si;
1031
1032     assert(O);
1033
1034     /* Allocate and initialize a new surface. */
1035
1036     if ((si = add__((void **) &O->sv,
1037                               &O->sc,
1038                               &O->sm, sizeof (struct obj_surf))) >= 0)
1039
1040         memset(O->sv + si, 0, sizeof (struct obj_surf));
1041
1042     return si;
1043 }
1044
1045 /*----------------------------------------------------------------------------*/
1046
1047 int obj_num_mtrl(const struct obj *O)
1048 {
1049     assert(O);
1050     return O->mc;
1051 }
1052
1053 int obj_num_vert(const struct obj *O)
1054 {
1055     assert(O);
1056     return O->vc;
1057 }
1058
1059 int obj_num_poly(const struct obj *O, int si)
1060 {
1061     assert_surf(O, si);
1062     return O->sv[si].pc;
1063 }
1064
1065 int obj_num_line(const struct obj *O, int si)
1066 {
1067     assert_surf(O, si);
1068     return O->sv[si].lc;
1069 }
1070
1071 int obj_num_surf(const struct obj *O)
1072 {
1073     assert(O);
1074     return O->sc;
1075 }
1076
1077
1078 /*----------------------------------------------------------------------------*/
1079
1080 void obj_del_mtrl(struct obj *O, int mi)
1081 {
1082     int si;
1083
1084     assert_mtrl(O, mi);
1085
1086     /* Remove this material from the material vector. */
1087
1088     obj_rel_mtrl(O->mv + mi);
1089
1090     memmove(O->mv + mi,
1091             O->mv + mi + 1,
1092            (O->mc - mi - 1) * sizeof (struct obj_mtrl));
1093
1094     O->mc--;
1095
1096     /* Remove all references to this material. */
1097
1098     for (si = O->sc - 1; si >= 0; --si)
1099     {
1100         struct obj_surf *sp = O->sv + si;
1101
1102         if (sp->mi == mi)
1103             obj_del_surf(O, si);
1104         else
1105             if (sp->mi > mi)
1106                 sp->mi--;
1107     }
1108 }
1109
1110 void obj_del_vert(struct obj *O, int vi)
1111 {
1112     int si;
1113     int pi;
1114     int li;
1115
1116     assert_vert(O, vi);
1117
1118     /* Remove this vertex from the file's vertex vector. */
1119
1120     memmove(O->vv + vi,
1121             O->vv + vi + 1,
1122            (O->vc - vi - 1) * sizeof (struct obj_vert));
1123
1124     O->vc--;
1125
1126     /* Remove all references to this vertex from all surfaces. */
1127
1128     for (si = 0; si < O->sc; ++si)
1129     {
1130         /* Delete all referencing polygons. Decrement later references. */
1131
1132         for (pi = O->sv[si].pc - 1; pi >= 0; --pi)
1133         {
1134             struct obj_poly *pp = O->sv[si].pv + pi;
1135
1136             if (pp->vi[0] == vi || pp->vi[1] == vi || pp->vi[2] == vi)
1137                 obj_del_poly(O, si, pi);
1138             else
1139             {
1140                 if (pp->vi[0] > vi) pp->vi[0]--;
1141                 if (pp->vi[1] > vi) pp->vi[1]--;
1142                 if (pp->vi[2] > vi) pp->vi[2]--;
1143             }
1144         }
1145
1146         /* Delete all referencing lines. Decrement later references. */
1147
1148         for (li = O->sv[si].lc - 1; li >= 0; --li)
1149         {
1150             struct obj_line *lp = O->sv[si].lv + li;
1151
1152             if (lp->vi[0] == vi || lp->vi[1] == vi)
1153                 obj_del_line(O, si, li);
1154             else
1155             {
1156                 if (lp->vi[0] > vi) lp->vi[0]--;
1157                 if (lp->vi[1] > vi) lp->vi[1]--;
1158             }
1159         }
1160     }
1161
1162     /* Schedule the VBO for refresh. */
1163
1164     invalidate(O);
1165 }
1166
1167 void obj_del_poly(struct obj *O, int si, int pi)
1168 {
1169     assert_poly(O, si, pi);
1170
1171     /* Remove this polygon from the surface's polygon vector. */
1172
1173     memmove(O->sv[si].pv + pi,
1174             O->sv[si].pv + pi + 1,
1175            (O->sv[si].pc - pi - 1) * sizeof (struct obj_poly));
1176
1177     O->sv[si].pc--;
1178 }
1179
1180 void obj_del_line(struct obj *O, int si, int li)
1181 {
1182     assert_line(O, si, li);
1183
1184     /* Remove this line from the surface's line vector. */
1185
1186     memmove(O->sv[si].lv + li,
1187             O->sv[si].lv + li + 1,
1188            (O->sv[si].lc - li - 1) * sizeof (struct obj_line));
1189
1190     O->sv[si].lc--;
1191 }
1192
1193 void obj_del_surf(struct obj *O, int si)
1194 {
1195     assert_surf(O, si);
1196
1197     /* Remove this surface from the file's surface vector. */
1198
1199     obj_rel_surf(O->sv + si);
1200
1201     memmove(O->sv + si,
1202             O->sv + si + 1,
1203            (O->sc - si - 1) * sizeof (struct obj_surf));
1204
1205     O->sc--;
1206 }
1207
1208 /*----------------------------------------------------------------------------*/
1209
1210 static char *set_name(char *old, const char *src)
1211 {
1212     char *dst = NULL;
1213
1214     if (old)
1215         free(old);
1216
1217     if (src && (dst = (char *) malloc(strlen(src) + 1)))
1218         strcpy(dst, src);
1219
1220     return dst;
1221 }
1222
1223 void obj_set_mtrl_name(struct obj *O, int mi, const char *name)
1224 {
1225     assert_mtrl(O, mi);
1226     O->mv[mi].name = set_name(O->mv[mi].name, name);
1227 }
1228
1229 void obj_set_mtrl_map(struct obj *O, int mi, int ki, const char *str)
1230 {
1231     assert_prop(O, mi, ki);
1232
1233     if (O->mv[mi].kv[ki].map)
1234 #ifdef CONF_NO_GL
1235         array_free(O->mv[mi].kv[ki].map); // note: may be NULL
1236 #else
1237         glDeleteTextures(1, &O->mv[mi].kv[ki].map);
1238 #endif
1239
1240     O->mv[mi].kv[ki].map = obj_load_image(str);
1241     O->mv[mi].kv[ki].str = set_name(O->mv[mi].kv[ki].str, str);
1242 }
1243
1244 void obj_set_mtrl_opt(struct obj *O, int mi, int ki, unsigned int opt)
1245 {
1246     assert_prop(O, mi, ki);
1247
1248     O->mv[mi].kv[ki].opt = opt;
1249 }
1250
1251 void obj_set_mtrl_c(struct obj *O, int mi, int ki, const float c[4])
1252 {
1253     assert_prop(O, mi, ki);
1254
1255     O->mv[mi].kv[ki].c[0] = c[0];
1256     O->mv[mi].kv[ki].c[1] = c[1];
1257     O->mv[mi].kv[ki].c[2] = c[2];
1258     O->mv[mi].kv[ki].c[3] = c[3];
1259 }
1260
1261 void obj_set_mtrl_s(struct obj *O, int mi, int ki, const float s[3])
1262 {
1263     assert_prop(O, mi, ki);
1264
1265     O->mv[mi].kv[ki].s[0] = s[0];
1266     O->mv[mi].kv[ki].s[1] = s[1];
1267     O->mv[mi].kv[ki].s[2] = s[2];
1268 }
1269
1270 void obj_set_mtrl_o(struct obj *O, int mi, int ki, const float o[3])
1271 {
1272     assert_prop(O, mi, ki);
1273
1274     O->mv[mi].kv[ki].o[0] = o[0];
1275     O->mv[mi].kv[ki].o[1] = o[1];
1276     O->mv[mi].kv[ki].o[2] = o[2];
1277 }
1278
1279 /*----------------------------------------------------------------------------*/
1280
1281 static void invalidate(struct obj *O)
1282 {
1283 #ifndef CONF_NO_GL
1284     if (O->vbo) glDeleteBuffers     (1, &O->vbo);
1285     if (O->vao) glDeleteVertexArrays(1, &O->vao);
1286 #endif
1287     O->vbo = 0;
1288     O->vao = 0;
1289 }
1290
1291 void obj_set_vert_v(struct obj *O, int vi, const float v[3])
1292 {
1293     assert_vert(O, vi);
1294
1295     O->vv[vi].v[0] = v[0];
1296     O->vv[vi].v[1] = v[1];
1297     O->vv[vi].v[2] = v[2];
1298
1299     invalidate(O);
1300 }
1301
1302 void obj_set_vert_t(struct obj *O, int vi, const float t[2])
1303 {
1304     assert_vert(O, vi);
1305
1306     O->vv[vi].t[0] = t[0];
1307     O->vv[vi].t[1] = t[1];
1308
1309     invalidate(O);
1310 }
1311
1312 void obj_set_vert_n(struct obj *O, int vi, const float n[3])
1313 {
1314     assert_vert(O, vi);
1315
1316     O->vv[vi].n[0] = n[0];
1317     O->vv[vi].n[1] = n[1];
1318     O->vv[vi].n[2] = n[2];
1319
1320     invalidate(O);
1321 }
1322
1323 void obj_set_vert_u(struct obj *O, int vi, const float u[3])
1324 {
1325     assert_vert(O, vi);
1326
1327     O->vv[vi].u[0] = u[0];
1328     O->vv[vi].u[1] = u[1];
1329     O->vv[vi].u[2] = u[2];
1330
1331     invalidate(O);
1332 }
1333
1334 /*----------------------------------------------------------------------------*/
1335
1336 void obj_set_poly(struct obj *O, int si, int pi, const int vi[3])
1337 {
1338     assert_poly(O, si, pi);
1339
1340     O->sv[si].pv[pi].vi[0] = (obj_index_t) vi[0];
1341     O->sv[si].pv[pi].vi[1] = (obj_index_t) vi[1];
1342     O->sv[si].pv[pi].vi[2] = (obj_index_t) vi[2];
1343 }
1344
1345 void obj_set_line(struct obj *O, int si, int li, const int vi[2])
1346 {
1347     assert_line(O, si, li);
1348
1349     O->sv[si].lv[li].vi[0] = (obj_index_t) vi[0];
1350     O->sv[si].lv[li].vi[1] = (obj_index_t) vi[1];
1351 }
1352
1353 void obj_set_surf(struct obj *O, int si, int mi)
1354 {
1355     assert_surf(O, si);
1356
1357     O->sv[si].mi = mi;
1358 }
1359
1360 /*----------------------------------------------------------------------------*/
1361
1362 void obj_set_vert_loc(struct obj *O, int u, int n, int t, int v)
1363 {
1364     assert(O);
1365
1366     O->uloc = u;
1367     O->nloc = n;
1368     O->tloc = t;
1369     O->vloc = v;
1370
1371     invalidate(O);
1372 }
1373
1374 void obj_set_prop_loc(struct obj *O, int ki, int c, int o, int M)
1375 {
1376     assert(O);
1377     assert(0 <= ki && ki < OBJ_PROP_COUNT);
1378
1379     O->cloc[ki] = c;
1380     O->oloc[ki] = o;
1381     O->Mloc[ki] = M;
1382 }
1383
1384 /*============================================================================*/
1385
1386 const char *obj_get_mtrl_name(const struct obj *O, int mi)
1387 {
1388     assert_mtrl(O, mi);
1389     return O->mv[mi].name;
1390 }
1391
1392 #ifdef CONF_NO_GL
1393 struct array *obj_get_mtrl_map(const struct obj *O, int mi, int ki)
1394 #else
1395 unsigned int obj_get_mtrl_map(const struct obj *O, int mi, int ki)
1396 #endif
1397 {
1398     assert_prop(O, mi, ki);
1399     return O->mv[mi].kv[ki].map;
1400 }
1401
1402 unsigned int obj_get_mtrl_opt(const struct obj *O, int mi, int ki)
1403 {
1404     assert_prop(O, mi, ki);
1405     return O->mv[mi].kv[ki].opt;
1406 }
1407
1408 void obj_get_mtrl_c(const struct obj *O, int mi, int ki, float *c)
1409 {
1410     assert_prop(O, mi, ki);
1411
1412     c[0] = O->mv[mi].kv[ki].c[0];
1413     c[1] = O->mv[mi].kv[ki].c[1];
1414     c[2] = O->mv[mi].kv[ki].c[2];
1415     c[3] = O->mv[mi].kv[ki].c[3];
1416 }
1417
1418 void obj_get_mtrl_s(const struct obj *O, int mi, int ki, float *s)
1419 {
1420     assert_prop(O, mi, ki);
1421
1422     s[0] = O->mv[mi].kv[ki].s[0];
1423     s[1] = O->mv[mi].kv[ki].s[1];
1424     s[2] = O->mv[mi].kv[ki].s[2];
1425 }
1426
1427 void obj_get_mtrl_o(const struct obj *O, int mi, int ki, float *o)
1428 {
1429     assert_prop(O, mi, ki);
1430
1431     o[0] = O->mv[mi].kv[ki].o[0];
1432     o[1] = O->mv[mi].kv[ki].o[1];
1433     o[2] = O->mv[mi].kv[ki].o[2];
1434 }
1435
1436 /*----------------------------------------------------------------------------*/
1437
1438 void obj_get_vert_v(const struct obj *O, int vi, float *v)
1439 {
1440     assert_vert(O, vi);
1441
1442     v[0] = O->vv[vi].v[0];
1443     v[1] = O->vv[vi].v[1];
1444     v[2] = O->vv[vi].v[2];
1445 }
1446
1447 void obj_get_vert_t(const struct obj *O, int vi, float *t)
1448 {
1449     assert_vert(O, vi);
1450
1451     t[0] = O->vv[vi].t[0];
1452     t[1] = O->vv[vi].t[1];
1453 }
1454
1455 void obj_get_vert_n(const struct obj *O, int vi, float *n)
1456 {
1457     assert_vert(O, vi);
1458
1459     n[0] = O->vv[vi].n[0];
1460     n[1] = O->vv[vi].n[1];
1461     n[2] = O->vv[vi].n[2];
1462 }
1463
1464 /*----------------------------------------------------------------------------*/
1465
1466 void obj_get_poly(const struct obj *O, int si, int pi, int *vi)
1467 {
1468     assert_poly(O, si, pi);
1469
1470     vi[0] = (int) O->sv[si].pv[pi].vi[0];
1471     vi[1] = (int) O->sv[si].pv[pi].vi[1];
1472     vi[2] = (int) O->sv[si].pv[pi].vi[2];
1473 }
1474
1475 void obj_get_line(const struct obj *O, int si, int li, int *vi)
1476 {
1477     assert_line(O, si, li);
1478
1479     vi[0] = (int) O->sv[si].lv[li].vi[0];
1480     vi[1] = (int) O->sv[si].lv[li].vi[1];
1481 }
1482
1483 int obj_get_surf(const struct obj *O, int si)
1484 {
1485     assert_surf(O, si);
1486     return O->sv[si].mi;
1487 }
1488
1489 /*============================================================================*/
1490
1491 void obj_mini(struct obj *O)
1492 {
1493     int si;
1494     int mi;
1495
1496     /* Remove empty surfaces. */
1497
1498     for (si = O->sc - 1; si >= 0; --si)
1499         if (O->sv[si].pc == 0 &&
1500             O->sv[si].lc == 0)
1501             obj_del_surf(O, si);
1502
1503     /* Remove unreferenced materials. */
1504
1505     for (mi = O->mc - 1; mi >= 0; --mi)
1506     {
1507         int cc = 0;
1508
1509         for (si = 0; si < O->sc; ++si)
1510             if (O->sv[si].mi == mi)
1511                 cc++;
1512
1513         if (cc == 0)
1514             obj_del_mtrl(O, mi);
1515     }
1516 }
1517
1518 void obj_norm(struct obj *O)
1519 {
1520     int vi;
1521     int si;
1522     int pi;
1523
1524     assert(O);
1525
1526     /* Zero the normals for all vertices. */
1527
1528     for (vi = 0; vi < O->vc; ++vi)
1529     {
1530         O->vv[vi].n[0] = 0;
1531         O->vv[vi].n[1] = 0;
1532         O->vv[vi].n[2] = 0;
1533     }
1534
1535     /* Compute normals for all faces. */
1536
1537     for (si = 0; si < O->sc; ++si)
1538         for (pi = 0; pi < O->sv[si].pc; ++pi)
1539         {
1540             struct obj_vert *v0 = O->vv + O->sv[si].pv[pi].vi[0];
1541             struct obj_vert *v1 = O->vv + O->sv[si].pv[pi].vi[1];
1542             struct obj_vert *v2 = O->vv + O->sv[si].pv[pi].vi[2];
1543
1544             float n[3];
1545
1546             /* Compute the normal formed by these 3 vertices. */
1547
1548             normal(n, v0->v, v1->v, v2->v);
1549
1550             /* Sum this normal to all vertices. */
1551
1552             v0->n[0] += n[0];
1553             v0->n[1] += n[1];
1554             v0->n[2] += n[2];
1555
1556             v1->n[0] += n[0];
1557             v1->n[1] += n[1];
1558             v1->n[2] += n[2];
1559
1560             v2->n[0] += n[0];
1561             v2->n[1] += n[1];
1562             v2->n[2] += n[2];
1563         }
1564 }
1565
1566 void obj_proc(struct obj *O)
1567 {
1568     int si;
1569     int sj;
1570     int pi;
1571     int vi;
1572
1573     assert(O);
1574
1575     /* Normalize all normals. Zero all tangent vectors. */
1576
1577     for (vi = 0; vi < O->vc; ++vi)
1578     {
1579         normalize(O->vv[vi].n);
1580
1581         O->vv[vi].u[0] = 0.0f;
1582         O->vv[vi].u[1] = 0.0f;
1583         O->vv[vi].u[2] = 0.0f;
1584     }
1585
1586     /* Compute tangent vectors for all vertices. */
1587
1588     for (si = 0; si < O->sc; ++si)
1589         for (pi = 0; pi < O->sv[si].pc; ++pi)
1590         {
1591             struct obj_vert *v0 = O->vv + O->sv[si].pv[pi].vi[0];
1592             struct obj_vert *v1 = O->vv + O->sv[si].pv[pi].vi[1];
1593             struct obj_vert *v2 = O->vv + O->sv[si].pv[pi].vi[2];
1594
1595             float dt1, dv1[3];
1596             float dt2, dv2[3];
1597
1598             float u[3];
1599
1600             /* Compute the tangent vector for this polygon. */
1601
1602             dv1[0] = v1->v[0] - v0->v[0];
1603             dv1[1] = v1->v[1] - v0->v[1];
1604             dv1[2] = v1->v[2] - v0->v[2];
1605
1606             dv2[0] = v2->v[0] - v0->v[0];
1607             dv2[1] = v2->v[1] - v0->v[1];
1608             dv2[2] = v2->v[2] - v0->v[2];
1609
1610             dt1    = v1->t[1] - v0->t[1];
1611             dt2    = v2->t[1] - v0->t[1];
1612
1613             u[0]   = dt2 * dv1[0] - dt1 * dv2[0];
1614             u[1]   = dt2 * dv1[1] - dt1 * dv2[1];
1615             u[2]   = dt2 * dv1[2] - dt1 * dv2[2];
1616
1617             normalize(u);
1618
1619             /* Accumulate the tangent vectors for this polygon's vertices. */
1620
1621             v0->u[0] += u[0];  v0->u[1] += u[1];  v0->u[2] += u[2];
1622             v1->u[0] += u[0];  v1->u[1] += u[1];  v1->u[2] += u[2];
1623             v2->u[0] += u[0];  v2->u[1] += u[1];  v2->u[2] += u[2];
1624         }
1625
1626     /* Orthonormalize each tangent basis. */
1627
1628     for (vi = 0; vi < O->vc; ++vi)
1629     {
1630         float *n = O->vv[vi].n;
1631         float *u = O->vv[vi].u;
1632
1633         float v[3];
1634
1635         cross(v, n, u);
1636         cross(u, v, n);
1637         normalize(u);
1638     }
1639
1640     /* Sort surfaces such that transparent ones appear later. */
1641
1642     for (si = 0; si < O->sc; ++si)
1643         for (sj = si + 1; sj < O->sc; ++sj)
1644             if (O->mv[O->sv[si].mi].kv[OBJ_KD].c[3] <
1645                 O->mv[O->sv[sj].mi].kv[OBJ_KD].c[3])
1646             {
1647                 struct obj_surf temp;
1648
1649                 temp      = O->sv[si];
1650                 O->sv[si] = O->sv[sj];
1651                 O->sv[sj] = temp;
1652             }
1653 }
1654
1655 void obj_init(struct obj *O)
1656 {
1657 #ifndef CONF_NO_GL
1658     if (O->vao == 0)
1659     {
1660         const size_t vs = sizeof (struct obj_vert);
1661         const size_t ps = sizeof (struct obj_poly);
1662         const size_t ls = sizeof (struct obj_line);
1663
1664         int si;
1665
1666         /* Store the following bindings in a vertex array object. */
1667
1668         glGenVertexArrays(1, &O->vao);
1669         glBindVertexArray(    O->vao);
1670
1671         /* Store all vertex data in a vertex buffer object. */
1672
1673         glGenBuffers(1, &O->vbo);
1674         glBindBuffer(GL_ARRAY_BUFFER, O->vbo);
1675         glBufferData(GL_ARRAY_BUFFER, O->vc * vs, O->vv, GL_STATIC_DRAW);
1676
1677         /* Store all index data in index buffer objects. */
1678
1679         for (si = 0; si < O->sc; ++si)
1680         {
1681             if (O->sv[si].pc > 0)
1682             {
1683                 glGenBuffers(1, &O->sv[si].pibo);
1684                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, O->sv[si].pibo);
1685                 glBufferData(GL_ELEMENT_ARRAY_BUFFER, O->sv[si].pc * ps,
1686                                         O->sv[si].pv, GL_STATIC_DRAW);
1687             }
1688
1689             if (O->sv[si].lc > 0)
1690             {
1691                 glGenBuffers(1, &O->sv[si].libo);
1692                 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, O->sv[si].libo);
1693                 glBufferData(GL_ELEMENT_ARRAY_BUFFER, O->sv[si].lc * ls,
1694                                         O->sv[si].lv, GL_STATIC_DRAW);
1695             }
1696         }
1697
1698         /* Enable and bind the attributes. */
1699
1700         if (O->uloc >= 0)
1701         {
1702             glEnableVertexAttribArray(O->uloc);
1703             glVertexAttribPointer(O->uloc, 3, GL_FLOAT, GL_FALSE, vs, (const GLvoid *)  0);
1704         }
1705         if (O->nloc >= 0)
1706         {
1707             glEnableVertexAttribArray(O->nloc);
1708             glVertexAttribPointer(O->nloc, 3, GL_FLOAT, GL_FALSE, vs, (const GLvoid *) 12);
1709         }
1710         if (O->tloc >= 0)
1711         {
1712             glEnableVertexAttribArray(O->tloc);
1713             glVertexAttribPointer(O->tloc, 2, GL_FLOAT, GL_FALSE, vs, (const GLvoid *) 24);
1714         }
1715         if (O->vloc >= 0)
1716         {
1717             glEnableVertexAttribArray(O->vloc);
1718             glVertexAttribPointer(O->vloc, 3, GL_FLOAT, GL_FALSE, vs, (const GLvoid *) 32);
1719         }
1720     }
1721 #endif
1722 }
1723
1724 /*----------------------------------------------------------------------------*/
1725
1726 int obj_cmp_vert(struct obj *O, int vi, int vj, float eps, float dot)
1727 {
1728     if (fabs(O->vv[vi].v[0] - O->vv[vj].v[0]) >= eps) return 0;
1729     if (fabs(O->vv[vi].v[1] - O->vv[vj].v[1]) >= eps) return 0;
1730     if (fabs(O->vv[vi].v[2] - O->vv[vj].v[2]) >= eps) return 0;
1731
1732     if (fabs(O->vv[vi].t[0] - O->vv[vj].t[0]) >= eps) return 0;
1733     if (fabs(O->vv[vi].t[1] - O->vv[vj].t[1]) >= eps) return 0;
1734
1735     if (O->vv[vi].n[0] * O->vv[vj].n[0] +
1736         O->vv[vi].n[1] * O->vv[vj].n[1] +
1737         O->vv[vi].n[2] * O->vv[vj].n[2] < dot) return 0;
1738
1739     return 1;
1740 }
1741
1742 void obj_swp_vert(struct obj *O, int vi, int vj)
1743 {
1744     int si;
1745     int pi;
1746     int li;
1747
1748     /* Replace all occurrences of vi with vj. */
1749
1750     for (si = 0; si < O->sc; ++si)
1751     {
1752         for (pi = 0; pi < O->sv[si].pc; ++pi)
1753         {
1754             if (O->sv[si].pv[pi].vi[0] == vi)
1755                 O->sv[si].pv[pi].vi[0] =  vj;
1756             if (O->sv[si].pv[pi].vi[1] == vi)
1757                 O->sv[si].pv[pi].vi[1] =  vj;
1758             if (O->sv[si].pv[pi].vi[2] == vi)
1759                 O->sv[si].pv[pi].vi[2] =  vj;
1760         }
1761         for (li = 0; li < O->sv[si].lc; ++li)
1762         {
1763             if (O->sv[si].lv[li].vi[0] == vi)
1764                 O->sv[si].lv[li].vi[0] =  vj;
1765             if (O->sv[si].lv[li].vi[1] == vi)
1766                 O->sv[si].lv[li].vi[1] =  vj;
1767         }
1768     }
1769 }
1770
1771 void obj_uniq(struct obj *O, float eps, float dot, int verbose)
1772 {
1773     int vc = O->vc;
1774     int vi;
1775     int vj;
1776     int di;
1777
1778     /* Merge all vertices within epsilon of one another. */
1779
1780     for (vi = 0; vi < O->vc; vi += di)
1781     {
1782         di = 1;
1783
1784         for (vj = 0; vj < vi; ++vj)
1785         {
1786             if (obj_cmp_vert(O, vi, vj, eps, dot))
1787             {
1788                 if (verbose) printf("%d %d\n", vi, vc--);
1789
1790                 obj_swp_vert(O, vi, vj);
1791                 obj_del_vert(O, vi);
1792
1793                 di = 0;
1794                 break;
1795             }
1796         }
1797     }
1798 }
1799
1800 /*----------------------------------------------------------------------------*/
1801
1802 void obj_sort(struct obj *O, int qc)
1803 {
1804     const int vc = O->vc;
1805
1806     struct vert
1807     {
1808         int  qs;    /* Cache insertion serial number */
1809         int *iv;    /* Polygon reference list buffer */
1810         int  ic;    /* Polygon reference list length */
1811     };
1812
1813     /* Vertex optimization data; vertex FIFO cache */
1814
1815     struct vert *vv = (struct vert *) malloc(vc * sizeof (struct vert));
1816     int         *qv = (int         *) malloc(qc * sizeof (int        ));
1817
1818     int qs = 1;   /* Current cache insertion serial number */
1819     int qi = 0;   /* Current cache insertion point [0, qc) */
1820
1821     int si;
1822     int pi;
1823     int vi;
1824     int ii;
1825     int qj;
1826
1827     /* Initialize the vertex cache to empty. */
1828
1829     for (qj = 0; qj < qc; ++qj)
1830         qv[qj] = -1;
1831
1832     /* Process each surface of this file in turn. */
1833
1834     for (si = 0; si < O->sc; ++si)
1835     {
1836         const int pc = O->sv[si].pc;
1837
1838         /* Allocate the polygon reference list buffers. */
1839
1840         int *ip, *iv = (int *) malloc(3 * pc * sizeof (int));
1841
1842         /* Count the number of polygon references per vertex. */
1843
1844         memset(vv, 0, vc * sizeof (struct vert));
1845
1846         for (pi = 0; pi < pc; ++pi)
1847         {
1848             const obj_index_t *i = O->sv[si].pv[pi].vi;
1849
1850             vv[i[0]].ic++;
1851             vv[i[1]].ic++;
1852             vv[i[2]].ic++;
1853         }
1854
1855         /* Initialize all vertex optimization data. */
1856
1857         for (vi = 0, ip = iv; vi < vc; ++vi)
1858         {
1859             vv[vi].qs = -qc;
1860             vv[vi].iv =  ip;
1861             ip += vv[vi].ic;
1862             vv[vi].ic =   0;
1863         }
1864
1865         /* Fill the polygon reference list buffers. */
1866
1867         for (pi = 0; pi < pc; ++pi)
1868         {
1869             const obj_index_t *i = O->sv[si].pv[pi].vi;
1870
1871             vv[i[0]].iv[vv[i[0]].ic++] = pi;
1872             vv[i[1]].iv[vv[i[1]].ic++] = pi;
1873             vv[i[2]].iv[vv[i[2]].ic++] = pi;
1874         }
1875
1876         /* Iterate over the polygon array of this surface. */
1877
1878         for (pi = 0; pi < pc; ++pi)
1879         {
1880             const obj_index_t *i = O->sv[si].pv[pi].vi;
1881
1882             int qd = qs - qc;
1883
1884             int dk = -1;    /* The best polygon score */
1885             int pk = pi;    /* The best polygon index */
1886
1887             /* Find the best polygon among those referred-to by the cache. */
1888
1889             for (qj = 0; qj < qc; ++qj)
1890                 if (qv[qj] >= 0)
1891
1892                     for (ii = 0;  ii < vv[qv[qj]].ic; ++ii)
1893                     {
1894                         int pj = vv[qv[qj]].iv[ii];
1895                         int dj = 0;
1896
1897                         const obj_index_t *j = O->sv[si].pv[pj].vi;
1898
1899                         /* Recently-used vertex bonus. */
1900
1901                         if (vv[j[0]].qs > qd) dj += vv[j[0]].qs - qd;
1902                         if (vv[j[1]].qs > qd) dj += vv[j[1]].qs - qd;
1903                         if (vv[j[2]].qs > qd) dj += vv[j[2]].qs - qd;
1904
1905                         /* Low-valence vertex bonus. */
1906
1907                         dj -= vv[j[0]].ic;
1908                         dj -= vv[j[1]].ic;
1909                         dj -= vv[j[2]].ic;
1910
1911                         if (dk < dj)
1912                         {
1913                             dk = dj;
1914                             pk = pj;
1915                         }
1916                     }
1917
1918             if (pk != pi)
1919             {
1920                 struct obj_poly temp;
1921
1922                 /* Update the polygon reference list. */
1923
1924                 for (vi = 0; vi < 3; ++vi)
1925                     for (ii = 0; ii < vv[i[vi]].ic; ++ii)
1926                         if (vv[i[vi]].iv[ii] == pi)
1927                         {
1928                             vv[i[vi]].iv[ii] =  pk;
1929                             break;
1930                         }
1931
1932                 /* Swap the best polygon into the current position. */
1933
1934                 temp             = O->sv[si].pv[pi];
1935                 O->sv[si].pv[pi] = O->sv[si].pv[pk];
1936                 O->sv[si].pv[pk] =  temp;
1937             }
1938
1939             /* Iterate over the current polygon's vertices. */
1940
1941             for (vi = 0; vi < 3; ++vi)
1942             {
1943                 struct vert *vp = vv + i[vi];
1944
1945                 /* If this vertex was a cache miss then queue it. */
1946
1947                 if (qs - vp->qs >= qc)
1948                 {
1949                     vp->qs = qs++;
1950                     qv[qi] = i[vi];
1951                     qi = (qi + 1) % qc;
1952                 }
1953
1954                 /* Remove the current polygon from the reference list. */
1955
1956                 vp->ic--;
1957
1958                 for (ii = 0; ii < vp->ic; ++ii)
1959                     if (vp->iv[ii] == pk)
1960                     {
1961                         vp->iv[ii] = vp->iv[vp->ic];
1962                         break;
1963                     }
1964             }
1965         }
1966         free(iv);
1967     }
1968     free(qv);
1969     free(vv);
1970 }
1971
1972 float obj_acmr(struct obj *O, int qc)
1973 {
1974     int *vs = (int *) malloc(O->vc * sizeof (int));
1975     int  qs = 1;
1976
1977     int si;
1978     int vi;
1979     int pi;
1980
1981     int nn = 0;
1982     int dd = 0;
1983
1984     for (si = 0; si < O->sc; ++si)
1985     {
1986         for (vi = 0; vi < O->vc; ++vi)
1987             vs[vi] = -qc;
1988
1989         for (pi = 0; pi < O->sv[si].pc; ++pi)
1990         {
1991             const obj_index_t *i = O->sv[si].pv[pi].vi;
1992
1993             if (qs - vs[i[0]] >= qc) { vs[i[0]] = qs++; nn++; }
1994             if (qs - vs[i[1]] >= qc) { vs[i[1]] = qs++; nn++; }
1995             if (qs - vs[i[2]] >= qc) { vs[i[2]] = qs++; nn++; }
1996
1997             dd++;
1998         }
1999     }
2000
2001     return (float) nn / (float) dd;
2002 }
2003
2004 /*----------------------------------------------------------------------------*/
2005
2006 #ifndef CONF_NO_GL
2007
2008 static void obj_render_prop(const struct obj *O, int mi, int ki)
2009 {
2010     const struct obj_prop *kp = O->mv[mi].kv + ki;
2011
2012     if (kp->map)
2013     {
2014         GLenum wrap = GL_REPEAT;
2015
2016         /* Bind the property map. */
2017
2018         glBindTexture(GL_TEXTURE_2D, kp->map);
2019
2020         /* Apply the property options. */
2021
2022         if (kp->opt & OPT_CLAMP)
2023             wrap = GL_CLAMP_TO_EDGE;
2024
2025         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
2026         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
2027
2028         /* Apply the texture coordinate offset and scale. */
2029
2030         if (O->Mloc[ki] >= 0)
2031         {
2032             GLfloat T[16];
2033
2034             memset(T, 0, sizeof (T));
2035
2036             T[ 0] = kp->s[0];
2037             T[ 5] = kp->s[1];
2038             T[10] = kp->s[2];
2039             T[12] = kp->o[0];
2040             T[13] = kp->o[1];
2041             T[14] = kp->o[2];
2042             T[15] = 1.0f;
2043
2044             glUniformMatrix4fv(O->Mloc[ki], 1, GL_FALSE, T);
2045         }
2046     }
2047     else glBindTexture(GL_TEXTURE_2D, 0);
2048 }
2049
2050 void obj_render_mtrl(const struct obj *O, int mi)
2051 {
2052     int ki;
2053
2054     /* Bind all material properties and texture maps. */
2055
2056     for (ki = 0; ki < OBJ_PROP_COUNT; ki++)
2057     {
2058         if (O->oloc[ki] >= 0)
2059         {
2060             glActiveTexture(GL_TEXTURE0 + ki);
2061             obj_render_prop(O, mi, ki);
2062             glUniform1i(O->oloc[ki], ki);
2063         }
2064         if (O->cloc[ki] >= 0)
2065             glUniform4fv(O->cloc[ki], 1, O->mv[mi].kv[ki].c);
2066     }
2067     glActiveTexture(GL_TEXTURE0);
2068 }
2069
2070 void obj_render_surf(const struct obj *O, int si)
2071 {
2072     const struct obj_surf *sp = O->sv + si;
2073
2074     if (0 < sp->pc || sp->lc > 0)
2075     {
2076         /* Apply this surface's material. */
2077
2078         if (0 <= sp->mi && sp->mi < O->mc)
2079             obj_render_mtrl(O, sp->mi);
2080
2081         /* Render all polygons. */
2082
2083         if (sp->pibo)
2084         {
2085             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sp->pibo);
2086             glDrawElements(GL_TRIANGLES, 3 * sp->pc, GL_INDEX_T, (const GLvoid *) 0);
2087         }
2088
2089         /* Render all lines. */
2090
2091         if (sp->libo)
2092         {
2093             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sp->libo);
2094             glDrawElements(GL_LINES, 2 * sp->lc, GL_INDEX_T, (const GLvoid *) 0);
2095         }
2096     }
2097 }
2098
2099 void obj_render(struct obj *O)
2100 {
2101     int si;
2102
2103     assert(O);
2104
2105     /* Initialize the vertex arrays. */
2106
2107     obj_init(O);
2108
2109     /* Render each surface. */
2110
2111     glBindVertexArray(O->vao);
2112
2113     for (si = 0; si < O->sc; ++si)
2114         obj_render_surf(O, si);
2115 }
2116
2117 #else
2118
2119 void obj_render(struct obj *O)
2120 {
2121 }
2122
2123 #endif
2124
2125 /*============================================================================*/
2126
2127 void obj_bound(const struct obj *O, float *b)
2128 {
2129     int vi;
2130
2131     assert(O);
2132
2133     /* Compute the bounding box of this object. */
2134
2135     if (O->vc > 0)
2136     {
2137         const float *v = O->vv[0].v;
2138
2139         b[0] = b[3] = v[0];
2140         b[1] = b[4] = v[1];
2141         b[2] = b[5] = v[2];
2142     }
2143
2144     for (vi = 0; vi < O->vc; ++vi)
2145     {
2146         const float *v = O->vv[vi].v;
2147
2148         if (b[0] > v[0]) b[0] = v[0];
2149         if (b[1] > v[1]) b[1] = v[1];
2150         if (b[2] > v[2]) b[2] = v[2];
2151
2152         if (b[3] < v[0]) b[3] = v[0];
2153         if (b[4] < v[1]) b[4] = v[1];
2154         if (b[5] < v[2]) b[5] = v[2];
2155     }
2156 }
2157
2158 /*============================================================================*/
2159
2160 static void obj_write_map(FILE *fout, const struct obj *O, int mi, int ki, const char *s)
2161 {
2162     struct obj_prop *kp = O->mv[mi].kv + ki;
2163
2164     /* If this property has a map... */
2165
2166     if (kp->str)
2167     {
2168         fprintf(fout, "map_%s ", s);
2169
2170         /* Store all map options. */
2171
2172         if ((kp->opt & OPT_CLAMP) != 0) fprintf(fout, "-clamp on ");
2173
2174         /* Store the map offset, if any. */
2175
2176         if (fabs(kp->o[0]) > 0 ||
2177             fabs(kp->o[1]) > 0 ||
2178             fabs(kp->o[2]) > 0) fprintf(fout, "-o %f %f %f ",
2179                                         kp->o[0], kp->o[1], kp->o[2]);
2180
2181         /* Store the map scale, if any. */
2182
2183         if (fabs(kp->s[0] - 1) > 0 ||
2184             fabs(kp->s[1] - 1) > 0 ||
2185             fabs(kp->s[2] - 1) > 0) fprintf(fout, "-s %f %f %f ",
2186                                             kp->s[0], kp->s[1], kp->s[2]);
2187
2188         /* Store the map image file name. */
2189
2190         fprintf(fout, "%s\n", kp->str);
2191     }
2192 }
2193
2194 static void obj_write_mtl(const struct obj *O, const char *mtl)
2195 {
2196     FILE *fout;
2197     int   mi;
2198
2199     if ((fout = fopen(mtl, "w")))
2200     {
2201         for (mi = 0; mi < O->mc; ++mi)
2202         {
2203             struct obj_mtrl *mp = O->mv + mi;
2204
2205             /* Start a new material. */
2206
2207             if (mp->name)
2208                 fprintf(fout, "newmtl %s\n", mp->name);
2209             else
2210                 fprintf(fout, "newmtl default\n");
2211
2212             /* Store all material property colors. */
2213
2214             fprintf(fout, "Kd %12.8f %12.8f %12.8f\n", mp->kv[OBJ_KD].c[0],
2215                                                        mp->kv[OBJ_KD].c[1],
2216                                                        mp->kv[OBJ_KD].c[2]);
2217             fprintf(fout, "Ka %12.8f %12.8f %12.8f\n", mp->kv[OBJ_KA].c[0],
2218                                                        mp->kv[OBJ_KA].c[1],
2219                                                        mp->kv[OBJ_KA].c[2]);
2220             fprintf(fout, "Ke %12.8f %12.8f %12.8f\n", mp->kv[OBJ_KE].c[0],
2221                                                        mp->kv[OBJ_KE].c[1],
2222                                                        mp->kv[OBJ_KE].c[2]);
2223             fprintf(fout, "Ks %12.8f %12.8f %12.8f\n", mp->kv[OBJ_KS].c[0],
2224                                                        mp->kv[OBJ_KS].c[1],
2225                                                        mp->kv[OBJ_KS].c[2]);
2226
2227             fprintf(fout, "Ns %12.8f\n", mp->kv[OBJ_NS].c[0]);
2228             fprintf(fout, "d  %12.8f\n", mp->kv[OBJ_KD].c[3]);
2229
2230             /* Store all material property maps. */
2231
2232             obj_write_map(fout, O, mi, OBJ_KD, "Kd");
2233             obj_write_map(fout, O, mi, OBJ_KA, "Ka");
2234             obj_write_map(fout, O, mi, OBJ_KA, "Ke");
2235             obj_write_map(fout, O, mi, OBJ_KS, "Ks");
2236             obj_write_map(fout, O, mi, OBJ_NS, "Ns");
2237             obj_write_map(fout, O, mi, OBJ_KN, "Kn");
2238         }
2239     }
2240     fclose(fout);
2241 }
2242
2243 static void obj_write_obj(const struct obj *O, const char *obj,
2244                                         const char *mtl, int prec)
2245 {
2246     FILE *fout;
2247
2248     if ((fout = fopen(obj, "w")))
2249     {
2250         char formv[256];
2251         char formt[256];
2252         char formn[256];
2253
2254         int si;
2255         int vi;
2256         int pi;
2257         int li;
2258
2259         if (mtl) fprintf(fout, "mtllib %s\n", mtl);
2260
2261         /* Store all vertex data. */
2262
2263         sprintf(formv, "v %%.%df %%.%df %%.%df\n",  prec, prec, prec);
2264         sprintf(formt, "vt %%.%df %%.%df\n",        prec, prec);
2265         sprintf(formn, "vn %%.%df %%.%df %%.%df\n", prec, prec, prec);
2266
2267         for (vi = 0; vi < O->vc; ++vi)
2268             fprintf(fout, formv, O->vv[vi].v[0],
2269                                  O->vv[vi].v[1],
2270                                  O->vv[vi].v[2]);
2271         for (vi = 0; vi < O->vc; ++vi)
2272             fprintf(fout, formt, O->vv[vi].t[0],
2273                                  O->vv[vi].t[1]);
2274         for (vi = 0; vi < O->vc; ++vi)
2275             fprintf(fout, formn, O->vv[vi].n[0],
2276                                  O->vv[vi].n[1],
2277                                  O->vv[vi].n[2]);
2278
2279         for (si = 0; si < O->sc; ++si)
2280         {
2281             int mi = O->sv[si].mi;
2282
2283             /* Store the surface's material reference */
2284
2285             if (0 <= mi && mi < O->mc && O->mv[mi].name)
2286                 fprintf(fout, "usemtl %s\n", O->mv[O->sv[si].mi].name);
2287             else
2288                 fprintf(fout, "usemtl default\n");
2289
2290             /* Store all polygon definitions. */
2291
2292             for (pi = 0; pi < O->sv[si].pc; pi++)
2293             {
2294                 int vi0 = O->sv[si].pv[pi].vi[0] + 1;
2295                 int vi1 = O->sv[si].pv[pi].vi[1] + 1;
2296                 int vi2 = O->sv[si].pv[pi].vi[2] + 1;
2297
2298                 fprintf(fout, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", vi0, vi0, vi0,
2299                                                                 vi1, vi1, vi1,
2300                                                                 vi2, vi2, vi2);
2301             }
2302
2303             /* Store all line definitions. */
2304
2305             for (li = 0; li < O->sv[si].lc; li++)
2306             {
2307                 int vi0 = O->sv[si].lv[li].vi[0] + 1;
2308                 int vi1 = O->sv[si].lv[li].vi[1] + 1;
2309
2310                 fprintf(fout, "l %d/%d/%d %d/%d/%d\n", vi0, vi0, vi0,
2311                                                        vi1, vi1, vi1);
2312             }
2313         }
2314
2315         fclose(fout);
2316     }
2317 }
2318
2319 void obj_write(const struct obj *O, const char *obj, const char *mtl, int prec)
2320 {
2321     assert(O);
2322
2323     if (obj) obj_write_obj(O, obj, mtl, prec);
2324     if (mtl) obj_write_mtl(O, mtl);
2325 }
2326
2327 /*============================================================================*/