1 // obj.cxx -- routines to handle WaveFront .obj format files.
3 // Written by Curtis Olson, started October 1997.
5 // Copyright (C) 1997 - 1998 Curtis L. Olson - curt@me.umn.edu
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 // General Public License for more details.
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 // (Log is kept at end of this file)
30 #include "Include/compiler.h"
32 #ifdef NEEDNAMESPACESTD
38 #include <Math/mat3.h>
39 #include <Math/point3d.hxx>
42 typedef vector < Point3D > container3;
43 typedef container3::iterator iterator3;
44 typedef container3::const_iterator const_iterator3;
47 // what do ya' know, here's some global variables
50 static int faces[MAXNODES][3];
53 static int ccw_list[MAXNODES];
56 static int cw_list[MAXNODES];
64 // some simple list routines
67 void list_init(int *list_ptr) {
73 void list_add(int *list, int *list_ptr, int node) {
74 if ( *list_ptr >= MAXNODES ) {
75 printf("ERROR: list overflow in list_add()\n");
79 list[*list_ptr] = node;
82 // printf("list pointer = %d adding %d\n", *list_ptr, node);
86 // fix the cw list and append to ccw_list
87 void fix_cw_list(int *list, int list_ptr) {
91 printf("List is empty ... skipping\n");
95 printf("Fixing cw list, size = %d\n", list_ptr);
98 while ( i < list_ptr ) {
103 // scan rest of strip (until -1)
104 while ( ((i+len) < list_ptr) && (list[i+len] != -1) ) {
105 // printf("len = %d item = %d\n", len, list[i+len] );
108 // printf(" Final length = %d\n", len);
110 if ( (len % 2) != 0 ) {
111 // if length is odd, just reverse order of nodes to
113 if ( ccw_list_ptr ) {
114 list_add(ccw_list, &ccw_list_ptr, -1);
116 for ( j = i + len - 1; j >= i; j-- ) {
117 // printf(" odd -> item = %d\n", list[j] );
118 list_add(ccw_list, &ccw_list_ptr, list[j]);
121 // if length is even, reverse order of (n-1) nodes to
122 // reverse winding, and create an orphan triangle for the
124 if ( ccw_list_ptr ) {
125 list_add(ccw_list, &ccw_list_ptr, -1);
127 for ( j = i + len - 2; j >= i; j-- ) {
128 // printf(" even -> item = %d\n", list[j] );
129 list_add(ccw_list, &ccw_list_ptr, list[j]);
132 // printf(" even bonus -> item = %d\n", list[i + len - 1] );
133 // printf(" even bonus -> item = %d\n", list[i + len - 2] );
134 // printf(" even bonus -> item = %d\n", list[i + len - 3] );
135 list_add(ccw_list, &ccw_list_ptr, -1);
136 list_add(ccw_list, &ccw_list_ptr, list[i + len - 3]);
137 list_add(ccw_list, &ccw_list_ptr, list[i + len - 2]);
138 list_add(ccw_list, &ccw_list_ptr, list[i + len - 1]);
146 void dump_global_bounds( void ) {
154 iterator3 current = nodes.begin();
155 iterator3 last = nodes.end();
157 // skip first dummy node
160 for ( ; current != last; ++current ) {
161 dist = ref.distance3D(*current);
162 // cout << "node = " << *current << " dist = " << dist << endl;
164 if ( dist > radius ) {
170 "gbs %.5f %.5f %.5f %.2f\n",
171 ref.x(), ref.y(), ref.z(), radius);
176 void dump_nodes( void ) {
181 iterator3 current = nodes.begin();
182 iterator3 last = nodes.end();
184 // skip first dummy node
187 for ( ; current != last; ++current ) {
189 fprintf( out, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
195 void dump_normals( void ) {
200 iterator3 current = normals.begin();
201 iterator3 last = normals.end();
203 // skip first dummy normal
206 for ( ; current != last; ++current ) {
208 fprintf(out, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
214 void dump_faces( void ) {
217 double xmax, xmin, ymax, ymin, zmax, zmin, dist, radius;
220 for ( i = 1; i < fcount; i++ ) {
225 // calc center of face
226 xmin = xmax = nodes[n1].x();
227 ymin = ymax = nodes[n1].y();
228 zmin = zmax = nodes[n1].z();
230 if ( nodes[n2].x() < xmin ) { xmin = nodes[n2].x(); }
231 if ( nodes[n2].x() > xmax ) { xmax = nodes[n2].x(); }
232 if ( nodes[n2].y() < ymin ) { ymin = nodes[n2].y(); }
233 if ( nodes[n2].y() > ymax ) { ymax = nodes[n2].y(); }
234 if ( nodes[n2].z() < zmin ) { zmin = nodes[n2].z(); }
235 if ( nodes[n2].z() > zmax ) { zmax = nodes[n2].z(); }
237 if ( nodes[n3].x() < xmin ) { xmin = nodes[n3].x(); }
238 if ( nodes[n3].x() > xmax ) { xmax = nodes[n3].x(); }
239 if ( nodes[n3].y() < ymin ) { ymin = nodes[n3].y(); }
240 if ( nodes[n3].y() > ymax ) { ymax = nodes[n3].y(); }
241 if ( nodes[n3].z() < zmin ) { zmin = nodes[n3].z(); }
242 if ( nodes[n3].z() > zmax ) { zmax = nodes[n3].z(); }
244 p = Point3D( (xmin + xmax) / 2.0,
246 (zmin + zmax) / 2.0 );
248 // calc bounding radius
249 radius = p.distance3D(nodes[n1]);
251 dist = p.distance3D(nodes[n2]);
252 if ( dist > radius ) { radius = dist; }
254 dist = p.distance3D(nodes[n3]);
255 if ( dist > radius ) { radius = dist; }
258 fprintf(out, "bs %.2f %.2f %.2f %.2f\n", p.x(), p.y(), p.z(), radius);
259 fprintf(out, "f %d %d %d\n", n1, n2, n3);
265 void dump_list(int *list, int list_ptr) {
267 double xmax, xmin, ymax, ymin, zmax, zmin, dist, radius;
270 if ( list_ptr < 3 ) {
271 printf("List is empty ... skipping\n");
275 printf("Dumping list, size = %d\n", list_ptr);
278 while ( i < list_ptr ) {
281 if ( (i % 2) == 0 ) {
282 fprintf(out, "\nusemtl desert1\n");
284 fprintf(out, "\nusemtl desert2\n");
287 // find length of next tri strip
289 // scan rest of strip (until -1)
290 while ( ((i+len) < list_ptr) && (list[i+len] != -1) ) {
291 // printf("len = %d item = %d\n", len, list[i+len] );
294 // printf("strip length = %d\n", len);
296 // calc center of face
298 xmin = xmax = nodes[n].x();
299 ymin = ymax = nodes[n].y();
300 zmin = zmax = nodes[n].z();
301 // printf("%.2f %.2f %.2f\n", nodes[n].x(), nodes[n].y(), nodes[n].z());
303 for ( j = i + 1; j < i + len; j++ ) {
304 // printf("j = %d\n", j);
306 if ( nodes[n].x() < xmin ) { xmin = nodes[n].x(); }
307 if ( nodes[n].x() > xmax ) { xmax = nodes[n].x(); }
308 if ( nodes[n].y() < ymin ) { ymin = nodes[n].y(); }
309 if ( nodes[n].y() > ymax ) { ymax = nodes[n].y(); }
310 if ( nodes[n].z() < zmin ) { zmin = nodes[n].z(); }
311 if ( nodes[n].z() > zmax ) { zmax = nodes[n].z(); }
312 // printf("%.2f %.2f %.2f\n", nodes[n].x(), nodes[n].y(), nodes[n].z());
314 p = Point3D( (xmin + xmax) / 2.0,
316 (zmin + zmax) / 2.0 );
317 // printf("center = %.2f %.2f %.2f\n", p.x(), p.y(), p.z());
319 // calc bounding radius
321 radius = p.distance3D(nodes[n]);
323 for ( j = i + 1; j < i + len; j++ ) {
325 dist = p.distance3D(nodes[n]);
326 if ( dist > radius ) { radius = dist; }
328 // printf("radius = %.2f\n", radius);
330 // dump bounding sphere and header
331 fprintf(out, "bs %.2f %.2f %.2f %.2f\n", p.x(), p.y(), p.z(), radius);
332 fprintf(out, "t %d %d %d\n", list[i], list[i+1], list[i+2]);
333 // printf("t %d %d %d\n", list[i], list[i+1], list[i+2]);
336 // dump rest of strip (until -1)
337 while ( (i < list_ptr) && (list[i] != -1) ) {
338 fprintf(out, "q %d", list[i]);
340 if ( (i < list_ptr) && (list[i] != -1) ) {
341 fprintf(out, " %d", list[i]);
352 // Check the direction the current triangle faces, compared to it's
353 // pregenerated normal. Returns the dot product between the target
354 // normal and actual normal. If the dot product is close to 1.0, they
355 // nearly match. If the are close to -1.0, the are nearly opposite.
356 double check_cur_face(int n1, int n2, int n3) {
357 double v1[3], v2[3], approx_normal[3], dot_prod, temp;
359 // check for the proper rotation by calculating an approximate
360 // normal and seeing if it is close to the precalculated normal
361 v1[0] = nodes[n2].x() - nodes[n1].x();
362 v1[1] = nodes[n2].y() - nodes[n1].y();
363 v1[2] = nodes[n2].z() - nodes[n1].z();
364 v2[0] = nodes[n3].x() - nodes[n1].x();
365 v2[1] = nodes[n3].y() - nodes[n1].y();
366 v2[2] = nodes[n3].z() - nodes[n1].z();
368 MAT3cross_product(approx_normal, v1, v2);
369 MAT3_NORMALIZE_VEC(approx_normal,temp);
370 dot_prod = MAT3_DOT_PRODUCT(normals[n1], approx_normal);
372 // not first triangle
373 // if ( ((dot_prod < -0.5) && !is_backwards) ||
374 // ((dot_prod > 0.5) && is_backwards) ) {
375 // printf(" Approx normal = %.2f %.2f %.2f\n", approx_normal[0],
376 // approx_normal[1], approx_normal[2]);
377 // printf(" Dot product = %.4f\n", dot_prod);
379 // angle = acos(dot_prod);
380 // printf("Normal ANGLE = %.3f rads.\n", angle);
387 void obj_fix(char *infile, char *outfile) {
388 Point3D node, normal;
391 int first, n1, n2, n3, n4;
392 double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
395 if ( (in = fopen(infile, "r")) == NULL ) {
396 printf("Cannot open file: %s\n", infile);
400 if ( (out = fopen(outfile, "w")) == NULL ) {
401 printf("Cannot open file: %s\n", outfile);
405 // push dummy records onto the lists since we start counting with "1"
406 node = Point3D(0.0, 0.0, 0.0);
407 nodes.push_back(node);
409 normal = Point3D(0.0, 0.0, 0.0);
410 normals.push_back(normal);
412 // initialize other lists
413 list_init(&ccw_list_ptr);
414 list_init(&cw_list_ptr);
416 // I start counting at one because that is how the triangle
417 // program refers to nodes and normals
422 printf("Reading file: %s\n", infile);
424 while ( fgets(line, 250, in) != NULL ) {
425 if ( line[0] == '#' ) {
426 // pass along the comments verbatim
427 fprintf(out, "%s", line);
428 } else if ( strlen(line) <= 1 ) {
429 // don't pass along empty lines
430 // fprintf(out, "%s", line);
431 } else if ( strncmp(line, "v ", 2) == 0 ) {
432 // save vertex to memory and output to file
433 // printf("vertex = %s", line);
434 sscanf(line, "v %lf %lf %lf\n", &x, &y, &z);
436 if ( nodes.size() == 1 ) {
437 // first time through set min's and max'es
445 // update min/max vertex values
446 if ( x < xmin ) xmin = x;
447 if ( x > xmax ) xmax = x;
448 if ( y < ymin ) ymin = y;
449 if ( y > ymax ) ymax = y;
450 if ( z < zmin ) zmin = z;
451 if ( z > zmax ) zmax = z;
454 node = Point3D(x, y, z);
455 nodes.push_back(node);
456 // fprintf(out, "v %.2f %.2f %.2f\n",
457 // node.x(), node.y(), node.z());
458 } else if ( strncmp(line, "vn ", 3) == 0 ) {
459 // save vertex normals to memory and output to file
460 // printf("vertex normal = %s", line);
461 sscanf(line, "vn %lf %lf %lf\n", &x, &y, &z);
462 normal = Point3D(x, y, z);
463 normals.push_back(normal);
464 } else if ( line[0] == 't' ) {
465 // starting a new triangle strip
467 printf("Starting a new triangle strip\n");
469 n1 = n2 = n3 = n4 = 0;
471 printf("new tri strip = %s", line);
472 sscanf(line, "t %d %d %d %d\n", &n1, &n2, &n3, &n4);
474 // special cases to handle bugs in our beloved tri striper
475 if ( (n1 == 4) && (n2 == 2) && (n3 == 2) && (n4 == 1) ) {
478 if ( (n1 == 3) && (n2 == 1) && (n3 == 1) && (n4 == 0) ) {
482 dot_prod = check_cur_face(n1, n2, n3);
483 if ( dot_prod < 0.0 ) {
484 // this stripe is backwards (CW)
486 printf(" -> Starting a backwards stripe\n");
488 // this stripe is normal (CCW)
493 if ( ccw_list_ptr ) {
494 list_add(ccw_list, &ccw_list_ptr, -1);
497 list_add(ccw_list, &ccw_list_ptr, n1);
498 list_add(ccw_list, &ccw_list_ptr, n2);
499 list_add(ccw_list, &ccw_list_ptr, n3);
502 list_add(cw_list, &cw_list_ptr, -1);
505 list_add(cw_list, &cw_list_ptr, n1);
506 list_add(cw_list, &cw_list_ptr, n2);
507 list_add(cw_list, &cw_list_ptr, n3);
512 list_add(ccw_list, &ccw_list_ptr, n4);
514 list_add(cw_list, &cw_list_ptr, n4);
517 } else if ( line[0] == 'f' ) {
518 if ( fcount < MAXNODES ) {
519 // pass along the unoptimized faces verbatim
520 sscanf(line, "f %d %d %d\n", &n1, &n2, &n3);
521 faces[fcount][0] = n1;
522 faces[fcount][1] = n2;
523 faces[fcount][2] = n3;
527 printf("Read too many unoptimized faces ... dying :-(\n");
531 // fprintf(out, "%s", line);
532 } else if ( line[0] == 'q' ) {
533 // continue a triangle strip
536 // printf("continued tri strip = %s ", line);
537 sscanf(line, "q %d %d\n", &n1, &n2);
540 list_add(ccw_list, &ccw_list_ptr, n1);
542 list_add(cw_list, &cw_list_ptr, n1);
547 list_add(ccw_list, &ccw_list_ptr, n2);
549 list_add(cw_list, &cw_list_ptr, n2);
553 printf("Unknown line in %s = %s\n", infile, line);
557 // reference point is the "center"
558 ref = Point3D( (xmin + xmax) / 2.0,
560 (zmin + zmax) / 2.0 );
562 // convert the cw_list to ccw add append to ccw_list
563 fix_cw_list(cw_list, cw_list_ptr);
565 dump_global_bounds();
572 dump_list(ccw_list, ccw_list_ptr);
580 // Revision 1.2 1998/10/21 14:55:55 curt
581 // Converted to Point3D class.
583 // Revision 1.1 1998/06/08 17:11:46 curt
584 // Renamed *.[ch] to *.[ch]xx
586 // Revision 1.16 1998/05/27 02:27:22 curt
587 // Commented out a couple of debugging messages.
589 // Revision 1.15 1998/05/24 02:47:47 curt
590 // For each strip, specify a default material property and calculate a center
591 // and bounding sphere.
593 // Revision 1.14 1998/05/23 15:19:49 curt
594 // Output more digits after the decimal place.
596 // Revision 1.13 1998/05/20 20:55:19 curt
597 // Fixed arbitrary polygon winding problem here so all tristrips are passed
598 // to runtime simulator with a consistant counter clockwise winding.
600 // Revision 1.12 1998/05/16 13:11:26 curt
601 // Fixed an off by one error in node, normal, and face counters.
603 // Revision 1.11 1998/04/27 15:59:24 curt
604 // Fixed an off by one error.
606 // Revision 1.10 1998/04/27 03:33:11 curt
607 // Code now calculates a center reference points and outputs everything
608 // relative to that. This is useful in the rendering engine to keep everything
609 // close to (0, 0, 0) where we can avoid many GLfloat precision problems.
611 // Revision 1.9 1998/04/18 04:01:03 curt
612 // Now use libMath rather than having local copies of math routines.
614 // Revision 1.8 1998/04/08 23:19:37 curt
615 // Adopted Gnu automake/autoconf system.
617 // Revision 1.7 1998/03/19 02:51:41 curt
618 // Added special case handling to compensate for bugs in our beloved tri striper
620 // Revision 1.6 1998/03/03 15:36:12 curt
621 // Tweaks for compiling with g++
623 // Revision 1.5 1998/03/03 03:37:03 curt
624 // Cumulative tweaks.
626 // Revision 1.4 1998/01/31 00:41:25 curt
627 // Made a few changes converting floats to doubles.
629 // Revision 1.3 1998/01/19 19:51:07 curt
630 // A couple final pre-release tweaks.
632 // Revision 1.2 1998/01/09 23:03:12 curt
633 // Restructured to split 1deg x 1deg dem's into 64 subsections.
635 // Revision 1.1 1997/12/08 19:28:54 curt