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 SG_MATH_EXCEPTION_CLASH
35 #include <simgear/compiler.h>
36 #include <simgear/io/sg_binobj.hxx>
40 #include <vector> // STL
41 #include <ctype.h> // isdigit()
43 #include <simgear/constants.h>
44 #include <simgear/debug/logstream.hxx>
45 #include <simgear/math/point3d.hxx>
46 #include <simgear/math/polar3d.hxx>
47 #include <simgear/math/sg_geodesy.hxx>
48 #include <simgear/math/sg_random.h>
49 #include <simgear/misc/sgstream.hxx>
50 #include <simgear/misc/stopwatch.hxx>
51 #include <simgear/misc/texcoord.hxx>
53 #include <Main/globals.hxx>
54 #include <Main/fg_props.hxx>
55 #include <Scenery/tileentry.hxx>
64 typedef vector < int > int_list;
65 typedef int_list::iterator int_list_iterator;
66 typedef int_list::const_iterator int_point_list_iterator;
69 static double normals[FG_MAX_NODES][3];
70 static double tex_coords[FG_MAX_NODES*3][3];
73 #define FG_TEX_CONSTANT 69.0
75 // Calculate texture coordinates for a given point.
76 static Point3D local_calc_tex_coords(const Point3D& node, const Point3D& ref) {
79 // double tmplon, tmplat;
81 // cout << "-> " << node[0] << " " << node[1] << " " << node[2] << endl;
82 // cout << "-> " << ref.x() << " " << ref.y() << " " << ref.z() << endl;
84 cp = Point3D( node[0] + ref.x(),
88 pp = sgCartToPolar3d(cp);
90 // tmplon = pp.lon() * SGD_RADIANS_TO_DEGREES;
91 // tmplat = pp.lat() * SGD_RADIANS_TO_DEGREES;
92 // cout << tmplon << " " << tmplat << endl;
94 pp.setx( fmod(SGD_RADIANS_TO_DEGREES * FG_TEX_CONSTANT * pp.x(), 11.0) );
95 pp.sety( fmod(SGD_RADIANS_TO_DEGREES * FG_TEX_CONSTANT * pp.y(), 11.0) );
98 pp.setx( pp.x() + 11.0 );
101 if ( pp.y() < 0.0 ) {
102 pp.sety( pp.y() + 11.0 );
105 // cout << pp << endl;
111 // Generate a generic ocean tile on the fly
112 ssgBranch *fgGenTile( const string& path, FGTileEntry *t) {
115 ssgSimpleState *state = NULL;
117 ssgBranch *tile = new ssgBranch () ;
118 tile -> setName ( (char *)path.c_str() ) ;
120 double tex_width = 1000.0;
121 // double tex_height;
123 // find Ocean material in the properties list
124 newmat = material_lib.find( "Ocean" );
125 if ( newmat != NULL ) {
126 // set the texture width and height values for this
128 tex_width = newmat->get_xsize();
129 // tex_height = newmat->get_ysize();
132 state = newmat->get_state();
134 SG_LOG( SG_TERRAIN, SG_ALERT,
135 "Ack! unknown usemtl name = " << "Ocean"
139 // Calculate center point
140 SGBucket b = t->tile_bucket;
141 double clon = b.get_center_lon();
142 double clat = b.get_center_lat();
143 double height = b.get_height();
144 double width = b.get_width();
146 Point3D center = sgGeodToCart(Point3D(clon*SGD_DEGREES_TO_RADIANS,clat*SGD_DEGREES_TO_RADIANS,0.0));
148 // cout << "center = " << center << endl;;
150 // Caculate corner vertices
152 geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
153 geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
154 geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
155 geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
159 for ( i = 0; i < 4; ++i ) {
160 rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS, geod[i].y() * SGD_DEGREES_TO_RADIANS,
164 Point3D cart[4], rel[4];
166 for ( i = 0; i < 4; ++i ) {
167 cart[i] = sgGeodToCart(rad[i]);
168 rel[i] = cart[i] - center;
169 t->nodes.push_back( rel[i] );
170 // cout << "corner " << i << " = " << cart[i] << endl;
175 // Calculate bounding radius
176 t->bounding_radius = center.distance3D( cart[0] );
177 // cout << "bounding radius = " << t->bounding_radius << endl;
181 for ( i = 0; i < 4; ++i ) {
182 normals[i] = cart[i];
183 double length = normals[i].distance3D( Point3D(0.0) );
184 normals[i] /= length;
185 // cout << "normal = " << normals[i] << endl;
188 // Calculate texture coordinates
189 point_list geod_nodes;
191 for ( i = 0; i < 4; ++i ) {
192 geod_nodes.push_back( geod[i] );
196 for ( i = 0; i < 4; ++i ) {
197 rectangle.push_back( i );
199 point_list texs = calc_tex_coords( b, geod_nodes, rectangle,
200 1000.0 / tex_width );
202 // Allocate ssg structure
203 ssgVertexArray *vl = new ssgVertexArray( 4 );
204 ssgNormalArray *nl = new ssgNormalArray( 4 );
205 ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
206 ssgColourArray *cl = new ssgColourArray( 1 );
209 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
212 // sgVec3 *vtlist = new sgVec3 [ 4 ];
213 // t->vec3_ptrs.push_back( vtlist );
214 // sgVec3 *vnlist = new sgVec3 [ 4 ];
215 // t->vec3_ptrs.push_back( vnlist );
216 // sgVec2 *tclist = new sgVec2 [ 4 ];
217 // t->vec2_ptrs.push_back( tclist );
221 for ( i = 0; i < 4; ++i ) {
223 rel[i].x(), rel[i].y(), rel[i].z() );
227 normals[i].x(), normals[i].y(), normals[i].z() );
230 sgSetVec2( tmp2, texs[i].x(), texs[i].y());
235 new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
237 leaf->setState( state );
239 tile->addKid( leaf );
245 #if defined ( PLIB_1_2_X )
246 static float fgTriArea( sgVec3 p0, sgVec3 p1, sgVec3 p2 ) {
248 From comp.graph.algorithms FAQ
249 2A(P) = abs(N.(sum_{i=0}^{n-1}(v_i x v_{i+1})))
255 sgMakeNormal( norm, p0, p1, p2 );
262 for( int i=0; i<3; i++ ) {
264 sum[0] += (vv[i][1] * vv[ii][2] - vv[i][2] * vv[ii][1]) ;
265 sum[1] += (vv[i][2] * vv[ii][0] - vv[i][0] * vv[ii][2]) ;
266 sum[2] += (vv[i][0] * vv[ii][1] - vv[i][1] * vv[ii][0]) ;
269 return( sgAbs(sgScalarProductVec3( norm, sum )) * SG_HALF );
272 # define fgTriArea(p0,p1,p2) sgTriArea(p0,p1,p2)
276 static void random_pt_inside_tri( float *res,
277 float *n1, float *n2, float *n3 )
281 double a = sg_random();
282 double b = sg_random();
287 double c = 1 - a - b;
289 sgScaleVec3( p1, n1, a );
290 sgScaleVec3( p2, n2, b );
291 sgScaleVec3( p3, n3, c );
293 sgAddVec3( res, p1, p2 );
294 sgAddVec3( res, p3 );
298 static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
300 int num = leaf->getNumTriangles();
302 short int n1, n2, n3;
306 // generate a repeatable random seed
307 p1 = leaf->getVertex( 0 );
308 unsigned int *seed = (unsigned int *)p1;
311 for ( int i = 0; i < num; ++i ) {
312 leaf->getTriangle( i, &n1, &n2, &n3 );
313 p1 = leaf->getVertex(n1);
314 p2 = leaf->getVertex(n2);
315 p3 = leaf->getVertex(n3);
316 double area = fgTriArea( p1, p2, p3 );
317 double num = area / factor;
319 // generate a light point for each unit of area
320 while ( num > 1.0 ) {
321 random_pt_inside_tri( result, p1, p2, p3 );
322 lights->add( result );
325 // for partial units of area, use a zombie door method to
326 // create the proper random chance of a light being created
329 if ( sg_random() <= num ) {
330 // a zombie made it through our door
331 random_pt_inside_tri( result, p1, p2, p3 );
332 lights->add( result );
340 // Load an Ascii obj file
341 ssgBranch *fgAsciiObjLoad( const string& path, FGTileEntry *t,
342 ssgVertexArray *lights, const bool is_base)
344 FGNewMat *newmat = NULL;
348 // sgVec3 approx_normal;
349 // double normal[3], scale = 0.0;
350 // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
351 // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
352 // GLint display_list = 0;
354 bool in_faces = false;
355 int vncount, vtcount;
356 int n1 = 0, n2 = 0, n3 = 0;
358 // int last1 = 0, last2 = 0;
363 double scenery_version = 0.0;
364 double tex_width = 1000.0, tex_height = 1000.0;
365 bool shared_done = false;
366 int_list fan_vertices;
367 int_list fan_tex_coords;
369 ssgSimpleState *state = NULL;
370 sgVec3 *vtlist, *vnlist;
373 ssgBranch *tile = new ssgBranch () ;
375 tile -> setName ( (char *)path.c_str() ) ;
377 // Attempt to open "path.gz" or "path"
378 sg_gzifstream in( path );
379 if ( ! in.is_open() ) {
380 SG_LOG( SG_TERRAIN, SG_DEBUG, "Cannot open file: " << path );
381 SG_LOG( SG_TERRAIN, SG_DEBUG, "default to ocean tile: " << path );
386 shading = fgGetBool("/sim/rendering/shading");
394 t->bounding_radius = 0.0;
401 // ignore initial comments and blank lines. (priming the pump)
402 // in >> skipcomment;
409 while ( in.get(c) && c != '\0' ) {
412 while ( ! in.eof() ) {
415 #if defined( macintosh ) || defined( _MSC_VER )
421 if ( in.get( c ) && c == '#' ) {
422 // process a comment line
424 // getline( in, line );
425 // cout << "comment = " << line << endl;
429 if ( token == "Version" ) {
430 // read scenery versions number
431 in >> scenery_version;
432 // cout << "scenery_version = " << scenery_version << endl;
433 if ( scenery_version > 0.4 ) {
434 SG_LOG( SG_TERRAIN, SG_ALERT,
435 "\nYou are attempting to load a tile format that\n"
436 << "is newer than this version of flightgear can\n"
437 << "handle. You should upgrade your copy of\n"
438 << "FlightGear to the newest version. For\n"
439 << "details, please see:\n"
440 << "\n http://www.flightgear.org\n" );
443 } else if ( token == "gbs" ) {
444 // reference point (center offset)
446 in >> t->center >> t->bounding_radius;
450 in >> junk1 >> junk2;
453 // cout << "center = " << center
454 // << " radius = " << t->bounding_radius << endl;
455 } else if ( token == "bs" ) {
456 // reference point (center offset)
460 in >> junk1 >> junk2;
461 } else if ( token == "usemtl" ) {
462 // material property specification
464 // if first usemtl with shared_done = false, then set
465 // shared_done true and build the ssg shared lists
466 if ( ! shared_done ) {
468 if ( (int)nodes.size() != vncount ) {
469 SG_LOG( SG_TERRAIN, SG_ALERT,
470 "Tile has mismatched nodes = " << nodes.size()
471 << " and normals = " << vncount << " : "
477 vtlist = new sgVec3 [ nodes.size() ];
478 t->vec3_ptrs.push_back( vtlist );
479 vnlist = new sgVec3 [ vncount ];
480 t->vec3_ptrs.push_back( vnlist );
481 tclist = new sgVec2 [ vtcount ];
482 t->vec2_ptrs.push_back( tclist );
484 for ( i = 0; i < (int)nodes.size(); ++i ) {
485 sgSetVec3( vtlist[i],
486 nodes[i][0], nodes[i][1], nodes[i][2] );
488 for ( i = 0; i < vncount; ++i ) {
489 sgSetVec3( vnlist[i],
494 for ( i = 0; i < vtcount; ++i ) {
495 sgSetVec2( tclist[i],
501 // display_list = xglGenLists(1);
502 // xglNewList(display_list, GL_COMPILE);
503 // printf("xglGenLists(); xglNewList();\n");
506 // scan the material line
509 // find this material in the properties list
511 newmat = material_lib.find( material );
512 if ( newmat == NULL ) {
513 // see if this is an on the fly texture
515 int pos = file.rfind( "/" );
516 file = file.substr( 0, pos );
517 cout << "current file = " << file << endl;
520 cout << "current file = " << file << endl;
521 if ( ! material_lib.add_item( file ) ) {
522 SG_LOG( SG_TERRAIN, SG_ALERT,
523 "Ack! unknown usemtl name = " << material
526 // locate our newly created material
527 newmat = material_lib.find( material );
528 if ( newmat == NULL ) {
529 SG_LOG( SG_TERRAIN, SG_ALERT,
530 "Ack! bad on the fly materia create = "
531 << material << " in " << path );
536 if ( newmat != NULL ) {
537 // set the texture width and height values for this
539 tex_width = newmat->get_xsize();
540 tex_height = newmat->get_ysize();
541 state = newmat->get_state();
542 coverage = newmat->get_light_coverage();
543 // cout << "(w) = " << tex_width << " (h) = "
544 // << tex_width << endl;
549 // unknown comment, just gobble the input until the
559 // cout << "token = " << token << endl;
561 if ( token == "vn" ) {
563 if ( vncount < FG_MAX_NODES ) {
564 in >> normals[vncount][0]
565 >> normals[vncount][1]
566 >> normals[vncount][2];
569 SG_LOG( SG_TERRAIN, SG_ALERT,
570 "Read too many vertex normals in " << path
571 << " ... dying :-(" );
574 } else if ( token == "vt" ) {
575 // vertex texture coordinate
576 if ( vtcount < FG_MAX_NODES*3 ) {
577 in >> tex_coords[vtcount][0]
578 >> tex_coords[vtcount][1];
581 SG_LOG( SG_TERRAIN, SG_ALERT,
582 "Read too many vertex texture coords in " << path
587 } else if ( token == "v" ) {
589 if ( t->ncount < FG_MAX_NODES ) {
590 /* in >> nodes[t->ncount][0]
591 >> nodes[t->ncount][1]
592 >> nodes[t->ncount][2]; */
594 nodes.push_back(node);
599 SG_LOG( SG_TERRAIN, SG_ALERT,
600 "Read too many nodes in " << path
601 << " ... dying :-(");
604 } else if ( (token == "tf") || (token == "ts") || (token == "f") ) {
605 // triangle fan, strip, or individual face
606 // SG_LOG( SG_TERRAIN, SG_INFO, "new fan or strip");
608 fan_vertices.clear();
609 fan_tex_coords.clear();
612 // xglBegin(GL_TRIANGLE_FAN);
615 fan_vertices.push_back( n1 );
616 // xglNormal3dv(normals[n1]);
617 if ( in.get( c ) && c == '/' ) {
619 fan_tex_coords.push_back( tex );
620 if ( scenery_version >= 0.4 ) {
621 if ( tex_width > 0 ) {
622 tclist[tex][0] *= (1000.0 / tex_width);
624 if ( tex_height > 0 ) {
625 tclist[tex][1] *= (1000.0 / tex_height);
628 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
629 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
632 pp = local_calc_tex_coords(nodes[n1], center);
634 // xglTexCoord2f(pp.x(), pp.y());
635 // xglVertex3dv(nodes[n1].get_n());
638 fan_vertices.push_back( n2 );
639 // xglNormal3dv(normals[n2]);
640 if ( in.get( c ) && c == '/' ) {
642 fan_tex_coords.push_back( tex );
643 if ( scenery_version >= 0.4 ) {
644 if ( tex_width > 0 ) {
645 tclist[tex][0] *= (1000.0 / tex_width);
647 if ( tex_height > 0 ) {
648 tclist[tex][1] *= (1000.0 / tex_height);
651 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
652 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
655 pp = local_calc_tex_coords(nodes[n2], center);
657 // xglTexCoord2f(pp.x(), pp.y());
658 // xglVertex3dv(nodes[n2].get_n());
660 // read all subsequent numbers until next thing isn't a number
662 #if defined( macintosh ) || defined( _MSC_VER )
671 if ( ! isdigit(c) || in.eof() ) {
676 fan_vertices.push_back( n3 );
677 // cout << " triangle = "
678 // << n1 << "," << n2 << "," << n3
680 // xglNormal3dv(normals[n3]);
681 if ( in.get( c ) && c == '/' ) {
683 fan_tex_coords.push_back( tex );
684 if ( scenery_version >= 0.4 ) {
685 if ( tex_width > 0 ) {
686 tclist[tex][0] *= (1000.0 / tex_width);
688 if ( tex_height > 0 ) {
689 tclist[tex][1] *= (1000.0 / tex_height);
692 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
693 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
696 pp = local_calc_tex_coords(nodes[n3], center);
698 // xglTexCoord2f(pp.x(), pp.y());
699 // xglVertex3dv(nodes[n3].get_n());
701 if ( (token == "tf") || (token == "f") ) {
714 // build the ssg entity
715 int size = (int)fan_vertices.size();
716 ssgVertexArray *vl = new ssgVertexArray( size );
717 ssgNormalArray *nl = new ssgNormalArray( size );
718 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
719 ssgColourArray *cl = new ssgColourArray( 1 );
722 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
727 for ( i = 0; i < size; ++i ) {
728 sgCopyVec3( tmp3, vtlist[ fan_vertices[i] ] );
731 sgCopyVec3( tmp3, vnlist[ fan_vertices[i] ] );
734 sgCopyVec2( tmp2, tclist[ fan_tex_coords[i] ] );
738 ssgLeaf *leaf = NULL;
739 if ( token == "tf" ) {
742 new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
743 } else if ( token == "ts" ) {
746 new ssgVtxTable ( GL_TRIANGLE_STRIP, vl, nl, tl, cl );
747 } else if ( token == "f" ) {
750 new ssgVtxTable ( GL_TRIANGLES, vl, nl, tl, cl );
752 // leaf->makeDList();
753 leaf->setState( state );
755 tile->addKid( leaf );
758 if ( coverage > 0.0 ) {
759 if ( coverage < 10000.0 ) {
760 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
761 << coverage << ", pushing up to 10000");
764 gen_random_surface_points(leaf, lights, coverage);
768 SG_LOG( SG_TERRAIN, SG_WARN, "Unknown token in "
769 << path << " = " << token );
772 // eat white space before start of while loop so if we are
773 // done with useful input it is noticed before hand.
774 #if defined( macintosh ) || defined( _MSC_VER )
787 SG_LOG( SG_TERRAIN, SG_DEBUG,
788 "Loaded " << path << " in "
789 << stopwatch.elapsedSeconds() << " seconds" );
795 static ssgLeaf *gen_leaf( const string& path,
796 const GLenum ty, const string& material,
797 const point_list& nodes, const point_list& normals,
798 const point_list& texcoords,
799 const int_list node_index,
800 const int_list& tex_index,
801 const bool calc_lights, ssgVertexArray *lights )
803 double tex_width = 1000.0, tex_height = 1000.0;
804 ssgSimpleState *state = NULL;
807 FGNewMat *newmat = material_lib.find( material );
808 if ( newmat == NULL ) {
809 // see if this is an on the fly texture
811 int pos = file.rfind( "/" );
812 file = file.substr( 0, pos );
813 cout << "current file = " << file << endl;
816 cout << "current file = " << file << endl;
817 if ( ! material_lib.add_item( file ) ) {
818 SG_LOG( SG_TERRAIN, SG_ALERT,
819 "Ack! unknown usemtl name = " << material
822 // locate our newly created material
823 newmat = material_lib.find( material );
824 if ( newmat == NULL ) {
825 SG_LOG( SG_TERRAIN, SG_ALERT,
826 "Ack! bad on the fly material create = "
827 << material << " in " << path );
832 if ( newmat != NULL ) {
833 // set the texture width and height values for this
835 tex_width = newmat->get_xsize();
836 tex_height = newmat->get_ysize();
837 state = newmat->get_state();
838 coverage = newmat->get_light_coverage();
839 // cout << "(w) = " << tex_width << " (h) = "
840 // << tex_width << endl;
845 int size = node_index.size();
846 ssgVertexArray *vl = new ssgVertexArray( size );
847 ssgNormalArray *nl = new ssgNormalArray( size );
848 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
849 ssgColourArray *cl = new ssgColourArray( 1 );
852 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
858 for ( i = 0; i < size; ++i ) {
859 Point3D node = nodes[ node_index[i] ];
860 sgSetVec3( tmp3, node[0], node[1], node[2] );
863 Point3D normal = normals[ node_index[i] ];
864 sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
867 Point3D texcoord = texcoords[ tex_index[i] ];
868 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
869 if ( tex_width > 0 ) {
870 tmp2[0] *= (1000.0 / tex_width);
872 if ( tex_height > 0 ) {
873 tmp2[1] *= (1000.0 / tex_height);
878 // cout << "before leaf create" << endl;
879 ssgLeaf *leaf = new ssgVtxTable ( ty, vl, nl, tl, cl );
880 // cout << "after leaf create" << endl;
882 // lookup the state record
883 // cout << "looking up material = " << endl;
884 // cout << material << endl;
885 // cout << "'" << endl;
887 leaf->setState( state );
890 if ( coverage > 0.0 ) {
891 if ( coverage < 10000.0 ) {
892 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
893 << coverage << ", pushing up to 10000");
896 gen_random_surface_points(leaf, lights, coverage);
904 // Load an Binary obj file
905 ssgBranch *fgBinObjLoad( const string& path, FGTileEntry *t,
906 ssgVertexArray *lights, const bool is_base)
911 bool result = obj.read_bin( path );
917 // cout << "fans size = " << obj.get_fans_v().size()
918 // << " fan_mats size = " << obj.get_fan_materials().size() << endl;
920 ssgBranch *object = new ssgBranch();
921 object->setName( (char *)path.c_str() );
923 if ( is_base && t != NULL ) {
924 // reference point (center offset/bounding sphere)
925 t->center = obj.get_gbs_center();
926 t->bounding_radius = obj.get_gbs_radius();
929 point_list nodes = obj.get_wgs84_nodes();
930 point_list normals = obj.get_normals();
931 point_list texcoords = obj.get_texcoords();
934 int_list vertex_index;
937 // generate triangles
938 string_list tri_materials = obj.get_tri_materials();
939 group_list tris_v = obj.get_tris_v();
940 group_list tris_tc = obj.get_tris_tc();
941 for ( i = 0; i < (int)tris_v.size(); ++i ) {
942 material = tri_materials[i];
943 vertex_index = tris_v[i];
944 tex_index = tris_tc[i];
945 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLES, material,
946 nodes, normals, texcoords,
947 vertex_index, tex_index,
950 object->addKid( leaf );
954 string_list strip_materials = obj.get_strip_materials();
955 group_list strips_v = obj.get_strips_v();
956 group_list strips_tc = obj.get_strips_tc();
957 for ( i = 0; i < (int)strips_v.size(); ++i ) {
958 material = strip_materials[i];
959 vertex_index = strips_v[i];
960 tex_index = strips_tc[i];
961 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, material,
962 nodes, normals, texcoords,
963 vertex_index, tex_index,
966 object->addKid( leaf );
970 string_list fan_materials = obj.get_fan_materials();
971 group_list fans_v = obj.get_fans_v();
972 group_list fans_tc = obj.get_fans_tc();
973 for ( i = 0; i < (int)fans_v.size(); ++i ) {
974 material = fan_materials[i];
975 vertex_index = fans_v[i];
976 tex_index = fans_tc[i];
977 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_FAN, material,
978 nodes, normals, texcoords,
979 vertex_index, tex_index,
982 object->addKid( leaf );
989 ssgBranch *gen_taxi_sign( const string path, const string content ) {
990 // for demo purposes we assume each element (letter) is 1x1 meter.
991 // Sign is placed 0.25 meters above the ground
993 ssgBranch *object = new ssgBranch();
994 object->setName( (char *)content.c_str() );
996 double offset = content.length() / 2.0;
998 for ( unsigned int i = 0; i < content.length(); ++i ) {
1001 char item = content[i];
1002 if ( item == '<' ) {
1003 material = "ArrowL.rgb";
1004 } else if ( item == '>' ) {
1005 material = "ArrowR.rgb";
1006 } else if ( item >= 'A' && item <= 'Z' ) {
1007 material = "Letter";
1010 } else if ( item >= 'a' && item <= 'z' ) {
1011 int tmp = item - 'a';
1017 cout << "Unknown taxi sign code = '" << item << "' !!!!" << endl;
1021 point_list nodes; nodes.clear();
1022 point_list normals; normals.clear();
1023 point_list texcoords; texcoords.clear();
1024 int_list vertex_index; vertex_index.clear();
1025 int_list tex_index; tex_index.clear();
1027 nodes.push_back( Point3D( -offset + i, 0, 0.25 ) );
1028 nodes.push_back( Point3D( -offset + i + 1, 0, 0.25 ) );
1029 nodes.push_back( Point3D( -offset + i, 0, 1.25 ) );
1030 nodes.push_back( Point3D( -offset + i + 1, 0, 1.25 ) );
1032 normals.push_back( Point3D( 0, -1, 0 ) );
1033 normals.push_back( Point3D( 0, -1, 0 ) );
1034 normals.push_back( Point3D( 0, -1, 0 ) );
1035 normals.push_back( Point3D( 0, -1, 0 ) );
1037 texcoords.push_back( Point3D( 0, 0, 0 ) );
1038 texcoords.push_back( Point3D( 1, 0, 0 ) );
1039 texcoords.push_back( Point3D( 0, 1, 0 ) );
1040 texcoords.push_back( Point3D( 1, 1, 0 ) );
1042 vertex_index.push_back( 0 );
1043 vertex_index.push_back( 1 );
1044 vertex_index.push_back( 2 );
1045 vertex_index.push_back( 3 );
1047 tex_index.push_back( 0 );
1048 tex_index.push_back( 1 );
1049 tex_index.push_back( 2 );
1050 tex_index.push_back( 3 );
1052 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, material,
1053 nodes, normals, texcoords,
1054 vertex_index, tex_index,
1057 object->addKid( leaf );
1064 ssgBranch *gen_runway_sign( const string path, const string name ) {
1065 // for demo purposes we assume each element (letter) is 1x1 meter.
1066 // Sign is placed 0.25 meters above the ground
1068 ssgBranch *object = new ssgBranch();
1069 object->setName( (char *)name.c_str() );
1071 double width = name.length() / 3.0;
1073 string material = name + ".rgb";
1075 point_list nodes; nodes.clear();
1076 point_list normals; normals.clear();
1077 point_list texcoords; texcoords.clear();
1078 int_list vertex_index; vertex_index.clear();
1079 int_list tex_index; tex_index.clear();
1081 nodes.push_back( Point3D( -width, 0, 0.25 ) );
1082 nodes.push_back( Point3D( width + 1, 0, 0.25 ) );
1083 nodes.push_back( Point3D( -width, 0, 1.25 ) );
1084 nodes.push_back( Point3D( width + 1, 0, 1.25 ) );
1086 normals.push_back( Point3D( 0, -1, 0 ) );
1087 normals.push_back( Point3D( 0, -1, 0 ) );
1088 normals.push_back( Point3D( 0, -1, 0 ) );
1089 normals.push_back( Point3D( 0, -1, 0 ) );
1091 texcoords.push_back( Point3D( 0, 0, 0 ) );
1092 texcoords.push_back( Point3D( 1, 0, 0 ) );
1093 texcoords.push_back( Point3D( 0, 1, 0 ) );
1094 texcoords.push_back( Point3D( 1, 1, 0 ) );
1096 vertex_index.push_back( 0 );
1097 vertex_index.push_back( 1 );
1098 vertex_index.push_back( 2 );
1099 vertex_index.push_back( 3 );
1101 tex_index.push_back( 0 );
1102 tex_index.push_back( 1 );
1103 tex_index.push_back( 2 );
1104 tex_index.push_back( 3 );
1106 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, material,
1107 nodes, normals, texcoords,
1108 vertex_index, tex_index,
1111 object->addKid( leaf );