]> git.mxchange.org Git - flightgear.git/blob - src/Objects/obj.cxx
Initial revision
[flightgear.git] / src / Objects / obj.cxx
1 // obj.cxx -- routines to handle "sorta" WaveFront .obj format files.
2 //
3 // Written by Curtis Olson, started October 1997.
4 //
5 // Copyright (C) 1997  Curtis L. Olson  - curt@infoplane.com
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
23
24 #ifdef HAVE_CONFIG_H
25 #  include <config.h>
26 #endif
27
28 #ifdef FG_MATH_EXCEPTION_CLASH
29 #  include <math.h>
30 #endif
31
32 #ifdef HAVE_WINDOWS_H
33 #  include <windows.h>
34 #endif
35
36 #include <stdio.h>
37 #include <string.h>
38 #include <GL/glut.h>
39 #include <XGL/xgl.h>
40
41 // #if defined ( __sun__ )
42 // extern "C" void *memmove(void *, const void *, size_t);
43 // extern "C" void *memset(void *, int, size_t);
44 // #endif
45
46 #include <Include/compiler.h>
47
48 #include STL_STRING
49 #include <map>          // STL
50 #include <ctype.h>      // isdigit()
51
52 #include <Debug/logstream.hxx>
53 #include <Misc/fgstream.hxx>
54 #include <Include/fg_constants.h>
55 #include <Main/options.hxx>
56 #include <Math/mat3.h>
57 #include <Math/fg_random.h>
58 #include <Math/point3d.hxx>
59 #include <Math/polar3d.hxx>
60 #include <Misc/stopwatch.hxx>
61 #include <Scenery/tileentry.hxx>
62
63 #include "materialmgr.hxx"
64 #include "obj.hxx"
65
66 FG_USING_STD(string);
67
68
69 static double normals[FG_MAX_NODES][3];
70 static double tex_coords[FG_MAX_NODES*3][3];
71
72
73 // given three points defining a triangle, calculate the normal
74 static void calc_normal(Point3D p1, Point3D p2, 
75                         Point3D p3, double normal[3])
76 {
77     double v1[3], v2[3];
78     double temp;
79
80     v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2];
81     v2[0] = p3[0] - p1[0]; v2[1] = p3[1] - p1[1]; v2[2] = p3[2] - p1[2];
82
83     MAT3cross_product(normal, v1, v2);
84     MAT3_NORMALIZE_VEC(normal,temp);
85
86     // fgPrintf( FG_TERRAIN, FG_DEBUG, "  Normal = %.2f %.2f %.2f\n", 
87     //           normal[0], normal[1], normal[2]);
88 }
89
90
91 #define FG_TEX_CONSTANT 69.0
92
93
94 // Calculate texture coordinates for a given point.
95 static Point3D calc_tex_coords(const Point3D& node, const Point3D& ref) {
96     Point3D cp;
97     Point3D pp;
98     // double tmplon, tmplat;
99
100     // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
101     // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
102
103     cp = Point3D( node[0] + ref.x(),
104                   node[1] + ref.y(),
105                   node[2] + ref.z() );
106
107     pp = fgCartToPolar3d(cp);
108
109     // tmplon = pp.lon() * RAD_TO_DEG;
110     // tmplat = pp.lat() * RAD_TO_DEG;
111     // cout << tmplon << " " << tmplat << endl;
112
113     pp.setx( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.x(), 11.0) );
114     pp.sety( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.y(), 11.0) );
115
116     if ( pp.x() < 0.0 ) {
117         pp.setx( pp.x() + 11.0 );
118     }
119
120     if ( pp.y() < 0.0 ) {
121         pp.sety( pp.y() + 11.0 );
122     }
123
124     // cout << pp << endl;
125
126     return(pp);
127 }
128
129
130 // Load a .obj file and build the GL fragment list
131 int fgObjLoad( const string& path, FGTileEntry *t) {
132     fgFRAGMENT fragment;
133     Point3D pp;
134     double approx_normal[3], normal[3] /*, scale = 0.0 */;
135     // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
136     // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
137     GLint display_list = 0;
138     int shading;
139     int in_fragment = 0, in_faces = 0, vncount, vtcount;
140     int n1 = 0, n2 = 0, n3 = 0, n4 = 0;
141     int tex;
142     int last1 = 0, last2 = 0, odd = 0;
143     point_list nodes;
144     Point3D node;
145     Point3D center;
146     double tex_width = 1000.0, tex_height = 1000.0;
147
148     // printf("loading %s\n", path.c_str() );
149
150     // Attempt to open "path.gz" or "path"
151     fg_gzifstream in( path );
152     if ( ! in.is_open() ) {
153         FG_LOG( FG_TERRAIN, FG_ALERT, "Cannot open file: " << path );
154         return 0;
155     }
156
157     shading = current_options.get_shading();
158
159     in_fragment = 0;
160     t->ncount = 0;
161     vncount = 0;
162     vtcount = 0;
163     t->bounding_radius = 0.0;
164     center = t->center;
165
166     StopWatch stopwatch;
167     stopwatch.start();
168
169     // ignore initial comments and blank lines. (priming the pump)
170     // in >> skipcomment;
171     string line;
172
173     while ( ! in.eof() ) {
174         string token;
175         char c;
176
177 #if defined( MACOS )
178         in >> ::skipws;
179 #else
180         in >> skipws;
181 #endif
182
183         if ( in.get( c ) && c == '#' ) {
184             // process a comment line
185
186             // getline( in, line );
187             // cout << "comment = " << line << endl;
188
189             in >> token;
190
191             if ( token == "gbs" ) {
192                 // reference point (center offset)
193                 in >> t->center >> t->bounding_radius;
194                 center = t->center;
195                 // cout << "center = " << center 
196                 //      << " radius = " << t->bounding_radius << endl;
197             } else if ( token == "bs" ) {
198                 // reference point (center offset)
199                 in >> fragment.center;
200                 in >> fragment.bounding_radius;
201
202                 // cout << "center = " << fragment.center 
203                 //      << " radius = " << fragment.bounding_radius << endl;
204             } else if ( token == "usemtl" ) {
205                 // material property specification
206
207                 // series of individual triangles
208                 if ( in_faces ) {
209                     xglEnd();
210                 }
211
212                 // this also signals the start of a new fragment
213                 if ( in_fragment ) {
214                     // close out the previous structure and start the next
215                     xglEndList();
216                     // printf("xglEnd(); xglEndList();\n");
217
218                     // update fragment
219                     fragment.display_list = display_list;
220
221                     // push this fragment onto the tile's object list
222                     t->fragment_list.push_back(fragment);
223                 } else {
224                     in_fragment = 1;
225                 }
226
227                 // printf("start of fragment (usemtl)\n");
228
229                 display_list = xglGenLists(1);
230                 xglNewList(display_list, GL_COMPILE);
231                 // printf("xglGenLists(); xglNewList();\n");
232                 in_faces = 0;
233
234                 // reset the existing face list
235                 // printf("cleaning a fragment with %d faces\n", 
236                 //        fragment.faces.size());
237                 fragment.init();
238                 
239                 // scan the material line
240                 string material;
241                 in >> material;
242                 fragment.tile_ptr = t;
243                 
244                 // find this material in the properties list
245                 if ( ! material_mgr.find( material, fragment.material_ptr )) {
246                     FG_LOG( FG_TERRAIN, FG_ALERT, 
247                             "Ack! unknown usemtl name = " << material 
248                             << " in " << path );
249                 }
250
251                 // set the texture width and height values for this
252                 // material
253                 FGMaterial m = fragment.material_ptr->get_m();
254                 tex_width = m.get_xsize();
255                 tex_height = m.get_ysize();
256                 // cout << "(w) = " << tex_width << " (h) = " 
257                 //      << tex_width << endl;
258
259                 // initialize the fragment transformation matrix
260                 /*
261                  for ( i = 0; i < 16; i++ ) {
262                    fragment.matrix[i] = 0.0;
263                  }
264                  fragment.matrix[0] = fragment.matrix[5] =
265                  fragment.matrix[10] = fragment.matrix[15] = 1.0;
266                 */
267             } else {
268                 // unknown comment, just gobble the input untill the
269                 // end of line
270
271                 in >> skipeol;
272             }
273         } else {
274             in.putback( c );
275         
276             in >> token;
277
278             // cout << "token = " << token << endl;
279
280             if ( token == "vn" ) {
281                 // vertex normal
282                 if ( vncount < FG_MAX_NODES ) {
283                     in >> normals[vncount][0]
284                        >> normals[vncount][1]
285                        >> normals[vncount][2];
286                     vncount++;
287                 } else {
288                     FG_LOG( FG_TERRAIN, FG_ALERT, 
289                             "Read too many vertex normals in " << path 
290                             << " ... dying :-(" );
291                     exit(-1);
292                 }
293             } else if ( token == "vt" ) {
294                 // vertex texture coordinate
295                 if ( vtcount < FG_MAX_NODES*3 ) {
296                     in >> tex_coords[vtcount][0]
297                        >> tex_coords[vtcount][1];
298                     vtcount++;
299                 } else {
300                     FG_LOG( FG_TERRAIN, FG_ALERT, 
301                             "Read too many vertex texture coords in " << path
302                             << " ... dying :-("
303                             );
304                     exit(-1);
305                 }
306             } else if ( token == "v" ) {
307                 // node (vertex)
308                 if ( t->ncount < FG_MAX_NODES ) {
309                     /* in >> nodes[t->ncount][0]
310                        >> nodes[t->ncount][1]
311                        >> nodes[t->ncount][2]; */
312                     in >> node;
313                     nodes.push_back(node);
314                     t->ncount++;
315                 } else {
316                     FG_LOG( FG_TERRAIN, FG_ALERT, 
317                             "Read too many nodes in " << path 
318                             << " ... dying :-(");
319                     exit(-1);
320                 }
321             } else if ( token == "t" ) {
322                 // start a new triangle strip
323
324                 n1 = n2 = n3 = n4 = 0;
325
326                 // fgPrintf( FG_TERRAIN, FG_DEBUG, 
327                 //           "    new tri strip = %s", line);
328                 in >> n1 >> n2 >> n3;
329                 fragment.add_face(n1, n2, n3);
330
331                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "(t) = ");
332
333                 xglBegin(GL_TRIANGLE_STRIP);
334                 // printf("xglBegin(tristrip) %d %d %d\n", n1, n2, n3);
335
336                 odd = 1; 
337                 // scale = 1.0;
338
339                 if ( shading ) {
340                     // Shading model is "GL_SMOOTH" so use precalculated
341                     // (averaged) normals
342                     // MAT3_SCALE_VEC(normal, normals[n1], scale);
343                     xglNormal3dv(normal);
344                     pp = calc_tex_coords(nodes[n1], center);
345                     xglTexCoord2f(pp.lon(), pp.lat());
346                     xglVertex3dv(nodes[n1].get_n());            
347
348                     // MAT3_SCALE_VEC(normal, normals[n2], scale);
349                     xglNormal3dv(normal);
350                     pp = calc_tex_coords(nodes[n2], center);
351                     xglTexCoord2f(pp.lon(), pp.lat());
352                     xglVertex3dv(nodes[n2].get_n());                            
353
354                     // MAT3_SCALE_VEC(normal, normals[n3], scale);
355                     xglNormal3dv(normal);
356                     pp = calc_tex_coords(nodes[n3], center);
357                     xglTexCoord2f(pp.lon(), pp.lat());
358                     xglVertex3dv(nodes[n3].get_n());
359                 } else {
360                     // Shading model is "GL_FLAT" so calculate per face
361                     // normals on the fly.
362                     if ( odd ) {
363                         calc_normal(nodes[n1], nodes[n2], 
364                                     nodes[n3], approx_normal);
365                     } else {
366                         calc_normal(nodes[n2], nodes[n1], 
367                                     nodes[n3], approx_normal);
368                     }
369                     // MAT3_SCALE_VEC(normal, approx_normal, scale);
370                     xglNormal3dv(normal);
371
372                     pp = calc_tex_coords(nodes[n1], center);
373                     xglTexCoord2f(pp.lon(), pp.lat());
374                     xglVertex3dv(nodes[n1].get_n());            
375
376                     pp = calc_tex_coords(nodes[n2], center);
377                     xglTexCoord2f(pp.lon(), pp.lat());
378                     xglVertex3dv(nodes[n2].get_n());            
379                     
380                     pp = calc_tex_coords(nodes[n3], center);
381                     xglTexCoord2f(pp.lon(), pp.lat());
382                     xglVertex3dv(nodes[n3].get_n());            
383                 }
384                 // printf("some normals, texcoords, and vertices\n");
385
386                 odd = 1 - odd;
387                 last1 = n2;
388                 last2 = n3;
389
390                 // There can be three or four values 
391                 char c;
392                 while ( in.get(c) ) {
393                     if ( c == '\n' ) {
394                         break; // only the one
395                     }
396                     if ( isdigit(c) ){
397                         in.putback(c);
398                         in >> n4;
399                         break;
400                     }
401                 }
402
403                 if ( n4 > 0 ) {
404                     fragment.add_face(n3, n2, n4);
405
406                     if ( shading ) {
407                         // Shading model is "GL_SMOOTH"
408                         // MAT3_SCALE_VEC(normal, normals[n4], scale);
409                     } else {
410                         // Shading model is "GL_FLAT"
411                         calc_normal(nodes[n3], nodes[n2], nodes[n4], 
412                                     approx_normal);
413                         // MAT3_SCALE_VEC(normal, approx_normal, scale);
414                     }
415                     xglNormal3dv(normal);
416                     pp = calc_tex_coords(nodes[n4], center);
417                     xglTexCoord2f(pp.lon(), pp.lat());
418                     xglVertex3dv(nodes[n4].get_n());            
419                     
420                     odd = 1 - odd;
421                     last1 = n3;
422                     last2 = n4;
423                     // printf("a normal, texcoord, and vertex (4th)\n");
424                 }
425             } else if ( token == "tf" ) {
426                 // triangle fan
427                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new fan");
428
429                 xglBegin(GL_TRIANGLE_FAN);
430
431                 in >> n1;
432                 xglNormal3dv(normals[n1]);
433                 if ( in.get( c ) && c == '/' ) {
434                     in >> tex;
435                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
436                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
437                 } else {
438                     in.putback( c );
439                     pp = calc_tex_coords(nodes[n1], center);
440                 }
441                 xglTexCoord2f(pp.x(), pp.y());
442                 xglVertex3dv(nodes[n1].get_n());
443
444                 in >> n2;
445                 xglNormal3dv(normals[n2]);
446                 if ( in.get( c ) && c == '/' ) {
447                     in >> tex;
448                     pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
449                     pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
450                 } else {
451                     in.putback( c );
452                     pp = calc_tex_coords(nodes[n2], center);
453                 }
454                 xglTexCoord2f(pp.x(), pp.y());
455                 xglVertex3dv(nodes[n2].get_n());
456                 
457                 // read all subsequent numbers until next thing isn't a number
458                 while ( true ) {
459 #if defined( MACOS )
460                     in >> ::skipws;
461 #else
462                     in >> skipws;
463 #endif
464
465                     char c;
466                     in.get(c);
467                     in.putback(c);
468                     if ( ! isdigit(c) || in.eof() ) {
469                         break;
470                     }
471
472                     in >> n3;
473                     // cout << "  triangle = " 
474                     //      << n1 << "," << n2 << "," << n3 
475                     //      << endl;
476                     xglNormal3dv(normals[n3]);
477                     if ( in.get( c ) && c == '/' ) {
478                         in >> tex;
479                         pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
480                         pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
481                     } else {
482                         in.putback( c );
483                         pp = calc_tex_coords(nodes[n3], center);
484                     }
485                     xglTexCoord2f(pp.x(), pp.y());
486                     xglVertex3dv(nodes[n3].get_n());
487
488                     fragment.add_face(n1, n2, n3);
489                     n2 = n3;
490                 }
491
492                 xglEnd();
493             } else if ( token == "f" ) {
494                 // unoptimized face
495
496                 if ( !in_faces ) {
497                     xglBegin(GL_TRIANGLES);
498                     // printf("xglBegin(triangles)\n");
499                     in_faces = 1;
500                 }
501
502                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new triangle = %s", line);*/
503                 in >> n1 >> n2 >> n3;
504                 fragment.add_face(n1, n2, n3);
505
506                 // xglNormal3d(normals[n1][0], normals[n1][1], normals[n1][2]);
507                 xglNormal3dv(normals[n1]);
508                 pp = calc_tex_coords(nodes[n1], center);
509                 xglTexCoord2f(pp.lon(), pp.lat());
510                 xglVertex3dv(nodes[n1].get_n());
511
512                 xglNormal3dv(normals[n2]);
513                 pp = calc_tex_coords(nodes[n2], center);
514                 xglTexCoord2f(pp.lon(), pp.lat());
515                 xglVertex3dv(nodes[n2].get_n());
516                 
517                 xglNormal3dv(normals[n3]);
518                 pp = calc_tex_coords(nodes[n3], center);
519                 xglTexCoord2f(pp.lon(), pp.lat());
520                 xglVertex3dv(nodes[n3].get_n());
521                 // printf("some normals, texcoords, and vertices (tris)\n");
522             } else if ( token == "q" ) {
523                 // continue a triangle strip
524                 n1 = n2 = 0;
525
526                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "continued tri strip = %s ", 
527                 //           line);
528                 in >> n1;
529
530                 // There can be one or two values 
531                 char c;
532                 while ( in.get(c) ) {
533                     if ( c == '\n' ) {
534                         break; // only the one
535                     }
536
537                     if ( isdigit(c) ) {
538                         in.putback(c);
539                         in >> n2;
540                         break;
541                     }
542                 }
543                 // fgPrintf( FG_TERRAIN, FG_DEBUG, "read %d %d\n", n1, n2);
544
545                 if ( odd ) {
546                     fragment.add_face(last1, last2, n1);
547                 } else {
548                     fragment.add_face(last2, last1, n1);
549                 }
550
551                 if ( shading ) {
552                     // Shading model is "GL_SMOOTH"
553                     // MAT3_SCALE_VEC(normal, normals[n1], scale);
554                 } else {
555                     // Shading model is "GL_FLAT"
556                     if ( odd ) {
557                         calc_normal(nodes[last1], nodes[last2], 
558                                     nodes[n1], approx_normal);
559                     } else {
560                         calc_normal(nodes[last2], nodes[last1], 
561                                     nodes[n1], approx_normal);
562                     }
563                     // MAT3_SCALE_VEC(normal, approx_normal, scale);
564                 }
565                 xglNormal3dv(normal);
566
567                 pp = calc_tex_coords(nodes[n1], center);
568                 xglTexCoord2f(pp.lon(), pp.lat());
569                 xglVertex3dv(nodes[n1].get_n());
570                 // printf("a normal, texcoord, and vertex (4th)\n");
571    
572                 odd = 1 - odd;
573                 last1 = last2;
574                 last2 = n1;
575
576                 if ( n2 > 0 ) {
577                     // fgPrintf( FG_TERRAIN, FG_DEBUG, " (cont)\n");
578
579                     if ( odd ) {
580                         fragment.add_face(last1, last2, n2);
581                     } else {
582                         fragment.add_face(last2, last1, n2);
583                     }
584
585                     if ( shading ) {
586                         // Shading model is "GL_SMOOTH"
587                         // MAT3_SCALE_VEC(normal, normals[n2], scale);
588                     } else {
589                         // Shading model is "GL_FLAT"
590                         if ( odd ) {
591                             calc_normal(nodes[last1], nodes[last2], 
592                                         nodes[n2], approx_normal);
593                         } else {
594                             calc_normal(nodes[last2], nodes[last1], 
595                                         nodes[n2], approx_normal);
596                         }
597                         // MAT3_SCALE_VEC(normal, approx_normal, scale);
598                     }
599                     xglNormal3dv(normal);
600                 
601                     pp = calc_tex_coords(nodes[n2], center);
602                     xglTexCoord2f(pp.lon(), pp.lat());
603                     xglVertex3dv(nodes[n2].get_n());            
604                     // printf("a normal, texcoord, and vertex (4th)\n");
605
606                     odd = 1 -odd;
607                     last1 = last2;
608                     last2 = n2;
609                 }
610             } else {
611                 FG_LOG( FG_TERRAIN, FG_WARN, "Unknown token in " 
612                         << path << " = " << token );
613             }
614
615             // eat white space before start of while loop so if we are
616             // done with useful input it is noticed before hand.
617 #if defined( MACOS )
618             in >> ::skipws;
619 #else
620             in >> skipws;
621 #endif
622         }
623     }
624
625     if ( in_fragment ) {
626         // close out the previous structure and start the next
627         xglEnd();
628         xglEndList();
629         // printf("xglEnd(); xglEndList();\n");
630         
631         // update fragment
632         fragment.display_list = display_list;
633         
634         // push this fragment onto the tile's object list
635         t->fragment_list.push_back(fragment);
636     }
637
638 #if 0
639     // Draw normal vectors (for visually verifying normals)
640     xglBegin(GL_LINES);
641     xglColor3f(0.0, 0.0, 0.0);
642     for ( i = 0; i < t->ncount; i++ ) {
643         xglVertex3d(nodes[i][0],
644                     nodes[i][1] ,
645                     nodes[i][2]);
646         xglVertex3d(nodes[i][0] + 500*normals[i][0],
647                     nodes[i][1] + 500*normals[i][1],
648                     nodes[i][2] + 500*normals[i][2]);
649     } 
650     xglEnd();
651 #endif
652
653     t->nodes = nodes;
654
655     stopwatch.stop();
656     FG_LOG( FG_TERRAIN, FG_INFO, 
657             "Loaded " << path << " in " 
658             << stopwatch.elapsedSeconds() << " seconds" );
659     
660     return 1;
661 }
662
663