1 // obj.cxx -- routines to handle "sorta" WaveFront .obj format files.
3 // Written by Curtis Olson, started October 1997.
5 // Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
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.
28 #ifdef FG_MATH_EXCEPTION_CLASH
41 // #if defined ( __sun__ )
42 // extern "C" void *memmove(void *, const void *, size_t);
43 // extern "C" void *memset(void *, int, size_t);
46 #include <Include/compiler.h>
50 #include <vector> // STL
51 #include <ctype.h> // isdigit()
53 #include <Debug/logstream.hxx>
54 #include <Misc/fgstream.hxx>
55 #include <Include/fg_constants.h>
56 #include <Main/options.hxx>
57 #include <Math/mat3.h>
58 #include <Math/fg_random.h>
59 #include <Math/point3d.hxx>
60 #include <Math/polar3d.hxx>
61 #include <Misc/stopwatch.hxx>
62 #include <Scenery/tileentry.hxx>
64 #include "materialmgr.hxx"
71 typedef vector < int > int_list;
72 typedef int_list::iterator int_list_iterator;
73 typedef int_list::const_iterator int_point_list_iterator;
76 static double normals[FG_MAX_NODES][3];
77 static double tex_coords[FG_MAX_NODES*3][3];
80 // given three points defining a triangle, calculate the normal
81 static void calc_normal(Point3D p1, Point3D p2,
82 Point3D p3, double normal[3])
87 v1[0] = p2[0] - p1[0]; v1[1] = p2[1] - p1[1]; v1[2] = p2[2] - p1[2];
88 v2[0] = p3[0] - p1[0]; v2[1] = p3[1] - p1[1]; v2[2] = p3[2] - p1[2];
90 MAT3cross_product(normal, v1, v2);
91 MAT3_NORMALIZE_VEC(normal,temp);
93 // fgPrintf( FG_TERRAIN, FG_DEBUG, " Normal = %.2f %.2f %.2f\n",
94 // normal[0], normal[1], normal[2]);
98 #define FG_TEX_CONSTANT 69.0
101 // Calculate texture coordinates for a given point.
102 static Point3D calc_tex_coords(const Point3D& node, const Point3D& ref) {
105 // double tmplon, tmplat;
107 // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
108 // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
110 cp = Point3D( node[0] + ref.x(),
114 pp = fgCartToPolar3d(cp);
116 // tmplon = pp.lon() * RAD_TO_DEG;
117 // tmplat = pp.lat() * RAD_TO_DEG;
118 // cout << tmplon << " " << tmplat << endl;
120 pp.setx( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.x(), 11.0) );
121 pp.sety( fmod(RAD_TO_DEG * FG_TEX_CONSTANT * pp.y(), 11.0) );
123 if ( pp.x() < 0.0 ) {
124 pp.setx( pp.x() + 11.0 );
127 if ( pp.y() < 0.0 ) {
128 pp.sety( pp.y() + 11.0 );
131 // cout << pp << endl;
137 // Load a .obj file and build the GL fragment list
138 ssgBranch *fgObjLoad( const string& path, FGTileEntry *t) {
141 double approx_normal[3], normal[3] /*, scale = 0.0 */;
142 // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
143 // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
144 GLint display_list = 0;
146 bool in_fragment = false, in_faces = false;
147 int vncount, vtcount;
148 int n1 = 0, n2 = 0, n3 = 0, n4 = 0;
150 int last1 = 0, last2 = 0, odd = 0;
154 double tex_width = 1000.0, tex_height = 1000.0;
155 bool shared_done = false;
156 int_list fan_vertices;
157 int_list fan_tex_coords;
159 ssgSimpleState *state = NULL;
161 ssgBranch *tile = new ssgBranch () ;
162 tile -> setName ( path.c_str() ) ;
164 // Attempt to open "path.gz" or "path"
165 fg_gzifstream in( path );
166 if ( ! in.is_open() ) {
167 FG_LOG( FG_TERRAIN, FG_ALERT, "Cannot open file: " << path );
171 shading = current_options.get_shading();
177 t->bounding_radius = 0.0;
183 // ignore initial comments and blank lines. (priming the pump)
184 // in >> skipcomment;
191 while ( in.get(c) && c != '\0' ) {
194 while ( ! in.eof() ) {
203 if ( in.get( c ) && c == '#' ) {
204 // process a comment line
206 // getline( in, line );
207 // cout << "comment = " << line << endl;
211 if ( token == "gbs" ) {
212 // reference point (center offset)
213 in >> t->center >> t->bounding_radius;
215 // cout << "center = " << center
216 // << " radius = " << t->bounding_radius << endl;
217 } else if ( token == "bs" ) {
218 // reference point (center offset)
219 in >> fragment.center;
220 in >> fragment.bounding_radius;
222 // cout << "center = " << fragment.center
223 // << " radius = " << fragment.bounding_radius << endl;
224 } else if ( token == "usemtl" ) {
225 // material property specification
227 // if first usemtl with shared_done = false, then set
228 // shared_done true and build the ssg shared lists
229 if ( ! shared_done ) {
231 if ( nodes.size() != vncount ) {
232 FG_LOG( FG_TERRAIN, FG_ALERT,
233 "Tile has mismatched nodes and normals: "
239 t->vtlist = new sgVec3 [ nodes.size() ];
240 t->vnlist = new sgVec3 [ vncount ];
241 t->tclist = new sgVec2 [ vtcount ];
243 for ( i = 0; i < (int)nodes.size(); ++i ) {
244 sgSetVec3( t->vtlist[i],
245 nodes[i][0], nodes[i][1], nodes[i][2] );
247 for ( i = 0; i < vncount; ++i ) {
248 sgSetVec3( t->vnlist[i],
253 for ( i = 0; i < vtcount; ++i ) {
254 sgSetVec2( t->tclist[i],
255 tex_coords[i][0], tex_coords[i][1] );
259 // series of individual triangles
264 // this also signals the start of a new fragment
266 // close out the previous structure and start the next
268 // printf("xglEnd(); xglEndList();\n");
271 fragment.display_list = display_list;
273 // push this fragment onto the tile's object list
274 t->fragment_list.push_back(fragment);
279 // printf("start of fragment (usemtl)\n");
281 display_list = xglGenLists(1);
282 xglNewList(display_list, GL_COMPILE);
283 // printf("xglGenLists(); xglNewList();\n");
286 // reset the existing face list
287 // printf("cleaning a fragment with %d faces\n",
288 // fragment.faces.size());
291 // scan the material line
294 fragment.tile_ptr = t;
296 // find this material in the properties list
297 if ( ! material_mgr.find( material, fragment.material_ptr )) {
298 FG_LOG( FG_TERRAIN, FG_ALERT,
299 "Ack! unknown usemtl name = " << material
303 // set the texture width and height values for this
305 FGMaterial m = fragment.material_ptr->get_m();
306 tex_width = m.get_xsize();
307 tex_height = m.get_ysize();
308 state = fragment.material_ptr->get_state();
309 // cout << "(w) = " << tex_width << " (h) = "
310 // << tex_width << endl;
312 // initialize the fragment transformation matrix
314 for ( i = 0; i < 16; i++ ) {
315 fragment.matrix[i] = 0.0;
317 fragment.matrix[0] = fragment.matrix[5] =
318 fragment.matrix[10] = fragment.matrix[15] = 1.0;
321 // unknown comment, just gobble the input untill the
331 // cout << "token = " << token << endl;
333 if ( token == "vn" ) {
335 if ( vncount < FG_MAX_NODES ) {
336 in >> normals[vncount][0]
337 >> normals[vncount][1]
338 >> normals[vncount][2];
341 FG_LOG( FG_TERRAIN, FG_ALERT,
342 "Read too many vertex normals in " << path
343 << " ... dying :-(" );
346 } else if ( token == "vt" ) {
347 // vertex texture coordinate
348 if ( vtcount < FG_MAX_NODES*3 ) {
349 in >> tex_coords[vtcount][0]
350 >> tex_coords[vtcount][1];
353 FG_LOG( FG_TERRAIN, FG_ALERT,
354 "Read too many vertex texture coords in " << path
359 } else if ( token == "v" ) {
361 if ( t->ncount < FG_MAX_NODES ) {
362 /* in >> nodes[t->ncount][0]
363 >> nodes[t->ncount][1]
364 >> nodes[t->ncount][2]; */
366 nodes.push_back(node);
369 FG_LOG( FG_TERRAIN, FG_ALERT,
370 "Read too many nodes in " << path
371 << " ... dying :-(");
374 } else if ( token == "t" ) {
375 // start a new triangle strip
377 n1 = n2 = n3 = n4 = 0;
379 // fgPrintf( FG_TERRAIN, FG_DEBUG,
380 // " new tri strip = %s", line);
381 in >> n1 >> n2 >> n3;
382 fragment.add_face(n1, n2, n3);
384 // fgPrintf( FG_TERRAIN, FG_DEBUG, "(t) = ");
386 xglBegin(GL_TRIANGLE_STRIP);
387 // printf("xglBegin(tristrip) %d %d %d\n", n1, n2, n3);
393 // Shading model is "GL_SMOOTH" so use precalculated
394 // (averaged) normals
395 // MAT3_SCALE_VEC(normal, normals[n1], scale);
396 xglNormal3dv(normal);
397 pp = calc_tex_coords(nodes[n1], center);
398 xglTexCoord2f(pp.lon(), pp.lat());
399 xglVertex3dv(nodes[n1].get_n());
401 // MAT3_SCALE_VEC(normal, normals[n2], scale);
402 xglNormal3dv(normal);
403 pp = calc_tex_coords(nodes[n2], center);
404 xglTexCoord2f(pp.lon(), pp.lat());
405 xglVertex3dv(nodes[n2].get_n());
407 // MAT3_SCALE_VEC(normal, normals[n3], scale);
408 xglNormal3dv(normal);
409 pp = calc_tex_coords(nodes[n3], center);
410 xglTexCoord2f(pp.lon(), pp.lat());
411 xglVertex3dv(nodes[n3].get_n());
413 // Shading model is "GL_FLAT" so calculate per face
414 // normals on the fly.
416 calc_normal(nodes[n1], nodes[n2],
417 nodes[n3], approx_normal);
419 calc_normal(nodes[n2], nodes[n1],
420 nodes[n3], approx_normal);
422 // MAT3_SCALE_VEC(normal, approx_normal, scale);
423 xglNormal3dv(normal);
425 pp = calc_tex_coords(nodes[n1], center);
426 xglTexCoord2f(pp.lon(), pp.lat());
427 xglVertex3dv(nodes[n1].get_n());
429 pp = calc_tex_coords(nodes[n2], center);
430 xglTexCoord2f(pp.lon(), pp.lat());
431 xglVertex3dv(nodes[n2].get_n());
433 pp = calc_tex_coords(nodes[n3], center);
434 xglTexCoord2f(pp.lon(), pp.lat());
435 xglVertex3dv(nodes[n3].get_n());
437 // printf("some normals, texcoords, and vertices\n");
443 // There can be three or four values
445 while ( in.get(c) ) {
447 break; // only the one
457 fragment.add_face(n3, n2, n4);
460 // Shading model is "GL_SMOOTH"
461 // MAT3_SCALE_VEC(normal, normals[n4], scale);
463 // Shading model is "GL_FLAT"
464 calc_normal(nodes[n3], nodes[n2], nodes[n4],
466 // MAT3_SCALE_VEC(normal, approx_normal, scale);
468 xglNormal3dv(normal);
469 pp = calc_tex_coords(nodes[n4], center);
470 xglTexCoord2f(pp.lon(), pp.lat());
471 xglVertex3dv(nodes[n4].get_n());
476 // printf("a normal, texcoord, and vertex (4th)\n");
478 } else if ( token == "tf" ) {
480 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new fan");
482 fan_vertices.clear();
483 fan_tex_coords.clear();
485 xglBegin(GL_TRIANGLE_FAN);
488 fan_vertices.push_back( n1 );
489 xglNormal3dv(normals[n1]);
490 if ( in.get( c ) && c == '/' ) {
492 fan_tex_coords.push_back( tex );
493 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
494 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
497 pp = calc_tex_coords(nodes[n1], center);
499 xglTexCoord2f(pp.x(), pp.y());
500 xglVertex3dv(nodes[n1].get_n());
503 fan_vertices.push_back( n2 );
504 xglNormal3dv(normals[n2]);
505 if ( in.get( c ) && c == '/' ) {
507 fan_tex_coords.push_back( tex );
508 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
509 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
512 pp = calc_tex_coords(nodes[n2], center);
514 xglTexCoord2f(pp.x(), pp.y());
515 xglVertex3dv(nodes[n2].get_n());
517 // read all subsequent numbers until next thing isn't a number
528 if ( ! isdigit(c) || in.eof() ) {
533 fan_vertices.push_back( n3 );
534 // cout << " triangle = "
535 // << n1 << "," << n2 << "," << n3
537 xglNormal3dv(normals[n3]);
538 if ( in.get( c ) && c == '/' ) {
540 fan_tex_coords.push_back( tex );
541 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
542 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
545 pp = calc_tex_coords(nodes[n3], center);
547 xglTexCoord2f(pp.x(), pp.y());
548 xglVertex3dv(nodes[n3].get_n());
550 fragment.add_face(n1, n2, n3);
556 // build the ssg entity
557 unsigned short *vindex =
558 new unsigned short [ fan_vertices.size() ];
559 unsigned short *tindex =
560 new unsigned short [ fan_tex_coords.size() ];
561 for ( i = 0; i < (int)fan_vertices.size(); ++i ) {
562 vindex[i] = fan_vertices[i];
564 for ( i = 0; i < (int)fan_tex_coords.size(); ++i ) {
565 tindex[i] = fan_tex_coords[i];
568 new ssgVTable ( GL_TRIANGLE_FAN,
569 fan_vertices.size(), vindex, t->vtlist,
570 fan_vertices.size(), vindex, t->vnlist,
571 fan_tex_coords.size(), tindex, t->tclist,
573 leaf->setState( state );
575 tile->addKid( leaf );
577 } else if ( token == "f" ) {
581 xglBegin(GL_TRIANGLES);
582 // printf("xglBegin(triangles)\n");
586 // fgPrintf( FG_TERRAIN, FG_DEBUG, "new triangle = %s", line);*/
587 in >> n1 >> n2 >> n3;
588 fragment.add_face(n1, n2, n3);
590 // xglNormal3d(normals[n1][0], normals[n1][1], normals[n1][2]);
591 xglNormal3dv(normals[n1]);
592 pp = calc_tex_coords(nodes[n1], center);
593 xglTexCoord2f(pp.lon(), pp.lat());
594 xglVertex3dv(nodes[n1].get_n());
596 xglNormal3dv(normals[n2]);
597 pp = calc_tex_coords(nodes[n2], center);
598 xglTexCoord2f(pp.lon(), pp.lat());
599 xglVertex3dv(nodes[n2].get_n());
601 xglNormal3dv(normals[n3]);
602 pp = calc_tex_coords(nodes[n3], center);
603 xglTexCoord2f(pp.lon(), pp.lat());
604 xglVertex3dv(nodes[n3].get_n());
605 // printf("some normals, texcoords, and vertices (tris)\n");
606 } else if ( token == "q" ) {
607 // continue a triangle strip
610 // fgPrintf( FG_TERRAIN, FG_DEBUG, "continued tri strip = %s ",
614 // There can be one or two values
616 while ( in.get(c) ) {
618 break; // only the one
627 // fgPrintf( FG_TERRAIN, FG_DEBUG, "read %d %d\n", n1, n2);
630 fragment.add_face(last1, last2, n1);
632 fragment.add_face(last2, last1, n1);
636 // Shading model is "GL_SMOOTH"
637 // MAT3_SCALE_VEC(normal, normals[n1], scale);
639 // Shading model is "GL_FLAT"
641 calc_normal(nodes[last1], nodes[last2],
642 nodes[n1], approx_normal);
644 calc_normal(nodes[last2], nodes[last1],
645 nodes[n1], approx_normal);
647 // MAT3_SCALE_VEC(normal, approx_normal, scale);
649 xglNormal3dv(normal);
651 pp = calc_tex_coords(nodes[n1], center);
652 xglTexCoord2f(pp.lon(), pp.lat());
653 xglVertex3dv(nodes[n1].get_n());
654 // printf("a normal, texcoord, and vertex (4th)\n");
661 // fgPrintf( FG_TERRAIN, FG_DEBUG, " (cont)\n");
664 fragment.add_face(last1, last2, n2);
666 fragment.add_face(last2, last1, n2);
670 // Shading model is "GL_SMOOTH"
671 // MAT3_SCALE_VEC(normal, normals[n2], scale);
673 // Shading model is "GL_FLAT"
675 calc_normal(nodes[last1], nodes[last2],
676 nodes[n2], approx_normal);
678 calc_normal(nodes[last2], nodes[last1],
679 nodes[n2], approx_normal);
681 // MAT3_SCALE_VEC(normal, approx_normal, scale);
683 xglNormal3dv(normal);
685 pp = calc_tex_coords(nodes[n2], center);
686 xglTexCoord2f(pp.lon(), pp.lat());
687 xglVertex3dv(nodes[n2].get_n());
688 // printf("a normal, texcoord, and vertex (4th)\n");
695 FG_LOG( FG_TERRAIN, FG_WARN, "Unknown token in "
696 << path << " = " << token );
699 // eat white space before start of while loop so if we are
700 // done with useful input it is noticed before hand.
710 // close out the previous structure and start the next
713 // printf("xglEnd(); xglEndList();\n");
716 fragment.display_list = display_list;
718 // push this fragment onto the tile's object list
719 t->fragment_list.push_back(fragment);
723 // Draw normal vectors (for visually verifying normals)
725 xglColor3f(0.0, 0.0, 0.0);
726 for ( i = 0; i < t->ncount; i++ ) {
727 xglVertex3d(nodes[i][0],
730 xglVertex3d(nodes[i][0] + 500*normals[i][0],
731 nodes[i][1] + 500*normals[i][1],
732 nodes[i][2] + 500*normals[i][2]);
740 FG_LOG( FG_TERRAIN, FG_INFO,
741 "Loaded " << path << " in "
742 << stopwatch.elapsedSeconds() << " seconds" );