1 // obj.hxx -- routines to handle loading scenery and building the plib
4 // Written by Curtis Olson, started October 1997.
6 // Copyright (C) 1997 Curtis L. Olson - curt@infoplane.com
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License as
10 // published by the Free Software Foundation; either version 2 of the
11 // License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful, but
14 // WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 #ifdef SG_MATH_EXCEPTION_CLASH
36 #include <simgear/compiler.h>
37 #include <simgear/sg_inlines.h>
38 #include <simgear/io/sg_binobj.hxx>
42 #include <vector> // STL
43 #include <ctype.h> // isdigit()
45 #include <simgear/constants.h>
46 #include <simgear/debug/logstream.hxx>
47 #include <simgear/math/point3d.hxx>
48 #include <simgear/math/polar3d.hxx>
49 #include <simgear/math/sg_geodesy.hxx>
50 #include <simgear/math/sg_random.h>
51 #include <simgear/math/vector.hxx>
52 #include <simgear/misc/sgstream.hxx>
53 #include <simgear/misc/stopwatch.hxx>
54 #include <simgear/misc/texcoord.hxx>
55 #include <simgear/scene/material/mat.hxx>
56 #include <simgear/scene/material/matlib.hxx>
58 #include <Main/globals.hxx>
59 #include <Main/fg_props.hxx>
60 #include <Time/light.hxx>
61 #include <Scenery/tileentry.hxx>
63 #include "pt_lights.hxx"
70 typedef vector < int > int_list;
71 typedef int_list::iterator int_list_iterator;
72 typedef int_list::const_iterator int_point_list_iterator;
75 // not used because plib branches don't honor call backs.
77 runway_lights_pretrav (ssgEntity * e, int mask)
79 // Turn on lights only at night
80 float sun_angle = cur_light_params.sun_angle * SGD_RADIANS_TO_DEGREES;
81 return int((sun_angle > 85.0) ||
82 (fgGetDouble("/environment/visibility-m") < 5000.0));
86 // Generate an ocean tile
87 bool fgGenTile( const string& path, SGBucket b,
89 double *bounding_radius,
94 ssgSimpleState *state = NULL;
96 geometry -> setName ( (char *)path.c_str() ) ;
98 double tex_width = 1000.0;
101 // find Ocean material in the properties list
102 newmat = material_lib.find( "Ocean" );
103 if ( newmat != NULL ) {
104 // set the texture width and height values for this
106 tex_width = newmat->get_xsize();
107 // tex_height = newmat->get_ysize();
110 state = newmat->get_state();
112 SG_LOG( SG_TERRAIN, SG_ALERT,
113 "Ack! unknown usemtl name = " << "Ocean"
117 // Calculate center point
118 double clon = b.get_center_lon();
119 double clat = b.get_center_lat();
120 double height = b.get_height();
121 double width = b.get_width();
123 *center = sgGeodToCart( Point3D(clon*SGD_DEGREES_TO_RADIANS,
124 clat*SGD_DEGREES_TO_RADIANS,
126 // cout << "center = " << center << endl;;
128 // Caculate corner vertices
130 geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
131 geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
132 geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
133 geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
137 for ( i = 0; i < 4; ++i ) {
138 rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
139 geod[i].y() * SGD_DEGREES_TO_RADIANS,
143 Point3D cart[4], rel[4];
144 for ( i = 0; i < 4; ++i ) {
145 cart[i] = sgGeodToCart(rad[i]);
146 rel[i] = cart[i] - *center;
147 // cout << "corner " << i << " = " << cart[i] << endl;
150 // Calculate bounding radius
151 *bounding_radius = center->distance3D( cart[0] );
152 // cout << "bounding radius = " << t->bounding_radius << endl;
156 for ( i = 0; i < 4; ++i ) {
157 double length = cart[i].distance3D( Point3D(0.0) );
158 normals[i] = cart[i] / length;
159 // cout << "normal = " << normals[i] << endl;
162 // Calculate texture coordinates
163 point_list geod_nodes;
165 geod_nodes.reserve(4);
168 rectangle.reserve(4);
169 for ( i = 0; i < 4; ++i ) {
170 geod_nodes.push_back( geod[i] );
171 rectangle.push_back( i );
173 point_list texs = calc_tex_coords( b, geod_nodes, rectangle,
174 1000.0 / tex_width );
176 // Allocate ssg structure
177 ssgVertexArray *vl = new ssgVertexArray( 4 );
178 ssgNormalArray *nl = new ssgNormalArray( 4 );
179 ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
180 ssgColourArray *cl = new ssgColourArray( 1 );
183 sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
186 // sgVec3 *vtlist = new sgVec3 [ 4 ];
187 // t->vec3_ptrs.push_back( vtlist );
188 // sgVec3 *vnlist = new sgVec3 [ 4 ];
189 // t->vec3_ptrs.push_back( vnlist );
190 // sgVec2 *tclist = new sgVec2 [ 4 ];
191 // t->vec2_ptrs.push_back( tclist );
195 for ( i = 0; i < 4; ++i ) {
197 rel[i].x(), rel[i].y(), rel[i].z() );
201 normals[i].x(), normals[i].y(), normals[i].z() );
204 sgSetVec2( tmp2, texs[i].x(), texs[i].y());
209 new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
211 leaf->setState( state );
213 geometry->addKid( leaf );
219 static void random_pt_inside_tri( float *res,
220 float *n1, float *n2, float *n3 )
222 double a = sg_random();
223 double b = sg_random();
228 double c = 1 - a - b;
230 res[0] = n1[0]*a + n2[0]*b + n3[0]*c;
231 res[1] = n1[1]*a + n2[1]*b + n3[1]*c;
232 res[2] = n1[2]*a + n2[2]*b + n3[2]*c;
236 static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
238 int tris = leaf->getNumTriangles();
240 short int n1, n2, n3;
244 // generate a repeatable random seed
245 p1 = leaf->getVertex( 0 );
246 unsigned int seed = (unsigned int)(fabs(p1[0]*100));
249 for ( int i = 0; i < tris; ++i ) {
250 leaf->getTriangle( i, &n1, &n2, &n3 );
251 p1 = leaf->getVertex(n1);
252 p2 = leaf->getVertex(n2);
253 p3 = leaf->getVertex(n3);
254 double area = sgTriArea( p1, p2, p3 );
255 double num = area / factor;
257 // generate a light point for each unit of area
258 while ( num > 1.0 ) {
259 random_pt_inside_tri( result, p1, p2, p3 );
260 lights->add( result );
263 // for partial units of area, use a zombie door method to
264 // create the proper random chance of a light being created
267 if ( sg_random() <= num ) {
268 // a zombie made it through our door
269 random_pt_inside_tri( result, p1, p2, p3 );
270 lights->add( result );
279 * User data for populating leaves when they come in range.
281 class LeafUserData : public ssgBase
293 void setup_triangle( int i );
298 * User data for populating triangles when they come in range.
300 class TriUserData : public ssgBase
309 SGMaterial::ObjectGroup * object_group;
311 LeafUserData * leafData;
314 void fill_in_triangle();
315 void add_object_to_triangle(SGMaterial::Object * object);
316 void makeWorldMatrix (sgMat4 ROT, double hdg_deg );
321 * Fill in a triangle with randomly-placed objects.
323 * This method is invoked by a callback when the triangle is in range
324 * but not yet populated.
328 void TriUserData::fill_in_triangle ()
330 // generate a repeatable random seed
333 int nObjects = object_group->get_object_count();
335 for (int i = 0; i < nObjects; i++) {
336 SGMaterial::Object * object = object_group->get_object(i);
337 double num = area / object->get_coverage_m2();
339 // place an object each unit of area
340 while ( num > 1.0 ) {
341 add_object_to_triangle(object);
344 // for partial units of area, use a zombie door method to
345 // create the proper random chance of an object being created
348 if ( sg_random() <= num ) {
349 // a zombie made it through our door
350 add_object_to_triangle(object);
356 void TriUserData::add_object_to_triangle (SGMaterial::Object * object)
358 // Set up the random heading if required.
360 if (object->get_heading_type() == SGMaterial::Object::HEADING_RANDOM)
361 hdg_deg = sg_random() * 360;
364 makeWorldMatrix(mat, hdg_deg);
366 ssgTransform * pos = new ssgTransform;
367 pos->setTransform(mat);
368 pos->addKid( object->get_random_model( globals->get_model_loader(),
369 globals->get_fg_root(),
370 globals->get_props(),
371 globals->get_sim_time_sec() )
376 void TriUserData::makeWorldMatrix (sgMat4 mat, double hdg_deg )
379 mat[0][0] = leafData->sin_lat * leafData->cos_lon;
380 mat[0][1] = leafData->sin_lat * leafData->sin_lon;
381 mat[0][2] = -leafData->cos_lat;
384 mat[1][0] = -leafData->sin_lon;
385 mat[1][1] = leafData->cos_lon;
389 float sin_hdg = sin( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
390 float cos_hdg = cos( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
391 mat[0][0] = cos_hdg * leafData->sin_lat * leafData->cos_lon - sin_hdg * leafData->sin_lon;
392 mat[0][1] = cos_hdg * leafData->sin_lat * leafData->sin_lon + sin_hdg * leafData->cos_lon;
393 mat[0][2] = -cos_hdg * leafData->cos_lat;
396 mat[1][0] = -sin_hdg * leafData->sin_lat * leafData->cos_lon - cos_hdg * leafData->sin_lon;
397 mat[1][1] = -sin_hdg * leafData->sin_lat * leafData->sin_lon + cos_hdg * leafData->cos_lon;
398 mat[1][2] = sin_hdg * leafData->cos_lat;
402 mat[2][0] = leafData->cos_lat * leafData->cos_lon;
403 mat[2][1] = leafData->cos_lat * leafData->sin_lon;
404 mat[2][2] = leafData->sin_lat;
407 // translate to random point in triangle
409 random_pt_inside_tri(result, p1, p2, p3);
410 sgSubVec3(mat[3], result, center);
416 * SSG callback for an in-range triangle of randomly-placed objects.
418 * This pretraversal callback is attached to a branch that is traversed
419 * only when a triangle is in range. If the triangle is not currently
420 * populated with randomly-placed objects, this callback will populate
423 * @param entity The entity to which the callback is attached (not used).
424 * @param mask The entity's traversal mask (not used).
425 * @return Always 1, to allow traversal and culling to continue.
428 tri_in_range_callback (ssgEntity * entity, int mask)
430 TriUserData * data = (TriUserData *)entity->getUserData();
431 if (!data->is_filled_in) {
432 data->fill_in_triangle();
433 data->is_filled_in = true;
440 * SSG callback for an out-of-range triangle of randomly-placed objects.
442 * This pretraversal callback is attached to a branch that is traversed
443 * only when a triangle is out of range. If the triangle is currently
444 * populated with randomly-placed objects, the objects will be removed.
447 * @param entity The entity to which the callback is attached (not used).
448 * @param mask The entity's traversal mask (not used).
449 * @return Always 0, to prevent any further traversal or culling.
452 tri_out_of_range_callback (ssgEntity * entity, int mask)
454 TriUserData * data = (TriUserData *)entity->getUserData();
455 if (data->is_filled_in) {
456 data->branch->removeAllKids();
457 data->is_filled_in = false;
464 * ssgEntity with a dummy bounding sphere, to fool culling.
466 * This forces the in-range and out-of-range branches to be visited
467 * when appropriate, even if they have no children. It's ugly, but
468 * it works and seems fairly efficient (since branches can still
469 * be culled when they're out of the view frustum).
471 class DummyBSphereEntity : public ssgBranch
474 DummyBSphereEntity (float radius)
476 bsphere.setCenter(0, 0, 0);
477 bsphere.setRadius(radius);
479 virtual ~DummyBSphereEntity () {}
480 virtual void recalcBSphere () { bsphere_is_invalid = false; }
485 * Calculate the bounding radius of a triangle from its center.
487 * @param center The triangle center.
488 * @param p1 The first point in the triangle.
489 * @param p2 The second point in the triangle.
490 * @param p3 The third point in the triangle.
491 * @return The greatest distance any point lies from the center.
494 get_bounding_radius( sgVec3 center, float *p1, float *p2, float *p3)
496 return sqrt( SG_MAX3( sgDistanceSquaredVec3(center, p1),
497 sgDistanceSquaredVec3(center, p2),
498 sgDistanceSquaredVec3(center, p3) ) );
503 * Set up a triangle for randomly-placed objects.
505 * No objects will be added unless the triangle comes into range.
509 void LeafUserData::setup_triangle (int i )
512 leaf->getTriangle(i, &n1, &n2, &n3);
514 float * p1 = leaf->getVertex(n1);
515 float * p2 = leaf->getVertex(n2);
516 float * p3 = leaf->getVertex(n3);
518 // Set up a single center point for LOD
521 (p1[0] + p2[0] + p3[0]) / 3.0,
522 (p1[1] + p2[1] + p3[1]) / 3.0,
523 (p1[2] + p2[2] + p3[2]) / 3.0);
524 double area = sgTriArea(p1, p2, p3);
526 // maximum radius of an object from center.
527 double bounding_radius = get_bounding_radius(center, p1, p2, p3);
529 // Set up a transformation to the center
530 // point, so that everything else can
531 // be specified relative to it.
532 ssgTransform * location = new ssgTransform;
534 sgMakeTransMat4(TRANS, center);
535 location->setTransform(TRANS);
536 branch->addKid(location);
538 // Iterate through all the object types.
539 int num_groups = mat->get_object_group_count();
540 for (int j = 0; j < num_groups; j++) {
541 // Look up the random object.
542 SGMaterial::ObjectGroup * group = mat->get_object_group(j);
544 // Set up the range selector for the entire
545 // triangle; note that we use the object
546 // range plus the bounding radius here, to
547 // allow for objects far from the center.
548 float ranges[] = { 0,
549 group->get_range_m() + bounding_radius,
551 ssgRangeSelector * lod = new ssgRangeSelector;
552 lod->setRanges(ranges, 3);
553 location->addKid(lod);
555 // Create the in-range and out-of-range
557 ssgBranch * in_range = new ssgBranch;
558 ssgBranch * out_of_range = new ssgBranch;
560 // Set up the user data for if/when
561 // the random objects in this triangle
563 TriUserData * data = new TriUserData;
564 data->is_filled_in = false;
568 sgCopyVec3 (data->center, center);
570 data->object_group = group;
571 data->branch = in_range;
572 data->leafData = this;
573 data->seed = (unsigned int)(p1[0] * j);
575 // Set up the in-range node.
576 in_range->setUserData(data);
577 in_range->setTravCallback(SSG_CALLBACK_PRETRAV,
578 tri_in_range_callback);
579 lod->addKid(in_range);
581 // Set up the out-of-range node.
582 out_of_range->setUserData(data);
583 out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
584 tri_out_of_range_callback);
585 out_of_range->addKid(new DummyBSphereEntity(bounding_radius));
586 lod->addKid(out_of_range);
591 * SSG callback for an in-range leaf of randomly-placed objects.
593 * This pretraversal callback is attached to a branch that is
594 * traversed only when a leaf is in range. If the leaf is not
595 * currently prepared to be populated with randomly-placed objects,
596 * this callback will prepare it (actual population is handled by
597 * the tri_in_range_callback for individual triangles).
599 * @param entity The entity to which the callback is attached (not used).
600 * @param mask The entity's traversal mask (not used).
601 * @return Always 1, to allow traversal and culling to continue.
604 leaf_in_range_callback (ssgEntity * entity, int mask)
606 LeafUserData * data = (LeafUserData *)entity->getUserData();
608 if (!data->is_filled_in) {
609 // Iterate through all the triangles
610 // and populate them.
611 int num_tris = data->leaf->getNumTriangles();
612 for ( int i = 0; i < num_tris; ++i ) {
613 data->setup_triangle(i);
615 data->is_filled_in = true;
622 * SSG callback for an out-of-range leaf of randomly-placed objects.
624 * This pretraversal callback is attached to a branch that is
625 * traversed only when a leaf is out of range. If the leaf is
626 * currently prepared to be populated with randomly-placed objects (or
627 * is actually populated), the objects will be removed.
629 * @param entity The entity to which the callback is attached (not used).
630 * @param mask The entity's traversal mask (not used).
631 * @return Always 0, to prevent any further traversal or culling.
634 leaf_out_of_range_callback (ssgEntity * entity, int mask)
636 LeafUserData * data = (LeafUserData *)entity->getUserData();
637 if (data->is_filled_in) {
638 data->branch->removeAllKids();
639 data->is_filled_in = false;
646 * Randomly place objects on a surface.
648 * The leaf node provides the geometry of the surface, while the
649 * material provides the objects and placement density. Latitude
650 * and longitude are required so that the objects can be rotated
651 * to the world-up vector. This function does not actually add
652 * any objects; instead, it attaches an ssgRangeSelector to the
653 * branch with callbacks to generate the objects when needed.
655 * @param leaf The surface where the objects should be placed.
656 * @param branch The branch that will hold the randomly-placed objects.
657 * @param center The center of the leaf in FlightGear coordinates.
658 * @param material_name The name of the surface's material.
661 gen_random_surface_objects (ssgLeaf *leaf,
664 const string &material_name)
666 // If the surface has no triangles, return
668 int num_tris = leaf->getNumTriangles();
672 // Get the material for this surface.
673 SGMaterial * mat = material_lib.find(material_name);
675 SG_LOG(SG_INPUT, SG_ALERT, "Unknown material " << material_name);
679 // If the material has no randomly-placed
680 // objects, return now.
681 if (mat->get_object_group_count() < 1)
684 // Calculate the geodetic centre of
685 // the tile, for aligning automatic
687 double lon_deg, lat_rad, lat_deg, alt_m, sl_radius_m;
688 Point3D geoc = sgCartToPolar3d(*center);
689 lon_deg = geoc.lon() * SGD_RADIANS_TO_DEGREES;
690 sgGeocToGeod(geoc.lat(), geoc.radius(),
691 &lat_rad, &alt_m, &sl_radius_m);
692 lat_deg = lat_rad * SGD_RADIANS_TO_DEGREES;
695 // max random object range: 20000m
696 float ranges[] = { 0, 20000, 1000000 };
697 ssgRangeSelector * lod = new ssgRangeSelector;
698 lod->setRanges(ranges, 3);
701 // Create the in-range and out-of-range
703 ssgBranch * in_range = new ssgBranch;
704 ssgBranch * out_of_range = new ssgBranch;
705 lod->addKid(in_range);
706 lod->addKid(out_of_range);
708 LeafUserData * data = new LeafUserData;
709 data->is_filled_in = false;
712 data->branch = in_range;
713 data->sin_lat = sin(lat_deg * SGD_DEGREES_TO_RADIANS);
714 data->cos_lat = cos(lat_deg * SGD_DEGREES_TO_RADIANS);
715 data->sin_lon = sin(lon_deg * SGD_DEGREES_TO_RADIANS);
716 data->cos_lon = cos(lon_deg * SGD_DEGREES_TO_RADIANS);
718 in_range->setUserData(data);
719 in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
720 out_of_range->setUserData(data);
721 out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
722 leaf_out_of_range_callback);
724 ->addKid(new DummyBSphereEntity(leaf->getBSphere()->getRadius()));
729 ////////////////////////////////////////////////////////////////////////
731 ////////////////////////////////////////////////////////////////////////
733 ssgLeaf *gen_leaf( const string& path,
734 const GLenum ty, const string& material,
735 const point_list& nodes, const point_list& normals,
736 const point_list& texcoords,
737 const int_list& node_index,
738 const int_list& normal_index,
739 const int_list& tex_index,
740 const bool calc_lights, ssgVertexArray *lights )
742 double tex_width = 1000.0, tex_height = 1000.0;
743 ssgSimpleState *state = NULL;
746 SGMaterial *newmat = material_lib.find( material );
747 if ( newmat == NULL ) {
748 // see if this is an on the fly texture
750 string::size_type pos = file.rfind( "/" );
751 file = file.substr( 0, pos );
752 // cout << "current file = " << file << endl;
755 // cout << "current file = " << file << endl;
756 if ( ! material_lib.add_item( file ) ) {
757 SG_LOG( SG_TERRAIN, SG_ALERT,
758 "Ack! unknown usemtl name = " << material
761 // locate our newly created material
762 newmat = material_lib.find( material );
763 if ( newmat == NULL ) {
764 SG_LOG( SG_TERRAIN, SG_ALERT,
765 "Ack! bad on the fly material create = "
766 << material << " in " << path );
771 if ( newmat != NULL ) {
772 // set the texture width and height values for this
774 tex_width = newmat->get_xsize();
775 tex_height = newmat->get_ysize();
776 state = newmat->get_state();
777 coverage = newmat->get_light_coverage();
778 // cout << "(w) = " << tex_width << " (h) = "
779 // << tex_width << endl;
790 int size = node_index.size();
792 SG_LOG( SG_TERRAIN, SG_ALERT, "Woh! node list size < 1" );
795 ssgVertexArray *vl = new ssgVertexArray( size );
797 for ( i = 0; i < size; ++i ) {
798 node = nodes[ node_index[i] ];
799 sgSetVec3( tmp3, node[0], node[1], node[2] );
805 ssgNormalArray *nl = new ssgNormalArray( size );
806 if ( normal_index.size() ) {
807 // object file specifies normal indices (i.e. normal indices
809 for ( i = 0; i < size; ++i ) {
810 normal = normals[ normal_index[i] ];
811 sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
815 // use implied normal indices. normal index = vertex index.
816 for ( i = 0; i < size; ++i ) {
817 normal = normals[ node_index[i] ];
818 sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
824 ssgColourArray *cl = new ssgColourArray( 1 );
825 sgSetVec4( tmp4, 1.0, 1.0, 1.0, 1.0 );
828 // texture coordinates
829 size = tex_index.size();
831 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
833 texcoord = texcoords[ tex_index[0] ];
834 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
835 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
836 if ( tex_width > 0 ) {
837 tmp2[0] *= (1000.0 / tex_width);
839 if ( tex_height > 0 ) {
840 tmp2[1] *= (1000.0 / tex_height);
843 } else if ( size > 1 ) {
844 for ( i = 0; i < size; ++i ) {
845 texcoord = texcoords[ tex_index[i] ];
846 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
847 if ( tex_width > 0 ) {
848 tmp2[0] *= (1000.0 / tex_width);
850 if ( tex_height > 0 ) {
851 tmp2[1] *= (1000.0 / tex_height);
857 ssgLeaf *leaf = new ssgVtxTable ( ty, vl, nl, tl, cl );
859 // lookup the state record
861 leaf->setState( state );
864 if ( coverage > 0.0 ) {
865 if ( coverage < 10000.0 ) {
866 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
867 << coverage << ", pushing up to 10000");
870 gen_random_surface_points(leaf, lights, coverage);
878 // Load an Binary obj file
879 bool fgBinObjLoad( const string& path, const bool is_base,
881 double *bounding_radius,
883 ssgBranch* rwy_lights,
884 ssgBranch* taxi_lights,
885 ssgVertexArray *ground_lights )
888 bool use_random_objects =
889 fgGetBool("/sim/rendering/random-objects", true);
891 if ( ! obj.read_bin( path ) ) {
895 geometry->setName( (char *)path.c_str() );
897 // reference point (center offset/bounding sphere)
898 *center = obj.get_gbs_center();
899 *bounding_radius = obj.get_gbs_radius();
901 point_list const& nodes = obj.get_wgs84_nodes();
902 // point_list const& colors = obj.get_colors();
903 point_list const& normals = obj.get_normals();
904 point_list const& texcoords = obj.get_texcoords();
909 group_list::size_type i;
912 string_list const& pt_materials = obj.get_pt_materials();
913 group_list const& pts_v = obj.get_pts_v();
914 group_list const& pts_n = obj.get_pts_n();
915 for ( i = 0; i < pts_v.size(); ++i ) {
916 // cout << "pts_v.size() = " << pts_v.size() << endl;
917 if ( pt_materials[i].substr(0, 3) == "RWY" ) {
919 sgSetVec3( up, center->x(), center->y(), center->z() );
920 // returns a transform -> lod -> leaf structure
921 ssgBranch *branch = gen_directional_lights( nodes, normals,
925 // branches don't honor callbacks as far as I know so I'm
926 // commenting this out to avoid a plib runtime warning.
927 branch->setTravCallback( SSG_CALLBACK_PRETRAV,
928 runway_lights_pretrav );
929 if ( pt_materials[i].substr(0, 16) == "RWY_BLUE_TAXIWAY" ) {
930 taxi_lights->addKid( branch );
932 rwy_lights->addKid( branch );
935 material = pt_materials[i];
937 ssgLeaf *leaf = gen_leaf( path, GL_POINTS, material,
938 nodes, normals, texcoords,
939 pts_v[i], pts_n[i], tex_index,
940 false, ground_lights );
941 geometry->addKid( leaf );
945 // Put all randomly-placed objects under a separate branch
946 // (actually an ssgRangeSelector) named "random-models".
947 ssgBranch * random_object_branch = 0;
948 if (use_random_objects) {
949 float ranges[] = { 0, 20000 }; // Maximum 20km range for random objects
950 ssgRangeSelector * object_lod = new ssgRangeSelector;
951 object_lod->setRanges(ranges, 2);
952 object_lod->setName("random-models");
953 geometry->addKid(object_lod);
954 random_object_branch = new ssgBranch;
955 object_lod->addKid(random_object_branch);
958 // generate triangles
959 string_list const& tri_materials = obj.get_tri_materials();
960 group_list const& tris_v = obj.get_tris_v();
961 group_list const& tris_n = obj.get_tris_n();
962 group_list const& tris_tc = obj.get_tris_tc();
963 for ( i = 0; i < tris_v.size(); ++i ) {
964 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLES, tri_materials[i],
965 nodes, normals, texcoords,
966 tris_v[i], tris_n[i], tris_tc[i],
967 is_base, ground_lights );
969 if (use_random_objects)
970 gen_random_surface_objects(leaf, random_object_branch,
971 center, tri_materials[i]);
972 geometry->addKid( leaf );
976 string_list const& strip_materials = obj.get_strip_materials();
977 group_list const& strips_v = obj.get_strips_v();
978 group_list const& strips_n = obj.get_strips_n();
979 group_list const& strips_tc = obj.get_strips_tc();
980 for ( i = 0; i < strips_v.size(); ++i ) {
981 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, strip_materials[i],
982 nodes, normals, texcoords,
983 strips_v[i], strips_n[i], strips_tc[i],
984 is_base, ground_lights );
986 if (use_random_objects)
987 gen_random_surface_objects(leaf, random_object_branch,
988 center,strip_materials[i]);
989 geometry->addKid( leaf );
993 string_list const& fan_materials = obj.get_fan_materials();
994 group_list const& fans_v = obj.get_fans_v();
995 group_list const& fans_n = obj.get_fans_n();
996 group_list const& fans_tc = obj.get_fans_tc();
997 for ( i = 0; i < fans_v.size(); ++i ) {
998 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_FAN, fan_materials[i],
999 nodes, normals, texcoords,
1000 fans_v[i], fans_n[i], fans_tc[i],
1001 is_base, ground_lights );
1002 if (use_random_objects)
1003 gen_random_surface_objects(leaf, random_object_branch,
1004 center, fan_materials[i]);
1005 geometry->addKid( leaf );