1 /* Copyright (c) 2005,2013,2014 Robert Kooima */
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: */
10 /* The above copyright notice and this permission notice shall be included in */
11 /* all copies or substantial portions of the Software. */
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. */
32 # include <OpenGL/gl3.h>
37 #ifdef OBJ_INDEX_IS_INT
38 typedef GL_UNSIGNED_INT GL_INDEX_T;
40 typedef GL_UNSIGNED_SHORT GL_INDEX_T;
47 static void invalidate(struct obj *);
49 /*----------------------------------------------------------------------------*/
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); }
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); }
65 /*============================================================================*/
96 static struct vec3 *_vv;
97 static struct vec2 *_tv;
98 static struct vec3 *_nv;
99 static struct iset *_iv;
101 /*----------------------------------------------------------------------------*/
103 static int add__(void **_v, int *_c, int *_m, size_t _s)
105 int m = (*_m > 0) ? *_m * 2 : 2;
108 /* If space remains in the current block, return it. */
113 /* Else, try to increase the size of the block. */
115 else if ((v = realloc(*_v, _s * m)))
122 /* Else, indicate failure. */
127 static int add_v(void)
129 return add__((void **) &_vv, &_vc, &_vm, sizeof (struct vec3));
132 static int add_t(void)
134 return add__((void **) &_tv, &_tc, &_tm, sizeof (struct vec2));
137 static int add_n(void)
139 return add__((void **) &_nv, &_nc, &_nm, sizeof (struct vec3));
142 static int add_i(void)
144 return add__((void **) &_iv, &_ic, &_im, sizeof (struct iset));
147 /*============================================================================*/
148 /* Handy functions */
150 static void cross(float *z, const float *x, const float *y)
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];
163 static void normalize(float *v)
165 float k = 1.0f / (float) sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
172 static void normal(float *n, const float *a,
191 /*============================================================================*/
194 struct array *obj_load_image(const char *filename)
196 unsigned int obj_load_image(const char *filename)
200 struct array *o = NULL;
207 /* Read the image data from the named file to a new pixel buffer. */
208 struct tga *tga = tga_read(filename);
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. */
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);
221 /* Create an OpenGL texture object using these pixels. */
222 glGenTextures(1, &o);
223 glBindTexture(GL_TEXTURE_2D, o);
225 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
226 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
229 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0,
230 GL_BGRA, GL_UNSIGNED_BYTE, p);
232 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0,
233 GL_BGR, GL_UNSIGNED_BYTE, p);
235 glGenerateMipmap(GL_TEXTURE_2D);
238 /* Discard the unnecessary pixel buffer. */
245 static void dirpath(char *pathname)
249 /* Find the path by cutting a file name at the last directory delimiter. */
251 for (i = (int) strlen(pathname) - 1; i >= 0; --i)
252 if (pathname[i] == '/' || pathname[i] == '\\')
258 /* If no delimiter was found, return the current directory. */
260 strcpy(pathname, ".");
263 /*----------------------------------------------------------------------------*/
265 static void read_image(struct obj *O, int mi, int ki, const char *line,
268 unsigned int clamp = 0;
270 float o[3] = { 0.0f, 0.0f, 0.0f };
271 float s[3] = { 1.0f, 1.0f, 1.0f };
273 char pathname[MAXSTR];
280 memset(map, 0, MAXSTR);
281 memset(val, 0, MAXSTR);
283 while (line[0] != '\0' && line[0] != '\r' && line[0] != '\n')
287 while(isspace(line[0])) line++;
289 /* Parse property map options. */
291 if (sscanf(line, "-clamp %s%n", val, &n) >= 1)
293 clamp = (strcmp(val, "on") == 0) ? OPT_CLAMP : 0;
297 /* Parse property map scale. */
299 else if (sscanf(line, "-s %f %f %f%n", s + 0, s + 1, s + 2, &n) >= 3)
301 else if (sscanf(line, "-s %f %f%n", s + 0, s + 1, &n) >= 2)
303 else if (sscanf(line, "-s %f%n", s + 0, &n) >= 1)
306 /* Parse property map offset. */
308 else if (sscanf(line, "-o %f %f %f%n", o + 0, o + 1, o + 2, &n) >= 3)
310 else if (sscanf(line, "-o %f %f%n", o + 0, o + 1, &n) >= 2)
312 else if (sscanf(line, "-o %f%n", o + 0, &n) >= 1)
315 /* Check for a file name */
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; }
320 /* If we see something we don't recognize, stop looking. */
325 /* Apply all parsed property attributes to the material. */
327 sprintf(pathname, "%s/%s", path, map);
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);
335 static void read_color(struct obj *O, int mi, int ki, const char *line)
339 /* Merge incoming color components with existing defaults. */
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);
346 static void read_alpha(struct obj *O, int mi, int ki, const char *line)
351 /* Merge incoming color components with existing defaults. */
353 obj_get_mtrl_c(O, mi, ki, c);
354 sscanf(line, "%f", &t);
356 obj_set_mtrl_c(O, mi, ki, c);
359 static void read_mtl(const char *path,
361 const char *name, struct obj *O, int mi)
363 char pathname[MAXSTR];
374 sprintf(pathname, "%s/%s", path, file);
376 if ((fin = fopen(pathname, "r")))
378 /* Process each line of the MTL file. */
380 while (fgets (buf, MAXSTR, fin))
381 if (sscanf(buf, "%s%n", key, &n) >= 1)
383 const char *c = buf + n;
387 /* Determine if we've found the MTL we're looking for. */
389 if (!strcmp(key, "newmtl"))
391 sscanf(c, "%s", arg);
393 if ((scanning = strcmp(arg, name)) == 0)
394 obj_set_mtrl_name(O, mi, name);
399 /* Stop scanning when the next MTL begins. */
401 if (!strcmp(key, "newmtl"))
404 /* Parse this material's properties. */
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);
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);
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);
440 static void read_mtllib(char *file, const char *line)
442 /* Parse the first file name from the given line. */
444 sscanf(line, "%s", file);
447 static int read_usemtl(const char *path,
449 const char *line, struct obj *O)
456 sscanf(line, "%s", name);
458 /* Create a new material for the incoming definition. */
460 if ((mi = obj_add_mtrl(O)) >= 0)
462 /* Create a new surface to contain geometry with the new material. */
464 if ((si = obj_add_surf(O)) >= 0)
466 /* Read the material definition and apply it to the new surface. */
468 read_mtl(path, file, name, O, mi);
469 obj_set_surf(O, si, mi);
471 /* Return the surface so that new geometry may be added to it. */
477 /* On failure, return the default surface. */
482 /*----------------------------------------------------------------------------*/
484 static int read_poly_indices(const char *line, int *_vi, int *_ti, int *_ni)
492 /* Parse a face vertex specification from the given line. */
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;
502 static int read_poly_vertices(const char *line, struct obj *O, int gi)
504 const char *c = line;
516 /* Scan the face string, converting index sets to vertices. */
518 while ((dc = read_poly_indices(c, &_vi, &_ti, &_ni)))
520 /* Convert face indices to vector cache indices. */
522 _vi += (_vi < 0) ? _vc : -1;
523 _ti += (_ti < 0) ? _tc : -1;
524 _ni += (_ni < 0) ? _nc : -1;
526 /* Initialize a new index set. */
528 if ((_ii = add_i()) >= 0)
534 /* Search the vector reference list for a repeated index set. */
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 &&
542 /* A repeat has been found. Link new to old. */
546 _iv[_ii]. vi = _iv[_ij].vi;
547 _iv[_ii]. gi = _iv[_ij].gi;
552 /* If no repeat was found, add a new vertex. */
554 if ((_ij < 0) && (vi = obj_add_vert(O)) >= 0)
561 /* Initialize the new vertex using valid cache references. */
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);
574 static void read_f(const char *line, struct obj *O, int si, int gi)
580 /* Create new vertex references for this face. */
583 int ic = read_poly_vertices(line, O, gi);
585 /* If smoothing, apply this face's normal to vertices that need it. */
589 normal(n, _vv[_iv[i0 + 0]._vi].v,
590 _vv[_iv[i0 + 1]._vi].v,
591 _vv[_iv[i0 + 2]._vi].v);
593 for (i = 0; i < ic; ++i)
594 if (_iv[i0 + 0]._ni < 0)
596 obj_get_vert_n(O, _iv[i0 + i]._vi, t);
600 obj_set_vert_n(O, _iv[i0 + i]._vi, t);
604 /* Convert our N new vertex references into N-2 new triangles. */
606 for (i = 0; i < ic - 2; ++i)
608 if ((pi = obj_add_poly(O, si)) >= 0)
613 vi[1] = _iv[i0 + i + 1].vi;
614 vi[2] = _iv[i0 + i + 2].vi;
616 obj_set_poly(O, si, pi, vi);
620 /*----------------------------------------------------------------------------*/
622 static int read_line_indices(const char *line, int *_vi, int *_ti)
629 /* Parse a line vertex specification from the given line. */
631 if (sscanf(line, "%d/%d%n", _vi, _ti, &n) >= 2) return n;
632 if (sscanf(line, "%d%n", _vi, &n) >= 1) return n;
637 static int read_line_vertices(const char *line, struct obj *O)
639 const char *c = line;
650 /* Scan the line string, converting index sets to vertices. */
652 while ((dc = read_line_indices(c, &_vi, &_ti)))
654 /* Convert line indices to vector cache indices. */
656 _vi += (_vi < 0) ? _vc : -1;
657 _ti += (_ti < 0) ? _tc : -1;
659 /* Initialize a new index set. */
661 if ((_ii = add_i()) >= 0)
666 /* Search the vector reference list for a repeated index set. */
668 for (_ij = _vv[_vi]._ii; _ij >= 0; _ij = _iv[_ij]._ii)
669 if (_iv[_ij]._vi == _vi &&
672 /* A repeat has been found. Link new to old. */
676 _iv[_ii]. vi = _iv[_ij].vi;
681 /* If no repeat was found, add a new vertex. */
683 if ((_ij < 0) && (vi = obj_add_vert(O)) >= 0)
689 /* Initialize the new vertex using valid cache references. */
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);
701 static void read_l(const char *line, struct obj *O, int si)
705 /* Create new vertices for this line. */
708 int ic = read_line_vertices(line, O);
710 /* Convert our N new vertices into N-1 new lines. */
712 for (i = 0; i < ic - 1; ++i)
714 if ((li = obj_add_line(O, si)) >= 0)
718 vi[0] = _iv[i0 + i ].vi;
719 vi[1] = _iv[i0 + i + 1].vi;
721 obj_set_line(O, si, li, vi);
725 /*----------------------------------------------------------------------------*/
727 static void read_v(const char *line)
731 /* Parse a vertex position. */
733 if ((_vi = add_v()) >= 0)
735 sscanf(line, "%f %f %f", _vv[_vi].v + 0,
742 static void read_vt(const char *line)
746 /* Parse a texture coordinate. */
748 if ((_ti = add_t()) >= 0)
750 sscanf(line, "%f %f", _tv[_ti].v + 0,
756 static void read_vn(const char *line)
760 /* Parse a normal. */
762 if ((_ni = add_n()) >= 0)
764 sscanf(line, "%f %f %f", _nv[_ni].v + 0,
771 /*----------------------------------------------------------------------------*/
773 static void read_obj(struct obj *O, const char *filename)
783 /* Flush the vector caches. */
790 /* Add the named file to the given object. */
792 if ((fin = fopen(filename, "r")))
794 /* Ensure there exists a default surface 0 and default material 0. */
796 int si = obj_add_surf(O);
797 int mi = obj_add_mtrl(O);
801 obj_set_surf(O, si, mi);
803 /* Extract the directory from the filename for use in MTL loading. */
805 strncpy(D, filename, MAXSTR);
808 /* Process each line of the OBJ file, invoking the handler for each. */
810 while (fgets (buf, MAXSTR, fin))
811 if (sscanf(buf, "%s%n", key, &n) >= 1)
813 const char *c = buf + n;
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);
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);
830 /*----------------------------------------------------------------------------*/
832 static void obj_rel_mtrl(struct obj_mtrl *mp)
834 /* Release any resources held by this material. */
838 for (ki = 0; ki < OBJ_PROP_COUNT; ki++)
840 if (mp->kv[ki].str) free(mp->kv[ki].str);
842 if (mp->kv[ki].map) glDeleteTextures(1, &mp->kv[ki].map);
847 static void obj_rel_surf(struct obj_surf *sp)
850 if (sp->pibo) glDeleteBuffers(1, &sp->pibo);
851 if (sp->libo) glDeleteBuffers(1, &sp->libo);
857 /* Release this surface's polygon and line vectors. */
859 if (sp->pv) free(sp->pv);
860 if (sp->lv) free(sp->lv);
863 static void obj_rel(struct obj *O)
868 /* Release resources held by this file and it's materials and surfaces. */
871 if (O->vbo) glDeleteBuffers (1, &O->vbo);
872 if (O->vao) glDeleteVertexArrays(1, &O->vao);
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);
881 /*============================================================================*/
883 struct obj *obj_create(const char *filename)
888 /* Allocate and initialize a new file. */
890 if ((O = (struct obj *) calloc(1, sizeof (struct obj))))
894 /* Read the named file. */
896 read_obj(O, filename);
898 /* Post-process the loaded object. */
904 /* Set default shader locations. */
906 for (i = 0; i < OBJ_PROP_COUNT; i++)
920 void obj_delete(struct obj *O)
929 /*----------------------------------------------------------------------------*/
931 int obj_add_mtrl(struct obj *O)
933 unsigned int opt = 0;
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 };
946 /* Allocate and initialize a new material. */
948 if ((mi = add__((void **) &O->mv,
950 &O->mm, sizeof (struct obj_mtrl))) >= 0)
952 memset(O->mv + mi, 0, sizeof (struct obj_mtrl));
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);
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);
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);
977 int obj_add_vert(struct obj *O)
983 /* Allocate and initialize a new vertex. */
985 if ((vi = add__((void **) &O->vv,
987 &O->vm, sizeof (struct obj_vert))) >= 0)
989 memset(O->vv + vi, 0, sizeof (struct obj_vert));
994 int obj_add_poly(struct obj *O, int si)
1000 /* Allocate and initialize a new polygon. */
1002 if ((pi = add__((void **) &O->sv[si].pv,
1004 &O->sv[si].pm, sizeof (struct obj_poly)))>=0)
1006 memset(O->sv[si].pv + pi, 0, sizeof (struct obj_poly));
1011 int obj_add_line(struct obj *O, int si)
1017 /* Allocate and initialize a new line. */
1019 if ((li = add__((void **) &O->sv[si].lv,
1021 &O->sv[si].lm, sizeof (struct obj_line)))>=0)
1023 memset(O->sv[si].lv + li, 0, sizeof (struct obj_line));
1028 int obj_add_surf(struct obj *O)
1034 /* Allocate and initialize a new surface. */
1036 if ((si = add__((void **) &O->sv,
1038 &O->sm, sizeof (struct obj_surf))) >= 0)
1040 memset(O->sv + si, 0, sizeof (struct obj_surf));
1045 /*----------------------------------------------------------------------------*/
1047 int obj_num_mtrl(const struct obj *O)
1053 int obj_num_vert(const struct obj *O)
1059 int obj_num_poly(const struct obj *O, int si)
1062 return O->sv[si].pc;
1065 int obj_num_line(const struct obj *O, int si)
1068 return O->sv[si].lc;
1071 int obj_num_surf(const struct obj *O)
1078 /*----------------------------------------------------------------------------*/
1080 void obj_del_mtrl(struct obj *O, int mi)
1086 /* Remove this material from the material vector. */
1088 obj_rel_mtrl(O->mv + mi);
1092 (O->mc - mi - 1) * sizeof (struct obj_mtrl));
1096 /* Remove all references to this material. */
1098 for (si = O->sc - 1; si >= 0; --si)
1100 struct obj_surf *sp = O->sv + si;
1103 obj_del_surf(O, si);
1110 void obj_del_vert(struct obj *O, int vi)
1118 /* Remove this vertex from the file's vertex vector. */
1122 (O->vc - vi - 1) * sizeof (struct obj_vert));
1126 /* Remove all references to this vertex from all surfaces. */
1128 for (si = 0; si < O->sc; ++si)
1130 /* Delete all referencing polygons. Decrement later references. */
1132 for (pi = O->sv[si].pc - 1; pi >= 0; --pi)
1134 struct obj_poly *pp = O->sv[si].pv + pi;
1136 if (pp->vi[0] == vi || pp->vi[1] == vi || pp->vi[2] == vi)
1137 obj_del_poly(O, si, pi);
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]--;
1146 /* Delete all referencing lines. Decrement later references. */
1148 for (li = O->sv[si].lc - 1; li >= 0; --li)
1150 struct obj_line *lp = O->sv[si].lv + li;
1152 if (lp->vi[0] == vi || lp->vi[1] == vi)
1153 obj_del_line(O, si, li);
1156 if (lp->vi[0] > vi) lp->vi[0]--;
1157 if (lp->vi[1] > vi) lp->vi[1]--;
1162 /* Schedule the VBO for refresh. */
1167 void obj_del_poly(struct obj *O, int si, int pi)
1169 assert_poly(O, si, pi);
1171 /* Remove this polygon from the surface's polygon vector. */
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));
1180 void obj_del_line(struct obj *O, int si, int li)
1182 assert_line(O, si, li);
1184 /* Remove this line from the surface's line vector. */
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));
1193 void obj_del_surf(struct obj *O, int si)
1197 /* Remove this surface from the file's surface vector. */
1199 obj_rel_surf(O->sv + si);
1203 (O->sc - si - 1) * sizeof (struct obj_surf));
1208 /*----------------------------------------------------------------------------*/
1210 static char *set_name(char *old, const char *src)
1217 if (src && (dst = (char *) malloc(strlen(src) + 1)))
1223 void obj_set_mtrl_name(struct obj *O, int mi, const char *name)
1226 O->mv[mi].name = set_name(O->mv[mi].name, name);
1229 void obj_set_mtrl_map(struct obj *O, int mi, int ki, const char *str)
1231 assert_prop(O, mi, ki);
1233 if (O->mv[mi].kv[ki].map)
1235 array_free(O->mv[mi].kv[ki].map); // note: may be NULL
1237 glDeleteTextures(1, &O->mv[mi].kv[ki].map);
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);
1244 void obj_set_mtrl_opt(struct obj *O, int mi, int ki, unsigned int opt)
1246 assert_prop(O, mi, ki);
1248 O->mv[mi].kv[ki].opt = opt;
1251 void obj_set_mtrl_c(struct obj *O, int mi, int ki, const float c[4])
1253 assert_prop(O, mi, ki);
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];
1261 void obj_set_mtrl_s(struct obj *O, int mi, int ki, const float s[3])
1263 assert_prop(O, mi, ki);
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];
1270 void obj_set_mtrl_o(struct obj *O, int mi, int ki, const float o[3])
1272 assert_prop(O, mi, ki);
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];
1279 /*----------------------------------------------------------------------------*/
1281 static void invalidate(struct obj *O)
1284 if (O->vbo) glDeleteBuffers (1, &O->vbo);
1285 if (O->vao) glDeleteVertexArrays(1, &O->vao);
1291 void obj_set_vert_v(struct obj *O, int vi, const float v[3])
1295 O->vv[vi].v[0] = v[0];
1296 O->vv[vi].v[1] = v[1];
1297 O->vv[vi].v[2] = v[2];
1302 void obj_set_vert_t(struct obj *O, int vi, const float t[2])
1306 O->vv[vi].t[0] = t[0];
1307 O->vv[vi].t[1] = t[1];
1312 void obj_set_vert_n(struct obj *O, int vi, const float n[3])
1316 O->vv[vi].n[0] = n[0];
1317 O->vv[vi].n[1] = n[1];
1318 O->vv[vi].n[2] = n[2];
1323 void obj_set_vert_u(struct obj *O, int vi, const float u[3])
1327 O->vv[vi].u[0] = u[0];
1328 O->vv[vi].u[1] = u[1];
1329 O->vv[vi].u[2] = u[2];
1334 /*----------------------------------------------------------------------------*/
1336 void obj_set_poly(struct obj *O, int si, int pi, const int vi[3])
1338 assert_poly(O, si, pi);
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];
1345 void obj_set_line(struct obj *O, int si, int li, const int vi[2])
1347 assert_line(O, si, li);
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];
1353 void obj_set_surf(struct obj *O, int si, int mi)
1360 /*----------------------------------------------------------------------------*/
1362 void obj_set_vert_loc(struct obj *O, int u, int n, int t, int v)
1374 void obj_set_prop_loc(struct obj *O, int ki, int c, int o, int M)
1377 assert(0 <= ki && ki < OBJ_PROP_COUNT);
1384 /*============================================================================*/
1386 const char *obj_get_mtrl_name(const struct obj *O, int mi)
1389 return O->mv[mi].name;
1393 struct array *obj_get_mtrl_map(const struct obj *O, int mi, int ki)
1395 unsigned int obj_get_mtrl_map(const struct obj *O, int mi, int ki)
1398 assert_prop(O, mi, ki);
1399 return O->mv[mi].kv[ki].map;
1402 unsigned int obj_get_mtrl_opt(const struct obj *O, int mi, int ki)
1404 assert_prop(O, mi, ki);
1405 return O->mv[mi].kv[ki].opt;
1408 void obj_get_mtrl_c(const struct obj *O, int mi, int ki, float *c)
1410 assert_prop(O, mi, ki);
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];
1418 void obj_get_mtrl_s(const struct obj *O, int mi, int ki, float *s)
1420 assert_prop(O, mi, ki);
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];
1427 void obj_get_mtrl_o(const struct obj *O, int mi, int ki, float *o)
1429 assert_prop(O, mi, ki);
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];
1436 /*----------------------------------------------------------------------------*/
1438 void obj_get_vert_v(const struct obj *O, int vi, float *v)
1442 v[0] = O->vv[vi].v[0];
1443 v[1] = O->vv[vi].v[1];
1444 v[2] = O->vv[vi].v[2];
1447 void obj_get_vert_t(const struct obj *O, int vi, float *t)
1451 t[0] = O->vv[vi].t[0];
1452 t[1] = O->vv[vi].t[1];
1455 void obj_get_vert_n(const struct obj *O, int vi, float *n)
1459 n[0] = O->vv[vi].n[0];
1460 n[1] = O->vv[vi].n[1];
1461 n[2] = O->vv[vi].n[2];
1464 /*----------------------------------------------------------------------------*/
1466 void obj_get_poly(const struct obj *O, int si, int pi, int *vi)
1468 assert_poly(O, si, pi);
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];
1475 void obj_get_line(const struct obj *O, int si, int li, int *vi)
1477 assert_line(O, si, li);
1479 vi[0] = (int) O->sv[si].lv[li].vi[0];
1480 vi[1] = (int) O->sv[si].lv[li].vi[1];
1483 int obj_get_surf(const struct obj *O, int si)
1486 return O->sv[si].mi;
1489 /*============================================================================*/
1491 void obj_mini(struct obj *O)
1496 /* Remove empty surfaces. */
1498 for (si = O->sc - 1; si >= 0; --si)
1499 if (O->sv[si].pc == 0 &&
1501 obj_del_surf(O, si);
1503 /* Remove unreferenced materials. */
1505 for (mi = O->mc - 1; mi >= 0; --mi)
1509 for (si = 0; si < O->sc; ++si)
1510 if (O->sv[si].mi == mi)
1514 obj_del_mtrl(O, mi);
1518 void obj_norm(struct obj *O)
1526 /* Zero the normals for all vertices. */
1528 for (vi = 0; vi < O->vc; ++vi)
1535 /* Compute normals for all faces. */
1537 for (si = 0; si < O->sc; ++si)
1538 for (pi = 0; pi < O->sv[si].pc; ++pi)
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];
1546 /* Compute the normal formed by these 3 vertices. */
1548 normal(n, v0->v, v1->v, v2->v);
1550 /* Sum this normal to all vertices. */
1566 void obj_proc(struct obj *O)
1575 /* Normalize all normals. Zero all tangent vectors. */
1577 for (vi = 0; vi < O->vc; ++vi)
1579 normalize(O->vv[vi].n);
1581 O->vv[vi].u[0] = 0.0f;
1582 O->vv[vi].u[1] = 0.0f;
1583 O->vv[vi].u[2] = 0.0f;
1586 /* Compute tangent vectors for all vertices. */
1588 for (si = 0; si < O->sc; ++si)
1589 for (pi = 0; pi < O->sv[si].pc; ++pi)
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];
1600 /* Compute the tangent vector for this polygon. */
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];
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];
1610 dt1 = v1->t[1] - v0->t[1];
1611 dt2 = v2->t[1] - v0->t[1];
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];
1619 /* Accumulate the tangent vectors for this polygon's vertices. */
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];
1626 /* Orthonormalize each tangent basis. */
1628 for (vi = 0; vi < O->vc; ++vi)
1630 float *n = O->vv[vi].n;
1631 float *u = O->vv[vi].u;
1640 /* Sort surfaces such that transparent ones appear later. */
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])
1647 struct obj_surf temp;
1650 O->sv[si] = O->sv[sj];
1655 void obj_init(struct obj *O)
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);
1666 /* Store the following bindings in a vertex array object. */
1668 glGenVertexArrays(1, &O->vao);
1669 glBindVertexArray( O->vao);
1671 /* Store all vertex data in a vertex buffer object. */
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);
1677 /* Store all index data in index buffer objects. */
1679 for (si = 0; si < O->sc; ++si)
1681 if (O->sv[si].pc > 0)
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);
1689 if (O->sv[si].lc > 0)
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);
1698 /* Enable and bind the attributes. */
1702 glEnableVertexAttribArray(O->uloc);
1703 glVertexAttribPointer(O->uloc, 3, GL_FLOAT, GL_FALSE, vs, (const GLvoid *) 0);
1707 glEnableVertexAttribArray(O->nloc);
1708 glVertexAttribPointer(O->nloc, 3, GL_FLOAT, GL_FALSE, vs, (const GLvoid *) 12);
1712 glEnableVertexAttribArray(O->tloc);
1713 glVertexAttribPointer(O->tloc, 2, GL_FLOAT, GL_FALSE, vs, (const GLvoid *) 24);
1717 glEnableVertexAttribArray(O->vloc);
1718 glVertexAttribPointer(O->vloc, 3, GL_FLOAT, GL_FALSE, vs, (const GLvoid *) 32);
1724 /*----------------------------------------------------------------------------*/
1726 int obj_cmp_vert(struct obj *O, int vi, int vj, float eps, float dot)
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;
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;
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;
1742 void obj_swp_vert(struct obj *O, int vi, int vj)
1748 /* Replace all occurrences of vi with vj. */
1750 for (si = 0; si < O->sc; ++si)
1752 for (pi = 0; pi < O->sv[si].pc; ++pi)
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;
1761 for (li = 0; li < O->sv[si].lc; ++li)
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;
1771 void obj_uniq(struct obj *O, float eps, float dot, int verbose)
1778 /* Merge all vertices within epsilon of one another. */
1780 for (vi = 0; vi < O->vc; vi += di)
1784 for (vj = 0; vj < vi; ++vj)
1786 if (obj_cmp_vert(O, vi, vj, eps, dot))
1788 if (verbose) printf("%d %d\n", vi, vc--);
1790 obj_swp_vert(O, vi, vj);
1791 obj_del_vert(O, vi);
1800 /*----------------------------------------------------------------------------*/
1802 void obj_sort(struct obj *O, int qc)
1804 const int vc = O->vc;
1808 int qs; /* Cache insertion serial number */
1809 int *iv; /* Polygon reference list buffer */
1810 int ic; /* Polygon reference list length */
1813 /* Vertex optimization data; vertex FIFO cache */
1815 struct vert *vv = (struct vert *) malloc(vc * sizeof (struct vert));
1816 int *qv = (int *) malloc(qc * sizeof (int ));
1818 int qs = 1; /* Current cache insertion serial number */
1819 int qi = 0; /* Current cache insertion point [0, qc) */
1827 /* Initialize the vertex cache to empty. */
1829 for (qj = 0; qj < qc; ++qj)
1832 /* Process each surface of this file in turn. */
1834 for (si = 0; si < O->sc; ++si)
1836 const int pc = O->sv[si].pc;
1838 /* Allocate the polygon reference list buffers. */
1840 int *ip, *iv = (int *) malloc(3 * pc * sizeof (int));
1842 /* Count the number of polygon references per vertex. */
1844 memset(vv, 0, vc * sizeof (struct vert));
1846 for (pi = 0; pi < pc; ++pi)
1848 const obj_index_t *i = O->sv[si].pv[pi].vi;
1855 /* Initialize all vertex optimization data. */
1857 for (vi = 0, ip = iv; vi < vc; ++vi)
1865 /* Fill the polygon reference list buffers. */
1867 for (pi = 0; pi < pc; ++pi)
1869 const obj_index_t *i = O->sv[si].pv[pi].vi;
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;
1876 /* Iterate over the polygon array of this surface. */
1878 for (pi = 0; pi < pc; ++pi)
1880 const obj_index_t *i = O->sv[si].pv[pi].vi;
1884 int dk = -1; /* The best polygon score */
1885 int pk = pi; /* The best polygon index */
1887 /* Find the best polygon among those referred-to by the cache. */
1889 for (qj = 0; qj < qc; ++qj)
1892 for (ii = 0; ii < vv[qv[qj]].ic; ++ii)
1894 int pj = vv[qv[qj]].iv[ii];
1897 const obj_index_t *j = O->sv[si].pv[pj].vi;
1899 /* Recently-used vertex bonus. */
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;
1905 /* Low-valence vertex bonus. */
1920 struct obj_poly temp;
1922 /* Update the polygon reference list. */
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)
1928 vv[i[vi]].iv[ii] = pk;
1932 /* Swap the best polygon into the current position. */
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;
1939 /* Iterate over the current polygon's vertices. */
1941 for (vi = 0; vi < 3; ++vi)
1943 struct vert *vp = vv + i[vi];
1945 /* If this vertex was a cache miss then queue it. */
1947 if (qs - vp->qs >= qc)
1954 /* Remove the current polygon from the reference list. */
1958 for (ii = 0; ii < vp->ic; ++ii)
1959 if (vp->iv[ii] == pk)
1961 vp->iv[ii] = vp->iv[vp->ic];
1972 float obj_acmr(struct obj *O, int qc)
1974 int *vs = (int *) malloc(O->vc * sizeof (int));
1984 for (si = 0; si < O->sc; ++si)
1986 for (vi = 0; vi < O->vc; ++vi)
1989 for (pi = 0; pi < O->sv[si].pc; ++pi)
1991 const obj_index_t *i = O->sv[si].pv[pi].vi;
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++; }
2001 return (float) nn / (float) dd;
2004 /*----------------------------------------------------------------------------*/
2008 static void obj_render_prop(const struct obj *O, int mi, int ki)
2010 const struct obj_prop *kp = O->mv[mi].kv + ki;
2014 GLenum wrap = GL_REPEAT;
2016 /* Bind the property map. */
2018 glBindTexture(GL_TEXTURE_2D, kp->map);
2020 /* Apply the property options. */
2022 if (kp->opt & OPT_CLAMP)
2023 wrap = GL_CLAMP_TO_EDGE;
2025 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap);
2026 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap);
2028 /* Apply the texture coordinate offset and scale. */
2030 if (O->Mloc[ki] >= 0)
2034 memset(T, 0, sizeof (T));
2044 glUniformMatrix4fv(O->Mloc[ki], 1, GL_FALSE, T);
2047 else glBindTexture(GL_TEXTURE_2D, 0);
2050 void obj_render_mtrl(const struct obj *O, int mi)
2054 /* Bind all material properties and texture maps. */
2056 for (ki = 0; ki < OBJ_PROP_COUNT; ki++)
2058 if (O->oloc[ki] >= 0)
2060 glActiveTexture(GL_TEXTURE0 + ki);
2061 obj_render_prop(O, mi, ki);
2062 glUniform1i(O->oloc[ki], ki);
2064 if (O->cloc[ki] >= 0)
2065 glUniform4fv(O->cloc[ki], 1, O->mv[mi].kv[ki].c);
2067 glActiveTexture(GL_TEXTURE0);
2070 void obj_render_surf(const struct obj *O, int si)
2072 const struct obj_surf *sp = O->sv + si;
2074 if (0 < sp->pc || sp->lc > 0)
2076 /* Apply this surface's material. */
2078 if (0 <= sp->mi && sp->mi < O->mc)
2079 obj_render_mtrl(O, sp->mi);
2081 /* Render all polygons. */
2085 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sp->pibo);
2086 glDrawElements(GL_TRIANGLES, 3 * sp->pc, GL_INDEX_T, (const GLvoid *) 0);
2089 /* Render all lines. */
2093 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sp->libo);
2094 glDrawElements(GL_LINES, 2 * sp->lc, GL_INDEX_T, (const GLvoid *) 0);
2099 void obj_render(struct obj *O)
2105 /* Initialize the vertex arrays. */
2109 /* Render each surface. */
2111 glBindVertexArray(O->vao);
2113 for (si = 0; si < O->sc; ++si)
2114 obj_render_surf(O, si);
2119 void obj_render(struct obj *O)
2125 /*============================================================================*/
2127 void obj_bound(const struct obj *O, float *b)
2133 /* Compute the bounding box of this object. */
2137 const float *v = O->vv[0].v;
2144 for (vi = 0; vi < O->vc; ++vi)
2146 const float *v = O->vv[vi].v;
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];
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];
2158 /*============================================================================*/
2160 static void obj_write_map(FILE *fout, const struct obj *O, int mi, int ki, const char *s)
2162 struct obj_prop *kp = O->mv[mi].kv + ki;
2164 /* If this property has a map... */
2168 fprintf(fout, "map_%s ", s);
2170 /* Store all map options. */
2172 if ((kp->opt & OPT_CLAMP) != 0) fprintf(fout, "-clamp on ");
2174 /* Store the map offset, if any. */
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]);
2181 /* Store the map scale, if any. */
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]);
2188 /* Store the map image file name. */
2190 fprintf(fout, "%s\n", kp->str);
2194 static void obj_write_mtl(const struct obj *O, const char *mtl)
2199 if ((fout = fopen(mtl, "w")))
2201 for (mi = 0; mi < O->mc; ++mi)
2203 struct obj_mtrl *mp = O->mv + mi;
2205 /* Start a new material. */
2208 fprintf(fout, "newmtl %s\n", mp->name);
2210 fprintf(fout, "newmtl default\n");
2212 /* Store all material property colors. */
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]);
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]);
2230 /* Store all material property maps. */
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");
2243 static void obj_write_obj(const struct obj *O, const char *obj,
2244 const char *mtl, int prec)
2248 if ((fout = fopen(obj, "w")))
2259 if (mtl) fprintf(fout, "mtllib %s\n", mtl);
2261 /* Store all vertex data. */
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);
2267 for (vi = 0; vi < O->vc; ++vi)
2268 fprintf(fout, formv, O->vv[vi].v[0],
2271 for (vi = 0; vi < O->vc; ++vi)
2272 fprintf(fout, formt, O->vv[vi].t[0],
2274 for (vi = 0; vi < O->vc; ++vi)
2275 fprintf(fout, formn, O->vv[vi].n[0],
2279 for (si = 0; si < O->sc; ++si)
2281 int mi = O->sv[si].mi;
2283 /* Store the surface's material reference */
2285 if (0 <= mi && mi < O->mc && O->mv[mi].name)
2286 fprintf(fout, "usemtl %s\n", O->mv[O->sv[si].mi].name);
2288 fprintf(fout, "usemtl default\n");
2290 /* Store all polygon definitions. */
2292 for (pi = 0; pi < O->sv[si].pc; pi++)
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;
2298 fprintf(fout, "f %d/%d/%d %d/%d/%d %d/%d/%d\n", vi0, vi0, vi0,
2303 /* Store all line definitions. */
2305 for (li = 0; li < O->sv[si].lc; li++)
2307 int vi0 = O->sv[si].lv[li].vi[0] + 1;
2308 int vi1 = O->sv[si].lv[li].vi[1] + 1;
2310 fprintf(fout, "l %d/%d/%d %d/%d/%d\n", vi0, vi0, vi0,
2319 void obj_write(const struct obj *O, const char *obj, const char *mtl, int prec)
2323 if (obj) obj_write_obj(O, obj, mtl, prec);
2324 if (mtl) obj_write_mtl(O, mtl);
2327 /*============================================================================*/