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 ) {
147 double dist_squared, radius, radius_squared;
154 iterator3 current = nodes.begin();
155 iterator3 last = nodes.end();
157 // skip first dummy node
160 for ( ; current != last; ++current ) {
161 dist_squared = ref.distance3Dsquared(*current);
162 // cout << "node = " << *current << " dist = " << dist_squared << endl;
164 if ( dist_squared > radius_squared ) {
165 radius_squared = dist_squared;
169 radius = sqrt(radius_squared);
172 "gbs %.5f %.5f %.5f %.2f\n",
173 ref.x(), ref.y(), ref.z(), radius);
178 void dump_nodes( void ) {
183 iterator3 current = nodes.begin();
184 iterator3 last = nodes.end();
186 // skip first dummy node
189 for ( ; current != last; ++current ) {
191 fprintf( out, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
197 void dump_normals( void ) {
202 iterator3 current = normals.begin();
203 iterator3 last = normals.end();
205 // skip first dummy normal
208 for ( ; current != last; ++current ) {
210 fprintf(out, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
216 void dump_faces( void ) {
219 double xmax, xmin, ymax, ymin, zmax, zmin, dist, radius;
222 for ( i = 1; i < fcount; i++ ) {
227 // calc center of face
228 xmin = xmax = nodes[n1].x();
229 ymin = ymax = nodes[n1].y();
230 zmin = zmax = nodes[n1].z();
232 if ( nodes[n2].x() < xmin ) { xmin = nodes[n2].x(); }
233 if ( nodes[n2].x() > xmax ) { xmax = nodes[n2].x(); }
234 if ( nodes[n2].y() < ymin ) { ymin = nodes[n2].y(); }
235 if ( nodes[n2].y() > ymax ) { ymax = nodes[n2].y(); }
236 if ( nodes[n2].z() < zmin ) { zmin = nodes[n2].z(); }
237 if ( nodes[n2].z() > zmax ) { zmax = nodes[n2].z(); }
239 if ( nodes[n3].x() < xmin ) { xmin = nodes[n3].x(); }
240 if ( nodes[n3].x() > xmax ) { xmax = nodes[n3].x(); }
241 if ( nodes[n3].y() < ymin ) { ymin = nodes[n3].y(); }
242 if ( nodes[n3].y() > ymax ) { ymax = nodes[n3].y(); }
243 if ( nodes[n3].z() < zmin ) { zmin = nodes[n3].z(); }
244 if ( nodes[n3].z() > zmax ) { zmax = nodes[n3].z(); }
246 p = Point3D( (xmin + xmax) / 2.0,
248 (zmin + zmax) / 2.0 );
250 // calc bounding radius
251 radius = p.distance3D(nodes[n1]);
253 dist = p.distance3D(nodes[n2]);
254 if ( dist > radius ) { radius = dist; }
256 dist = p.distance3D(nodes[n3]);
257 if ( dist > radius ) { radius = dist; }
260 fprintf(out, "bs %.2f %.2f %.2f %.2f\n", p.x(), p.y(), p.z(), radius);
261 fprintf(out, "f %d %d %d\n", n1, n2, n3);
267 void dump_list(int *list, int list_ptr) {
269 double xmax, xmin, ymax, ymin, zmax, zmin, dist_squared, radius_squared;
273 if ( list_ptr < 3 ) {
274 printf("List is empty ... skipping\n");
278 printf("Dumping list, size = %d\n", list_ptr);
281 while ( i < list_ptr ) {
284 if ( (i % 2) == 0 ) {
285 fprintf(out, "\nusemtl desert1\n");
287 fprintf(out, "\nusemtl desert2\n");
290 // find length of next tri strip
292 // scan rest of strip (until -1)
293 while ( ((i+len) < list_ptr) && (list[i+len] != -1) ) {
294 // printf("len = %d item = %d\n", len, list[i+len] );
297 // printf("strip length = %d\n", len);
299 // calc center of face
301 xmin = xmax = nodes[n].x();
302 ymin = ymax = nodes[n].y();
303 zmin = zmax = nodes[n].z();
304 // printf("%.2f %.2f %.2f\n", nodes[n].x(), nodes[n].y(), nodes[n].z());
306 for ( j = i + 1; j < i + len; j++ ) {
307 // printf("j = %d\n", j);
309 if ( nodes[n].x() < xmin ) { xmin = nodes[n].x(); }
310 if ( nodes[n].x() > xmax ) { xmax = nodes[n].x(); }
311 if ( nodes[n].y() < ymin ) { ymin = nodes[n].y(); }
312 if ( nodes[n].y() > ymax ) { ymax = nodes[n].y(); }
313 if ( nodes[n].z() < zmin ) { zmin = nodes[n].z(); }
314 if ( nodes[n].z() > zmax ) { zmax = nodes[n].z(); }
315 // printf("%.2f %.2f %.2f\n", nodes[n].x(), nodes[n].y(), nodes[n].z());
317 p = Point3D( (xmin + xmax) / 2.0,
319 (zmin + zmax) / 2.0 );
320 // printf("center = %.2f %.2f %.2f\n", p.x(), p.y(), p.z());
322 // calc bounding radius
324 radius_squared = p.distance3Dsquared(nodes[n]);
326 for ( j = i + 1; j < i + len; j++ ) {
328 dist_squared = p.distance3Dsquared(nodes[n]);
329 if ( dist_squared > radius_squared ) {
330 radius_squared = dist_squared;
333 radius = sqrt(radius_squared);
335 // printf("radius = %.2f\n", radius);
337 // dump bounding sphere and header
338 fprintf(out, "bs %.2f %.2f %.2f %.2f\n", p.x(), p.y(), p.z(), radius);
339 fprintf(out, "t %d %d %d\n", list[i], list[i+1], list[i+2]);
340 // printf("t %d %d %d\n", list[i], list[i+1], list[i+2]);
343 // dump rest of strip (until -1)
344 while ( (i < list_ptr) && (list[i] != -1) ) {
345 fprintf(out, "q %d", list[i]);
347 if ( (i < list_ptr) && (list[i] != -1) ) {
348 fprintf(out, " %d", list[i]);
359 // Check the direction the current triangle faces, compared to it's
360 // pregenerated normal. Returns the dot product between the target
361 // normal and actual normal. If the dot product is close to 1.0, they
362 // nearly match. If the are close to -1.0, the are nearly opposite.
363 double check_cur_face(int n1, int n2, int n3) {
364 double v1[3], v2[3], approx_normal[3], dot_prod, temp;
366 // check for the proper rotation by calculating an approximate
367 // normal and seeing if it is close to the precalculated normal
368 v1[0] = nodes[n2].x() - nodes[n1].x();
369 v1[1] = nodes[n2].y() - nodes[n1].y();
370 v1[2] = nodes[n2].z() - nodes[n1].z();
371 v2[0] = nodes[n3].x() - nodes[n1].x();
372 v2[1] = nodes[n3].y() - nodes[n1].y();
373 v2[2] = nodes[n3].z() - nodes[n1].z();
375 MAT3cross_product(approx_normal, v1, v2);
376 MAT3_NORMALIZE_VEC(approx_normal,temp);
377 dot_prod = MAT3_DOT_PRODUCT(normals[n1], approx_normal);
379 // not first triangle
380 // if ( ((dot_prod < -0.5) && !is_backwards) ||
381 // ((dot_prod > 0.5) && is_backwards) ) {
382 // printf(" Approx normal = %.2f %.2f %.2f\n", approx_normal[0],
383 // approx_normal[1], approx_normal[2]);
384 // printf(" Dot product = %.4f\n", dot_prod);
386 // angle = acos(dot_prod);
387 // printf("Normal ANGLE = %.3f rads.\n", angle);
394 void obj_fix(char *infile, char *outfile) {
395 Point3D node, normal;
398 int first, n1, n2, n3, n4;
399 double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
402 if ( (in = fopen(infile, "r")) == NULL ) {
403 printf("Cannot open file: %s\n", infile);
407 if ( (out = fopen(outfile, "w")) == NULL ) {
408 printf("Cannot open file: %s\n", outfile);
412 // push dummy records onto the lists since we start counting with "1"
413 node = Point3D(0.0, 0.0, 0.0);
414 nodes.push_back(node);
416 normal = Point3D(0.0, 0.0, 0.0);
417 normals.push_back(normal);
419 // initialize other lists
420 list_init(&ccw_list_ptr);
421 list_init(&cw_list_ptr);
423 // I start counting at one because that is how the triangle
424 // program refers to nodes and normals
429 printf("Reading file: %s\n", infile);
431 while ( fgets(line, 250, in) != NULL ) {
432 if ( line[0] == '#' ) {
433 // pass along the comments verbatim
434 fprintf(out, "%s", line);
435 } else if ( strlen(line) <= 1 ) {
436 // don't pass along empty lines
437 // fprintf(out, "%s", line);
438 } else if ( strncmp(line, "v ", 2) == 0 ) {
439 // save vertex to memory and output to file
440 // printf("vertex = %s", line);
441 sscanf(line, "v %lf %lf %lf\n", &x, &y, &z);
443 if ( nodes.size() == 1 ) {
444 // first time through set min's and max'es
452 // update min/max vertex values
453 if ( x < xmin ) xmin = x;
454 if ( x > xmax ) xmax = x;
455 if ( y < ymin ) ymin = y;
456 if ( y > ymax ) ymax = y;
457 if ( z < zmin ) zmin = z;
458 if ( z > zmax ) zmax = z;
461 node = Point3D(x, y, z);
462 nodes.push_back(node);
463 // fprintf(out, "v %.2f %.2f %.2f\n",
464 // node.x(), node.y(), node.z());
465 } else if ( strncmp(line, "vn ", 3) == 0 ) {
466 // save vertex normals to memory and output to file
467 // printf("vertex normal = %s", line);
468 sscanf(line, "vn %lf %lf %lf\n", &x, &y, &z);
469 normal = Point3D(x, y, z);
470 normals.push_back(normal);
471 } else if ( line[0] == 't' ) {
472 // starting a new triangle strip
474 printf("Starting a new triangle strip\n");
476 n1 = n2 = n3 = n4 = 0;
478 printf("new tri strip = %s", line);
479 sscanf(line, "t %d %d %d %d\n", &n1, &n2, &n3, &n4);
481 // special cases to handle bugs in our beloved tri striper
482 if ( (n1 == 4) && (n2 == 2) && (n3 == 2) && (n4 == 1) ) {
485 if ( (n1 == 3) && (n2 == 1) && (n3 == 1) && (n4 == 0) ) {
489 dot_prod = check_cur_face(n1, n2, n3);
490 if ( dot_prod < 0.0 ) {
491 // this stripe is backwards (CW)
493 printf(" -> Starting a backwards stripe\n");
495 // this stripe is normal (CCW)
500 if ( ccw_list_ptr ) {
501 list_add(ccw_list, &ccw_list_ptr, -1);
504 list_add(ccw_list, &ccw_list_ptr, n1);
505 list_add(ccw_list, &ccw_list_ptr, n2);
506 list_add(ccw_list, &ccw_list_ptr, n3);
509 list_add(cw_list, &cw_list_ptr, -1);
512 list_add(cw_list, &cw_list_ptr, n1);
513 list_add(cw_list, &cw_list_ptr, n2);
514 list_add(cw_list, &cw_list_ptr, n3);
519 list_add(ccw_list, &ccw_list_ptr, n4);
521 list_add(cw_list, &cw_list_ptr, n4);
524 } else if ( line[0] == 'f' ) {
525 if ( fcount < MAXNODES ) {
526 // pass along the unoptimized faces verbatim
527 sscanf(line, "f %d %d %d\n", &n1, &n2, &n3);
528 faces[fcount][0] = n1;
529 faces[fcount][1] = n2;
530 faces[fcount][2] = n3;
534 printf("Read too many unoptimized faces ... dying :-(\n");
538 // fprintf(out, "%s", line);
539 } else if ( line[0] == 'q' ) {
540 // continue a triangle strip
543 // printf("continued tri strip = %s ", line);
544 sscanf(line, "q %d %d\n", &n1, &n2);
547 list_add(ccw_list, &ccw_list_ptr, n1);
549 list_add(cw_list, &cw_list_ptr, n1);
554 list_add(ccw_list, &ccw_list_ptr, n2);
556 list_add(cw_list, &cw_list_ptr, n2);
560 printf("Unknown line in %s = %s\n", infile, line);
564 // reference point is the "center"
565 ref = Point3D( (xmin + xmax) / 2.0,
567 (zmin + zmax) / 2.0 );
569 // convert the cw_list to ccw add append to ccw_list
570 fix_cw_list(cw_list, cw_list_ptr);
572 dump_global_bounds();
579 dump_list(ccw_list, ccw_list_ptr);
587 // Revision 1.3 1999/02/01 21:09:40 curt
588 // Optimizations from Norman Vine.
590 // Revision 1.2 1998/10/21 14:55:55 curt
591 // Converted to Point3D class.
593 // Revision 1.1 1998/06/08 17:11:46 curt
594 // Renamed *.[ch] to *.[ch]xx
596 // Revision 1.16 1998/05/27 02:27:22 curt
597 // Commented out a couple of debugging messages.
599 // Revision 1.15 1998/05/24 02:47:47 curt
600 // For each strip, specify a default material property and calculate a center
601 // and bounding sphere.
603 // Revision 1.14 1998/05/23 15:19:49 curt
604 // Output more digits after the decimal place.
606 // Revision 1.13 1998/05/20 20:55:19 curt
607 // Fixed arbitrary polygon winding problem here so all tristrips are passed
608 // to runtime simulator with a consistant counter clockwise winding.
610 // Revision 1.12 1998/05/16 13:11:26 curt
611 // Fixed an off by one error in node, normal, and face counters.
613 // Revision 1.11 1998/04/27 15:59:24 curt
614 // Fixed an off by one error.
616 // Revision 1.10 1998/04/27 03:33:11 curt
617 // Code now calculates a center reference points and outputs everything
618 // relative to that. This is useful in the rendering engine to keep everything
619 // close to (0, 0, 0) where we can avoid many GLfloat precision problems.
621 // Revision 1.9 1998/04/18 04:01:03 curt
622 // Now use libMath rather than having local copies of math routines.
624 // Revision 1.8 1998/04/08 23:19:37 curt
625 // Adopted Gnu automake/autoconf system.
627 // Revision 1.7 1998/03/19 02:51:41 curt
628 // Added special case handling to compensate for bugs in our beloved tri striper
630 // Revision 1.6 1998/03/03 15:36:12 curt
631 // Tweaks for compiling with g++
633 // Revision 1.5 1998/03/03 03:37:03 curt
634 // Cumulative tweaks.
636 // Revision 1.4 1998/01/31 00:41:25 curt
637 // Made a few changes converting floats to doubles.
639 // Revision 1.3 1998/01/19 19:51:07 curt
640 // A couple final pre-release tweaks.
642 // Revision 1.2 1998/01/09 23:03:12 curt
643 // Restructured to split 1deg x 1deg dem's into 64 subsections.
645 // Revision 1.1 1997/12/08 19:28:54 curt