]> git.mxchange.org Git - flightgear.git/blob - FixObj/obj.cxx
Lots of directory layout reorganization.
[flightgear.git] / FixObj / obj.cxx
1 // obj.cxx -- routines to handle WaveFront .obj format files.
2 //
3 // Written by Curtis Olson, started October 1997.
4 //
5 // Copyright (C) 1997 - 1998  Curtis L. Olson - curt@me.umn.edu
6 //
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.
11 //
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.
16 //
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.
20 //
21 // $Id$
22 // (Log is kept at end of this file)
23
24
25 #include <stdio.h>
26 #include <iostream>
27 #include <string.h>
28
29 #include <vector>
30 #include "Include/compiler.h"
31
32 #ifdef NEEDNAMESPACESTD
33 using namespace std;
34 #endif
35
36 #include "obj.hxx"
37
38 #include <Math/mat3.h>
39 #include <Math/point3d.hxx>
40
41
42 typedef vector < Point3D > container3;
43 typedef container3::iterator iterator3;
44 typedef container3::const_iterator const_iterator3;
45
46
47 // what do ya' know, here's some global variables
48 container3 nodes;
49 container3 normals;
50 static int faces[MAXNODES][3];
51 int vncount, fcount;
52
53 static int ccw_list[MAXNODES];
54 int ccw_list_ptr;
55
56 static int cw_list[MAXNODES];
57 int cw_list_ptr;
58
59 FILE *in, *out;
60
61 Point3D ref;
62
63
64 // some simple list routines
65
66 // reset the list
67 void list_init(int *list_ptr) {
68     *list_ptr = 0;
69 }
70
71
72 // add to list
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");
76         exit(-1);
77     }
78
79     list[*list_ptr] = node;
80     *list_ptr += 1;
81
82     // printf("list pointer = %d  adding %d\n", *list_ptr, node);
83 }
84
85
86 // fix the cw list and append to ccw_list
87 void fix_cw_list(int *list, int list_ptr) {
88     int i, j, len;
89
90     if ( list_ptr < 3 ) {
91         printf("List is empty ... skipping\n");
92         return;
93     }
94
95     printf("Fixing cw list, size = %d\n", list_ptr);
96
97     i = 0;
98     while ( i < list_ptr ) { 
99         // do next strip
100
101         // find length
102         len = 0;
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] );
106             len++;
107         }
108         // printf("          Final length = %d\n", len);
109
110         if ( (len % 2) != 0 ) {
111             // if length is odd, just reverse order of nodes to
112             // reverse winding
113             if ( ccw_list_ptr ) {
114                 list_add(ccw_list, &ccw_list_ptr, -1);
115             }
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]);
119             }
120         } else {
121             // if length is even, reverse order of (n-1) nodes to
122             // reverse winding, and create an orphan triangle for the
123             // last "nth" node
124             if ( ccw_list_ptr ) {
125                 list_add(ccw_list, &ccw_list_ptr, -1);
126             }
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]);
130             }
131
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]);
139         }
140
141         i += len + 1;
142     }
143 }
144
145
146 void dump_global_bounds( void ) {
147     double dist_squared, radius, radius_squared;
148
149     radius = 0.0;
150
151     fprintf(out, "\n");
152
153     
154     iterator3 current = nodes.begin();
155     iterator3 last = nodes.end();
156
157     // skip first dummy node
158     ++current;
159
160     for ( ; current != last; ++current ) {
161         dist_squared = ref.distance3Dsquared(*current);
162         // cout << "node = " << *current << " dist = " << dist_squared << endl;
163
164         if ( dist_squared > radius_squared ) {
165             radius_squared = dist_squared;
166         }
167     }
168
169     radius = sqrt(radius_squared);
170
171     fprintf( out, 
172              "gbs %.5f %.5f %.5f %.2f\n", 
173              ref.x(), ref.y(), ref.z(), radius);
174 }
175
176
177 // dump nodes
178 void dump_nodes( void ) {
179     Point3D p;
180
181     fprintf(out, "\n");
182
183     iterator3 current = nodes.begin();
184     iterator3 last = nodes.end();
185
186     // skip first dummy node
187     ++current;
188
189     for ( ; current != last; ++current ) {
190         p = *current - ref;
191         fprintf( out, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
192     }
193 }
194
195
196 // dump normals
197 void dump_normals( void ) {
198     Point3D p;
199
200     fprintf(out, "\n");
201
202     iterator3 current = normals.begin();
203     iterator3 last = normals.end();
204
205     // skip first dummy normal
206     ++current;
207
208     for ( ; current != last; ++current ) {
209         p = *current;
210         fprintf(out, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
211     }
212 }
213
214
215 // dump faces
216 void dump_faces( void ) {
217     Point3D p;
218     int i, n1, n2, n3;
219     double xmax, xmin, ymax, ymin, zmax, zmin, dist, radius;
220
221     fprintf(out, "\n");
222     for ( i = 1; i < fcount; i++ ) {
223         n1 = faces[i][0];
224         n2 = faces[i][1];
225         n3 = faces[i][2];
226
227         // calc center of face
228         xmin = xmax = nodes[n1].x();
229         ymin = ymax = nodes[n1].y();
230         zmin = zmax = nodes[n1].z();
231
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(); }
238
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(); }
245
246         p = Point3D( (xmin + xmax) / 2.0,
247                      (ymin + ymax) / 2.0,
248                      (zmin + zmax) / 2.0 );
249
250         // calc bounding radius
251         radius = p.distance3D(nodes[n1]);
252
253         dist = p.distance3D(nodes[n2]);
254         if ( dist > radius ) { radius = dist; }
255
256         dist = p.distance3D(nodes[n3]);
257         if ( dist > radius ) { radius = dist; }
258         
259         // output data
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);
262     }
263 }
264
265
266 // dump list
267 void dump_list(int *list, int list_ptr) {
268     Point3D p;
269     double xmax, xmin, ymax, ymin, zmax, zmin, dist_squared, radius_squared;
270     double radius;
271     int i, j, len, n;
272
273     if ( list_ptr < 3 ) {
274         printf("List is empty ... skipping\n");
275         return;
276     }
277
278     printf("Dumping list, size = %d\n", list_ptr);
279
280     i = 0;
281     while ( i < list_ptr ) { 
282         // do next strip
283
284         if ( (i % 2) == 0 ) {
285             fprintf(out, "\nusemtl desert1\n");
286         } else {
287             fprintf(out, "\nusemtl desert2\n");
288         }
289
290         // find length of next tri strip
291         len = 0;
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] );
295             len++;
296         }
297         // printf("strip length = %d\n", len);
298
299         // calc center of face
300         n = list[i];
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());
305
306         for ( j = i + 1; j < i + len; j++ ) {
307             // printf("j = %d\n", j);
308             n = list[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());
316         }
317         p = Point3D( (xmin + xmax) / 2.0,
318                      (ymin + ymax) / 2.0,
319                      (zmin + zmax) / 2.0 );
320         // printf("center = %.2f %.2f %.2f\n", p.x(), p.y(), p.z());
321
322         // calc bounding radius
323         n = list[i];
324         radius_squared = p.distance3Dsquared(nodes[n]);
325
326         for ( j = i + 1; j < i + len; j++ ) {
327             n = list[j];
328             dist_squared = p.distance3Dsquared(nodes[n]);
329             if ( dist_squared > radius_squared ) {
330                 radius_squared = dist_squared;
331             }
332         }
333         radius = sqrt(radius_squared);
334
335         // printf("radius = %.2f\n", radius);
336
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]);
341         i += 3;
342
343         // dump rest of strip (until -1)
344         while ( (i < list_ptr) && (list[i] != -1) ) { 
345             fprintf(out, "q %d", list[i]);
346             i++;
347             if ( (i < list_ptr) && (list[i] != -1) ) { 
348                 fprintf(out, " %d", list[i]);
349                 i++;
350             }
351             fprintf(out, "\n");
352         }
353
354         i++;
355     }
356 }
357
358
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;
365
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();
374
375     MAT3cross_product(approx_normal, v1, v2);
376     MAT3_NORMALIZE_VEC(approx_normal,temp);
377     dot_prod = MAT3_DOT_PRODUCT(normals[n1], approx_normal);
378
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);
385     // }
386     // angle = acos(dot_prod);
387     // printf("Normal ANGLE = %.3f rads.\n", angle);
388
389     return(dot_prod);
390 }
391
392
393 // Load a .obj file
394 void obj_fix(char *infile, char *outfile) {
395     Point3D node, normal;
396     char line[256];
397     double dot_prod;
398     int first, n1, n2, n3, n4;
399     double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
400     int is_ccw;
401
402     if ( (in = fopen(infile, "r")) == NULL ) {
403         printf("Cannot open file: %s\n", infile);
404         exit(-1);
405     }
406
407     if ( (out = fopen(outfile, "w")) == NULL ) {
408         printf("Cannot open file: %s\n", outfile);
409         exit(-1);
410     }
411
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);
415
416     normal = Point3D(0.0, 0.0, 0.0);
417     normals.push_back(normal);
418
419     // initialize other lists
420     list_init(&ccw_list_ptr);
421     list_init(&cw_list_ptr);
422
423     // I start counting at one because that is how the triangle
424     // program refers to nodes and normals
425     first = 1;
426     vncount = 1;
427     fcount = 1;
428
429     printf("Reading file:  %s\n", infile);
430
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);
442
443             if ( nodes.size() == 1 ) {
444                 // first time through set min's and max'es
445                 xmin = x;
446                 xmax = x;
447                 ymin = y;
448                 ymax = y;
449                 zmin = z;
450                 zmax = z;
451             } else {
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;               
459             }
460
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
473
474             printf("Starting a new triangle strip\n");
475
476             n1 = n2 = n3 = n4 = 0;
477
478             printf("new tri strip = %s", line);
479             sscanf(line, "t %d %d %d %d\n", &n1, &n2, &n3, &n4);
480
481             // special cases to handle bugs in our beloved tri striper
482             if ( (n1 == 4) && (n2 == 2) && (n3 == 2) && (n4 == 1) ) {
483                 n2 = 3;
484             }
485             if ( (n1 == 3) && (n2 == 1) && (n3 == 1) && (n4 == 0) ) {
486                 n3 = 4;
487             }
488
489             dot_prod = check_cur_face(n1, n2, n3);
490             if ( dot_prod < 0.0 ) {
491                 // this stripe is backwards (CW)
492                 is_ccw = 0;
493                 printf(" -> Starting a backwards stripe\n");
494             } else {
495                 // this stripe is normal (CCW)
496                 is_ccw = 1;
497             }
498
499             if ( is_ccw ) {
500                 if ( ccw_list_ptr ) {
501                     list_add(ccw_list, &ccw_list_ptr, -1);
502                 }
503
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);
507             } else {
508                 if ( cw_list_ptr ) {
509                     list_add(cw_list, &cw_list_ptr, -1);
510                 }
511
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);
515             }
516
517             if ( n4 > 0 ) {
518                 if ( is_ccw ) {
519                     list_add(ccw_list, &ccw_list_ptr, n4);
520                 } else {
521                     list_add(cw_list, &cw_list_ptr, n4);
522                 }
523             }
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;
531
532                 fcount++;
533             } else {
534                 printf("Read too many unoptimized faces ... dying :-(\n");
535                 exit(-1);
536             }
537  
538             // fprintf(out, "%s", line);
539         } else if ( line[0] == 'q' ) {
540             // continue a triangle strip
541             n1 = n2 = 0;
542
543             // printf("continued tri strip = %s ", line);
544             sscanf(line, "q %d %d\n", &n1, &n2);
545
546             if ( is_ccw ) {
547                 list_add(ccw_list, &ccw_list_ptr, n1);
548             } else {
549                 list_add(cw_list, &cw_list_ptr, n1);
550             }
551
552             if ( n2 > 0 ) {
553                 if ( is_ccw ) {
554                     list_add(ccw_list, &ccw_list_ptr, n2);
555                 } else {
556                     list_add(cw_list, &cw_list_ptr, n2);
557                 }
558             }
559         } else {
560             printf("Unknown line in %s = %s\n", infile, line);
561         }
562     }
563
564     // reference point is the "center"
565     ref = Point3D( (xmin + xmax) / 2.0,
566                    (ymin + ymax) / 2.0,
567                    (zmin + zmax) / 2.0 );
568
569     // convert the cw_list to ccw add append to ccw_list
570     fix_cw_list(cw_list, cw_list_ptr);
571
572     dump_global_bounds();
573     dump_nodes();
574     dump_normals();
575     if ( fcount > 1 ) {
576         dump_faces();
577     }
578
579     dump_list(ccw_list, ccw_list_ptr);
580
581     fclose(in);
582     fclose(out);
583 }
584
585
586 // $Log$
587 // Revision 1.3  1999/02/01 21:09:40  curt
588 // Optimizations from Norman Vine.
589 //
590 // Revision 1.2  1998/10/21 14:55:55  curt
591 // Converted to Point3D class.
592 //
593 // Revision 1.1  1998/06/08 17:11:46  curt
594 // Renamed *.[ch] to *.[ch]xx
595 //
596 // Revision 1.16  1998/05/27 02:27:22  curt
597 // Commented out a couple of debugging messages.
598 //
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.
602 //
603 // Revision 1.14  1998/05/23 15:19:49  curt
604 // Output more digits after the decimal place.
605 //
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.
609 //
610 // Revision 1.12  1998/05/16 13:11:26  curt
611 // Fixed an off by one error in node, normal, and face counters.
612 //
613 // Revision 1.11  1998/04/27 15:59:24  curt
614 // Fixed an off by one error.
615 //
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.
620 //
621 // Revision 1.9  1998/04/18 04:01:03  curt
622 // Now use libMath rather than having local copies of math routines.
623 //
624 // Revision 1.8  1998/04/08 23:19:37  curt
625 // Adopted Gnu automake/autoconf system.
626 //
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
629 //
630 // Revision 1.6  1998/03/03 15:36:12  curt
631 // Tweaks for compiling with g++
632 //
633 // Revision 1.5  1998/03/03 03:37:03  curt
634 // Cumulative tweaks.
635 //
636 // Revision 1.4  1998/01/31 00:41:25  curt
637 // Made a few changes converting floats to doubles.
638 //
639 // Revision 1.3  1998/01/19 19:51:07  curt
640 // A couple final pre-release tweaks.
641 //
642 // Revision 1.2  1998/01/09 23:03:12  curt
643 // Restructured to split 1deg x 1deg dem's into 64 subsections.
644 //
645 // Revision 1.1  1997/12/08 19:28:54  curt
646 // Initial revision.
647 //