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