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 static void random_pt_inside_tri( float *res,
246 float *n1, float *n2, float *n3 )
250 double a = sg_random();
251 double b = sg_random();
256 double c = 1 - a - b;
258 sgScaleVec3( p1, n1, a );
259 sgScaleVec3( p2, n2, b );
260 sgScaleVec3( p3, n3, c );
262 sgAddVec3( res, p1, p2 );
263 sgAddVec3( res, p3 );
267 static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
269 int num = leaf->getNumTriangles();
271 short int n1, n2, n3;
275 // generate a repeatable random seed
276 p1 = leaf->getVertex( 0 );
277 unsigned int seed = (unsigned int)p1[0];
280 for ( int i = 0; i < num; ++i ) {
281 leaf->getTriangle( i, &n1, &n2, &n3 );
282 p1 = leaf->getVertex(n1);
283 p2 = leaf->getVertex(n2);
284 p3 = leaf->getVertex(n3);
285 double area = sgTriArea( p1, p2, p3 );
286 double num = area / factor;
288 // generate a light point for each unit of area
289 while ( num > 1.0 ) {
290 random_pt_inside_tri( result, p1, p2, p3 );
291 lights->add( result );
294 // for partial units of area, use a zombie door method to
295 // create the proper random chance of a light being created
298 if ( sg_random() <= num ) {
299 // a zombie made it through our door
300 random_pt_inside_tri( result, p1, p2, p3 );
301 lights->add( result );
309 // Load an Ascii obj file
310 ssgBranch *fgAsciiObjLoad( const string& path, FGTileEntry *t,
311 ssgVertexArray *lights, const bool is_base)
313 FGNewMat *newmat = NULL;
317 // sgVec3 approx_normal;
318 // double normal[3], scale = 0.0;
319 // double x, y, z, xmax, xmin, ymax, ymin, zmax, zmin;
320 // GLfloat sgenparams[] = { 1.0, 0.0, 0.0, 0.0 };
321 // GLint display_list = 0;
323 bool in_faces = false;
324 int vncount, vtcount;
325 int n1 = 0, n2 = 0, n3 = 0;
327 // int last1 = 0, last2 = 0;
332 double scenery_version = 0.0;
333 double tex_width = 1000.0, tex_height = 1000.0;
334 bool shared_done = false;
335 int_list fan_vertices;
336 int_list fan_tex_coords;
338 ssgSimpleState *state = NULL;
339 sgVec3 *vtlist, *vnlist;
342 ssgBranch *tile = new ssgBranch () ;
344 tile -> setName ( (char *)path.c_str() ) ;
346 // Attempt to open "path.gz" or "path"
347 sg_gzifstream in( path );
348 if ( ! in.is_open() ) {
349 SG_LOG( SG_TERRAIN, SG_DEBUG, "Cannot open file: " << path );
350 SG_LOG( SG_TERRAIN, SG_DEBUG, "default to ocean tile: " << path );
355 shading = fgGetBool("/sim/rendering/shading");
363 t->bounding_radius = 0.0;
370 // ignore initial comments and blank lines. (priming the pump)
371 // in >> skipcomment;
378 while ( in.get(c) && c != '\0' ) {
381 while ( ! in.eof() ) {
384 #if defined( macintosh ) || defined( _MSC_VER )
390 if ( in.get( c ) && c == '#' ) {
391 // process a comment line
393 // getline( in, line );
394 // cout << "comment = " << line << endl;
398 if ( token == "Version" ) {
399 // read scenery versions number
400 in >> scenery_version;
401 // cout << "scenery_version = " << scenery_version << endl;
402 if ( scenery_version > 0.4 ) {
403 SG_LOG( SG_TERRAIN, SG_ALERT,
404 "\nYou are attempting to load a tile format that\n"
405 << "is newer than this version of flightgear can\n"
406 << "handle. You should upgrade your copy of\n"
407 << "FlightGear to the newest version. For\n"
408 << "details, please see:\n"
409 << "\n http://www.flightgear.org\n" );
412 } else if ( token == "gbs" ) {
413 // reference point (center offset)
415 in >> t->center >> t->bounding_radius;
419 in >> junk1 >> junk2;
422 // cout << "center = " << center
423 // << " radius = " << t->bounding_radius << endl;
424 } else if ( token == "bs" ) {
425 // reference point (center offset)
429 in >> junk1 >> junk2;
430 } else if ( token == "usemtl" ) {
431 // material property specification
433 // if first usemtl with shared_done = false, then set
434 // shared_done true and build the ssg shared lists
435 if ( ! shared_done ) {
437 if ( (int)nodes.size() != vncount ) {
438 SG_LOG( SG_TERRAIN, SG_ALERT,
439 "Tile has mismatched nodes = " << nodes.size()
440 << " and normals = " << vncount << " : "
446 vtlist = new sgVec3 [ nodes.size() ];
447 t->vec3_ptrs.push_back( vtlist );
448 vnlist = new sgVec3 [ vncount ];
449 t->vec3_ptrs.push_back( vnlist );
450 tclist = new sgVec2 [ vtcount ];
451 t->vec2_ptrs.push_back( tclist );
453 for ( i = 0; i < (int)nodes.size(); ++i ) {
454 sgSetVec3( vtlist[i],
455 nodes[i][0], nodes[i][1], nodes[i][2] );
457 for ( i = 0; i < vncount; ++i ) {
458 sgSetVec3( vnlist[i],
463 for ( i = 0; i < vtcount; ++i ) {
464 sgSetVec2( tclist[i],
470 // display_list = xglGenLists(1);
471 // xglNewList(display_list, GL_COMPILE);
472 // printf("xglGenLists(); xglNewList();\n");
475 // scan the material line
478 // find this material in the properties list
480 newmat = material_lib.find( material );
481 if ( newmat == NULL ) {
482 // see if this is an on the fly texture
484 int pos = file.rfind( "/" );
485 file = file.substr( 0, pos );
486 cout << "current file = " << file << endl;
489 cout << "current file = " << file << endl;
490 if ( ! material_lib.add_item( file ) ) {
491 SG_LOG( SG_TERRAIN, SG_ALERT,
492 "Ack! unknown usemtl name = " << material
495 // locate our newly created material
496 newmat = material_lib.find( material );
497 if ( newmat == NULL ) {
498 SG_LOG( SG_TERRAIN, SG_ALERT,
499 "Ack! bad on the fly materia create = "
500 << material << " in " << path );
505 if ( newmat != NULL ) {
506 // set the texture width and height values for this
508 tex_width = newmat->get_xsize();
509 tex_height = newmat->get_ysize();
510 state = newmat->get_state();
511 coverage = newmat->get_light_coverage();
512 // cout << "(w) = " << tex_width << " (h) = "
513 // << tex_width << endl;
518 // unknown comment, just gobble the input until the
528 // cout << "token = " << token << endl;
530 if ( token == "vn" ) {
532 if ( vncount < FG_MAX_NODES ) {
533 in >> normals[vncount][0]
534 >> normals[vncount][1]
535 >> normals[vncount][2];
538 SG_LOG( SG_TERRAIN, SG_ALERT,
539 "Read too many vertex normals in " << path
540 << " ... dying :-(" );
543 } else if ( token == "vt" ) {
544 // vertex texture coordinate
545 if ( vtcount < FG_MAX_NODES*3 ) {
546 in >> tex_coords[vtcount][0]
547 >> tex_coords[vtcount][1];
550 SG_LOG( SG_TERRAIN, SG_ALERT,
551 "Read too many vertex texture coords in " << path
556 } else if ( token == "v" ) {
558 if ( t->ncount < FG_MAX_NODES ) {
559 /* in >> nodes[t->ncount][0]
560 >> nodes[t->ncount][1]
561 >> nodes[t->ncount][2]; */
563 nodes.push_back(node);
568 SG_LOG( SG_TERRAIN, SG_ALERT,
569 "Read too many nodes in " << path
570 << " ... dying :-(");
573 } else if ( (token == "tf") || (token == "ts") || (token == "f") ) {
574 // triangle fan, strip, or individual face
575 // SG_LOG( SG_TERRAIN, SG_INFO, "new fan or strip");
577 fan_vertices.clear();
578 fan_tex_coords.clear();
581 // xglBegin(GL_TRIANGLE_FAN);
584 fan_vertices.push_back( n1 );
585 // xglNormal3dv(normals[n1]);
586 if ( in.get( c ) && c == '/' ) {
588 fan_tex_coords.push_back( tex );
589 if ( scenery_version >= 0.4 ) {
590 if ( tex_width > 0 ) {
591 tclist[tex][0] *= (1000.0 / tex_width);
593 if ( tex_height > 0 ) {
594 tclist[tex][1] *= (1000.0 / tex_height);
597 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
598 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
601 pp = local_calc_tex_coords(nodes[n1], center);
603 // xglTexCoord2f(pp.x(), pp.y());
604 // xglVertex3dv(nodes[n1].get_n());
607 fan_vertices.push_back( n2 );
608 // xglNormal3dv(normals[n2]);
609 if ( in.get( c ) && c == '/' ) {
611 fan_tex_coords.push_back( tex );
612 if ( scenery_version >= 0.4 ) {
613 if ( tex_width > 0 ) {
614 tclist[tex][0] *= (1000.0 / tex_width);
616 if ( tex_height > 0 ) {
617 tclist[tex][1] *= (1000.0 / tex_height);
620 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
621 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
624 pp = local_calc_tex_coords(nodes[n2], center);
626 // xglTexCoord2f(pp.x(), pp.y());
627 // xglVertex3dv(nodes[n2].get_n());
629 // read all subsequent numbers until next thing isn't a number
631 #if defined( macintosh ) || defined( _MSC_VER )
640 if ( ! isdigit(c) || in.eof() ) {
645 fan_vertices.push_back( n3 );
646 // cout << " triangle = "
647 // << n1 << "," << n2 << "," << n3
649 // xglNormal3dv(normals[n3]);
650 if ( in.get( c ) && c == '/' ) {
652 fan_tex_coords.push_back( tex );
653 if ( scenery_version >= 0.4 ) {
654 if ( tex_width > 0 ) {
655 tclist[tex][0] *= (1000.0 / tex_width);
657 if ( tex_height > 0 ) {
658 tclist[tex][1] *= (1000.0 / tex_height);
661 pp.setx( tex_coords[tex][0] * (1000.0 / tex_width) );
662 pp.sety( tex_coords[tex][1] * (1000.0 / tex_height) );
665 pp = local_calc_tex_coords(nodes[n3], center);
667 // xglTexCoord2f(pp.x(), pp.y());
668 // xglVertex3dv(nodes[n3].get_n());
670 if ( (token == "tf") || (token == "f") ) {
683 // build the ssg entity
684 int size = (int)fan_vertices.size();
685 ssgVertexArray *vl = new ssgVertexArray( size );
686 ssgNormalArray *nl = new ssgNormalArray( size );
687 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
688 ssgColourArray *cl = new ssgColourArray( 1 );
691 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
696 for ( i = 0; i < size; ++i ) {
697 sgCopyVec3( tmp3, vtlist[ fan_vertices[i] ] );
700 sgCopyVec3( tmp3, vnlist[ fan_vertices[i] ] );
703 sgCopyVec2( tmp2, tclist[ fan_tex_coords[i] ] );
707 ssgLeaf *leaf = NULL;
708 if ( token == "tf" ) {
711 new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
712 } else if ( token == "ts" ) {
715 new ssgVtxTable ( GL_TRIANGLE_STRIP, vl, nl, tl, cl );
716 } else if ( token == "f" ) {
719 new ssgVtxTable ( GL_TRIANGLES, vl, nl, tl, cl );
721 // leaf->makeDList();
722 leaf->setState( state );
724 tile->addKid( leaf );
727 if ( coverage > 0.0 ) {
728 if ( coverage < 10000.0 ) {
729 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
730 << coverage << ", pushing up to 10000");
733 gen_random_surface_points(leaf, lights, coverage);
737 SG_LOG( SG_TERRAIN, SG_WARN, "Unknown token in "
738 << path << " = " << token );
741 // eat white space before start of while loop so if we are
742 // done with useful input it is noticed before hand.
743 #if defined( macintosh ) || defined( _MSC_VER )
756 SG_LOG( SG_TERRAIN, SG_DEBUG,
757 "Loaded " << path << " in "
758 << stopwatch.elapsedSeconds() << " seconds" );
764 ssgLeaf *gen_leaf( const string& path,
765 const GLenum ty, const string& material,
766 const point_list& nodes, const point_list& normals,
767 const point_list& texcoords,
768 const int_list node_index,
769 const int_list& tex_index,
770 const bool calc_lights, ssgVertexArray *lights )
772 double tex_width = 1000.0, tex_height = 1000.0;
773 ssgSimpleState *state = NULL;
776 FGNewMat *newmat = material_lib.find( material );
777 if ( newmat == NULL ) {
778 // see if this is an on the fly texture
780 int pos = file.rfind( "/" );
781 file = file.substr( 0, pos );
782 cout << "current file = " << file << endl;
785 cout << "current file = " << file << endl;
786 if ( ! material_lib.add_item( file ) ) {
787 SG_LOG( SG_TERRAIN, SG_ALERT,
788 "Ack! unknown usemtl name = " << material
791 // locate our newly created material
792 newmat = material_lib.find( material );
793 if ( newmat == NULL ) {
794 SG_LOG( SG_TERRAIN, SG_ALERT,
795 "Ack! bad on the fly material create = "
796 << material << " in " << path );
801 if ( newmat != NULL ) {
802 // set the texture width and height values for this
804 tex_width = newmat->get_xsize();
805 tex_height = newmat->get_ysize();
806 state = newmat->get_state();
807 coverage = newmat->get_light_coverage();
808 // cout << "(w) = " << tex_width << " (h) = "
809 // << tex_width << endl;
814 // cout << "before list allocs" << endl;
816 int size = node_index.size();
819 SG_LOG( SG_TERRAIN, SG_ALERT, "Woh! list size < 1" );
823 // cout << "before vl, size = " << size << endl;
824 ssgVertexArray *vl = new ssgVertexArray( size );
825 // cout << "before nl" << endl;
826 ssgNormalArray *nl = new ssgNormalArray( size );
827 // cout << "before tl" << endl;
828 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
829 // cout << "before cl" << endl;
830 ssgColourArray *cl = new ssgColourArray( 1 );
833 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
839 for ( i = 0; i < size; ++i ) {
840 Point3D node = nodes[ node_index[i] ];
841 sgSetVec3( tmp3, node[0], node[1], node[2] );
844 Point3D normal = normals[ node_index[i] ];
845 sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
848 Point3D texcoord = texcoords[ tex_index[i] ];
849 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
850 if ( tex_width > 0 ) {
851 tmp2[0] *= (1000.0 / tex_width);
853 if ( tex_height > 0 ) {
854 tmp2[1] *= (1000.0 / tex_height);
859 // cout << "before leaf create" << endl;
860 ssgLeaf *leaf = new ssgVtxTable ( ty, vl, nl, tl, cl );
861 // cout << "after leaf create" << endl;
863 // lookup the state record
864 // cout << "looking up material = " << endl;
865 // cout << material << endl;
866 // cout << "'" << endl;
868 leaf->setState( state );
871 if ( coverage > 0.0 ) {
872 if ( coverage < 10000.0 ) {
873 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
874 << coverage << ", pushing up to 10000");
877 gen_random_surface_points(leaf, lights, coverage);
885 // Load an Binary obj file
886 ssgBranch *fgBinObjLoad( const string& path, FGTileEntry *t,
887 ssgVertexArray *lights, const bool is_base)
892 bool result = obj.read_bin( path );
898 // cout << "fans size = " << obj.get_fans_v().size()
899 // << " fan_mats size = " << obj.get_fan_materials().size() << endl;
901 ssgBranch *object = new ssgBranch();
902 object->setName( (char *)path.c_str() );
904 if ( is_base && t != NULL ) {
905 // reference point (center offset/bounding sphere)
906 t->center = obj.get_gbs_center();
907 t->bounding_radius = obj.get_gbs_radius();
910 point_list nodes = obj.get_wgs84_nodes();
911 point_list normals = obj.get_normals();
912 point_list texcoords = obj.get_texcoords();
915 int_list vertex_index;
918 // generate triangles
919 string_list tri_materials = obj.get_tri_materials();
920 group_list tris_v = obj.get_tris_v();
921 group_list tris_tc = obj.get_tris_tc();
922 for ( i = 0; i < (int)tris_v.size(); ++i ) {
923 material = tri_materials[i];
924 vertex_index = tris_v[i];
925 tex_index = tris_tc[i];
926 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLES, material,
927 nodes, normals, texcoords,
928 vertex_index, tex_index,
931 object->addKid( leaf );
935 string_list strip_materials = obj.get_strip_materials();
936 group_list strips_v = obj.get_strips_v();
937 group_list strips_tc = obj.get_strips_tc();
938 for ( i = 0; i < (int)strips_v.size(); ++i ) {
939 material = strip_materials[i];
940 vertex_index = strips_v[i];
941 tex_index = strips_tc[i];
942 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, material,
943 nodes, normals, texcoords,
944 vertex_index, tex_index,
947 object->addKid( leaf );
951 string_list fan_materials = obj.get_fan_materials();
952 group_list fans_v = obj.get_fans_v();
953 group_list fans_tc = obj.get_fans_tc();
954 for ( i = 0; i < (int)fans_v.size(); ++i ) {
955 material = fan_materials[i];
956 vertex_index = fans_v[i];
957 tex_index = fans_tc[i];
958 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_FAN, material,
959 nodes, normals, texcoords,
960 vertex_index, tex_index,
963 object->addKid( leaf );