]> git.mxchange.org Git - flightgear.git/blob - FixObj/obj.cxx
Converted to Point3D class.
[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, radius;
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 = ref.distance3D(*current);
162         // cout << "node = " << *current << " dist = " << dist << endl;
163
164         if ( dist > radius ) {
165             radius = dist;
166         }
167     }
168
169     fprintf( out, 
170              "gbs %.5f %.5f %.5f %.2f\n", 
171              ref.x(), ref.y(), ref.z(), radius);
172 }
173
174
175 // dump nodes
176 void dump_nodes( void ) {
177     Point3D p;
178
179     fprintf(out, "\n");
180
181     iterator3 current = nodes.begin();
182     iterator3 last = nodes.end();
183
184     // skip first dummy node
185     ++current;
186
187     for ( ; current != last; ++current ) {
188         p = *current - ref;
189         fprintf( out, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
190     }
191 }
192
193
194 // dump normals
195 void dump_normals( void ) {
196     Point3D p;
197
198     fprintf(out, "\n");
199
200     iterator3 current = normals.begin();
201     iterator3 last = normals.end();
202
203     // skip first dummy normal
204     ++current;
205
206     for ( ; current != last; ++current ) {
207         p = *current;
208         fprintf(out, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z() );
209     }
210 }
211
212
213 // dump faces
214 void dump_faces( void ) {
215     Point3D p;
216     int i, n1, n2, n3;
217     double xmax, xmin, ymax, ymin, zmax, zmin, dist, radius;
218
219     fprintf(out, "\n");
220     for ( i = 1; i < fcount; i++ ) {
221         n1 = faces[i][0];
222         n2 = faces[i][1];
223         n3 = faces[i][2];
224
225         // calc center of face
226         xmin = xmax = nodes[n1].x();
227         ymin = ymax = nodes[n1].y();
228         zmin = zmax = nodes[n1].z();
229
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(); }
236
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(); }
243
244         p = Point3D( (xmin + xmax) / 2.0,
245                      (ymin + ymax) / 2.0,
246                      (zmin + zmax) / 2.0 );
247
248         // calc bounding radius
249         radius = p.distance3D(nodes[n1]);
250
251         dist = p.distance3D(nodes[n2]);
252         if ( dist > radius ) { radius = dist; }
253
254         dist = p.distance3D(nodes[n3]);
255         if ( dist > radius ) { radius = dist; }
256         
257         // output data
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);
260     }
261 }
262
263
264 // dump list
265 void dump_list(int *list, int list_ptr) {
266     Point3D p;
267     double xmax, xmin, ymax, ymin, zmax, zmin, dist, radius;
268     int i, j, len, n;
269
270     if ( list_ptr < 3 ) {
271         printf("List is empty ... skipping\n");
272         return;
273     }
274
275     printf("Dumping list, size = %d\n", list_ptr);
276
277     i = 0;
278     while ( i < list_ptr ) { 
279         // do next strip
280
281         if ( (i % 2) == 0 ) {
282             fprintf(out, "\nusemtl desert1\n");
283         } else {
284             fprintf(out, "\nusemtl desert2\n");
285         }
286
287         // find length of next tri strip
288         len = 0;
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] );
292             len++;
293         }
294         // printf("strip length = %d\n", len);
295
296         // calc center of face
297         n = list[i];
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());
302
303         for ( j = i + 1; j < i + len; j++ ) {
304             // printf("j = %d\n", j);
305             n = list[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());
313         }
314         p = Point3D( (xmin + xmax) / 2.0,
315                      (ymin + ymax) / 2.0,
316                      (zmin + zmax) / 2.0 );
317         // printf("center = %.2f %.2f %.2f\n", p.x(), p.y(), p.z());
318
319         // calc bounding radius
320         n = list[i];
321         radius = p.distance3D(nodes[n]);
322
323         for ( j = i + 1; j < i + len; j++ ) {
324             n = list[j];
325             dist = p.distance3D(nodes[n]);
326             if ( dist > radius ) { radius = dist; }
327         }
328         // printf("radius = %.2f\n", radius);
329
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]);
334         i += 3;
335
336         // dump rest of strip (until -1)
337         while ( (i < list_ptr) && (list[i] != -1) ) { 
338             fprintf(out, "q %d", list[i]);
339             i++;
340             if ( (i < list_ptr) && (list[i] != -1) ) { 
341                 fprintf(out, " %d", list[i]);
342                 i++;
343             }
344             fprintf(out, "\n");
345         }
346
347         i++;
348     }
349 }
350
351
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;
358
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();
367
368     MAT3cross_product(approx_normal, v1, v2);
369     MAT3_NORMALIZE_VEC(approx_normal,temp);
370     dot_prod = MAT3_DOT_PRODUCT(normals[n1], approx_normal);
371
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);
378     // }
379     // angle = acos(dot_prod);
380     // printf("Normal ANGLE = %.3f rads.\n", angle);
381
382     return(dot_prod);
383 }
384
385
386 // Load a .obj file
387 void obj_fix(char *infile, char *outfile) {
388     Point3D node, normal;
389     char line[256];
390     double dot_prod;
391     int first, n1, n2, n3, n4;
392     double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
393     int is_ccw;
394
395     if ( (in = fopen(infile, "r")) == NULL ) {
396         printf("Cannot open file: %s\n", infile);
397         exit(-1);
398     }
399
400     if ( (out = fopen(outfile, "w")) == NULL ) {
401         printf("Cannot open file: %s\n", outfile);
402         exit(-1);
403     }
404
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);
408
409     normal = Point3D(0.0, 0.0, 0.0);
410     normals.push_back(normal);
411
412     // initialize other lists
413     list_init(&ccw_list_ptr);
414     list_init(&cw_list_ptr);
415
416     // I start counting at one because that is how the triangle
417     // program refers to nodes and normals
418     first = 1;
419     vncount = 1;
420     fcount = 1;
421
422     printf("Reading file:  %s\n", infile);
423
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);
435
436             if ( nodes.size() == 1 ) {
437                 // first time through set min's and max'es
438                 xmin = x;
439                 xmax = x;
440                 ymin = y;
441                 ymax = y;
442                 zmin = z;
443                 zmax = z;
444             } else {
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;               
452             }
453
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
466
467             printf("Starting a new triangle strip\n");
468
469             n1 = n2 = n3 = n4 = 0;
470
471             printf("new tri strip = %s", line);
472             sscanf(line, "t %d %d %d %d\n", &n1, &n2, &n3, &n4);
473
474             // special cases to handle bugs in our beloved tri striper
475             if ( (n1 == 4) && (n2 == 2) && (n3 == 2) && (n4 == 1) ) {
476                 n2 = 3;
477             }
478             if ( (n1 == 3) && (n2 == 1) && (n3 == 1) && (n4 == 0) ) {
479                 n3 = 4;
480             }
481
482             dot_prod = check_cur_face(n1, n2, n3);
483             if ( dot_prod < 0.0 ) {
484                 // this stripe is backwards (CW)
485                 is_ccw = 0;
486                 printf(" -> Starting a backwards stripe\n");
487             } else {
488                 // this stripe is normal (CCW)
489                 is_ccw = 1;
490             }
491
492             if ( is_ccw ) {
493                 if ( ccw_list_ptr ) {
494                     list_add(ccw_list, &ccw_list_ptr, -1);
495                 }
496
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);
500             } else {
501                 if ( cw_list_ptr ) {
502                     list_add(cw_list, &cw_list_ptr, -1);
503                 }
504
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);
508             }
509
510             if ( n4 > 0 ) {
511                 if ( is_ccw ) {
512                     list_add(ccw_list, &ccw_list_ptr, n4);
513                 } else {
514                     list_add(cw_list, &cw_list_ptr, n4);
515                 }
516             }
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;
524
525                 fcount++;
526             } else {
527                 printf("Read too many unoptimized faces ... dying :-(\n");
528                 exit(-1);
529             }
530  
531             // fprintf(out, "%s", line);
532         } else if ( line[0] == 'q' ) {
533             // continue a triangle strip
534             n1 = n2 = 0;
535
536             // printf("continued tri strip = %s ", line);
537             sscanf(line, "q %d %d\n", &n1, &n2);
538
539             if ( is_ccw ) {
540                 list_add(ccw_list, &ccw_list_ptr, n1);
541             } else {
542                 list_add(cw_list, &cw_list_ptr, n1);
543             }
544
545             if ( n2 > 0 ) {
546                 if ( is_ccw ) {
547                     list_add(ccw_list, &ccw_list_ptr, n2);
548                 } else {
549                     list_add(cw_list, &cw_list_ptr, n2);
550                 }
551             }
552         } else {
553             printf("Unknown line in %s = %s\n", infile, line);
554         }
555     }
556
557     // reference point is the "center"
558     ref = Point3D( (xmin + xmax) / 2.0,
559                    (ymin + ymax) / 2.0,
560                    (zmin + zmax) / 2.0 );
561
562     // convert the cw_list to ccw add append to ccw_list
563     fix_cw_list(cw_list, cw_list_ptr);
564
565     dump_global_bounds();
566     dump_nodes();
567     dump_normals();
568     if ( fcount > 1 ) {
569         dump_faces();
570     }
571
572     dump_list(ccw_list, ccw_list_ptr);
573
574     fclose(in);
575     fclose(out);
576 }
577
578
579 // $Log$
580 // Revision 1.2  1998/10/21 14:55:55  curt
581 // Converted to Point3D class.
582 //
583 // Revision 1.1  1998/06/08 17:11:46  curt
584 // Renamed *.[ch] to *.[ch]xx
585 //
586 // Revision 1.16  1998/05/27 02:27:22  curt
587 // Commented out a couple of debugging messages.
588 //
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.
592 //
593 // Revision 1.14  1998/05/23 15:19:49  curt
594 // Output more digits after the decimal place.
595 //
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.
599 //
600 // Revision 1.12  1998/05/16 13:11:26  curt
601 // Fixed an off by one error in node, normal, and face counters.
602 //
603 // Revision 1.11  1998/04/27 15:59:24  curt
604 // Fixed an off by one error.
605 //
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.
610 //
611 // Revision 1.9  1998/04/18 04:01:03  curt
612 // Now use libMath rather than having local copies of math routines.
613 //
614 // Revision 1.8  1998/04/08 23:19:37  curt
615 // Adopted Gnu automake/autoconf system.
616 //
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
619 //
620 // Revision 1.6  1998/03/03 15:36:12  curt
621 // Tweaks for compiling with g++
622 //
623 // Revision 1.5  1998/03/03 03:37:03  curt
624 // Cumulative tweaks.
625 //
626 // Revision 1.4  1998/01/31 00:41:25  curt
627 // Made a few changes converting floats to doubles.
628 //
629 // Revision 1.3  1998/01/19 19:51:07  curt
630 // A couple final pre-release tweaks.
631 //
632 // Revision 1.2  1998/01/09 23:03:12  curt
633 // Restructured to split 1deg x 1deg dem's into 64 subsections.
634 //
635 // Revision 1.1  1997/12/08 19:28:54  curt
636 // Initial revision.
637 //
638