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>
56 #include <Main/globals.hxx>
57 #include <Main/fg_props.hxx>
58 #include <Time/light.hxx>
59 #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 FGNewMat::ObjectGroup * object_group;
311 LeafUserData * leafData;
314 void fill_in_triangle();
315 void add_object_to_triangle(FGNewMat::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 FGNewMat::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 (FGNewMat::Object * object)
358 // Set up the random heading if required.
360 if (object->get_heading_type() == FGNewMat::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());
372 void TriUserData::makeWorldMatrix (sgMat4 mat, double hdg_deg )
375 mat[0][0] = leafData->sin_lat * leafData->cos_lon;
376 mat[0][1] = leafData->sin_lat * leafData->sin_lon;
377 mat[0][2] = -leafData->cos_lat;
380 mat[1][0] = -leafData->sin_lon;
381 mat[1][1] = leafData->cos_lon;
385 float sin_hdg = sin( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
386 float cos_hdg = cos( hdg_deg * SGD_DEGREES_TO_RADIANS ) ;
387 mat[0][0] = cos_hdg * leafData->sin_lat * leafData->cos_lon - sin_hdg * leafData->sin_lon;
388 mat[0][1] = cos_hdg * leafData->sin_lat * leafData->sin_lon + sin_hdg * leafData->cos_lon;
389 mat[0][2] = -cos_hdg * leafData->cos_lat;
392 mat[1][0] = -sin_hdg * leafData->sin_lat * leafData->cos_lon - cos_hdg * leafData->sin_lon;
393 mat[1][1] = -sin_hdg * leafData->sin_lat * leafData->sin_lon + cos_hdg * leafData->cos_lon;
394 mat[1][2] = sin_hdg * leafData->cos_lat;
398 mat[2][0] = leafData->cos_lat * leafData->cos_lon;
399 mat[2][1] = leafData->cos_lat * leafData->sin_lon;
400 mat[2][2] = leafData->sin_lat;
403 // translate to random point in triangle
405 random_pt_inside_tri(result, p1, p2, p3);
406 sgSubVec3(mat[3], result, center);
412 * SSG callback for an in-range triangle of randomly-placed objects.
414 * This pretraversal callback is attached to a branch that is traversed
415 * only when a triangle is in range. If the triangle is not currently
416 * populated with randomly-placed objects, this callback will populate
419 * @param entity The entity to which the callback is attached (not used).
420 * @param mask The entity's traversal mask (not used).
421 * @return Always 1, to allow traversal and culling to continue.
424 tri_in_range_callback (ssgEntity * entity, int mask)
426 TriUserData * data = (TriUserData *)entity->getUserData();
427 if (!data->is_filled_in) {
428 data->fill_in_triangle();
429 data->is_filled_in = true;
436 * SSG callback for an out-of-range triangle of randomly-placed objects.
438 * This pretraversal callback is attached to a branch that is traversed
439 * only when a triangle is out of range. If the triangle is currently
440 * populated with randomly-placed objects, the objects will be removed.
443 * @param entity The entity to which the callback is attached (not used).
444 * @param mask The entity's traversal mask (not used).
445 * @return Always 0, to prevent any further traversal or culling.
448 tri_out_of_range_callback (ssgEntity * entity, int mask)
450 TriUserData * data = (TriUserData *)entity->getUserData();
451 if (data->is_filled_in) {
452 data->branch->removeAllKids();
453 data->is_filled_in = false;
460 * ssgEntity with a dummy bounding sphere, to fool culling.
462 * This forces the in-range and out-of-range branches to be visited
463 * when appropriate, even if they have no children. It's ugly, but
464 * it works and seems fairly efficient (since branches can still
465 * be culled when they're out of the view frustum).
467 class DummyBSphereEntity : public ssgBranch
470 DummyBSphereEntity (float radius)
472 bsphere.setCenter(0, 0, 0);
473 bsphere.setRadius(radius);
475 virtual ~DummyBSphereEntity () {}
476 virtual void recalcBSphere () { bsphere_is_invalid = false; }
481 * Calculate the bounding radius of a triangle from its center.
483 * @param center The triangle center.
484 * @param p1 The first point in the triangle.
485 * @param p2 The second point in the triangle.
486 * @param p3 The third point in the triangle.
487 * @return The greatest distance any point lies from the center.
490 get_bounding_radius( sgVec3 center, float *p1, float *p2, float *p3)
492 return sqrt( SG_MAX3( sgDistanceSquaredVec3(center, p1),
493 sgDistanceSquaredVec3(center, p2),
494 sgDistanceSquaredVec3(center, p3) ) );
499 * Set up a triangle for randomly-placed objects.
501 * No objects will be added unless the triangle comes into range.
505 void LeafUserData::setup_triangle (int i )
508 leaf->getTriangle(i, &n1, &n2, &n3);
510 float * p1 = leaf->getVertex(n1);
511 float * p2 = leaf->getVertex(n2);
512 float * p3 = leaf->getVertex(n3);
514 // Set up a single center point for LOD
517 (p1[0] + p2[0] + p3[0]) / 3.0,
518 (p1[1] + p2[1] + p3[1]) / 3.0,
519 (p1[2] + p2[2] + p3[2]) / 3.0);
520 double area = sgTriArea(p1, p2, p3);
522 // maximum radius of an object from center.
523 double bounding_radius = get_bounding_radius(center, p1, p2, p3);
525 // Set up a transformation to the center
526 // point, so that everything else can
527 // be specified relative to it.
528 ssgTransform * location = new ssgTransform;
530 sgMakeTransMat4(TRANS, center);
531 location->setTransform(TRANS);
532 branch->addKid(location);
534 // Iterate through all the object types.
535 int num_groups = mat->get_object_group_count();
536 for (int j = 0; j < num_groups; j++) {
537 // Look up the random object.
538 FGNewMat::ObjectGroup * group = mat->get_object_group(j);
540 // Set up the range selector for the entire
541 // triangle; note that we use the object
542 // range plus the bounding radius here, to
543 // allow for objects far from the center.
544 float ranges[] = { 0,
545 group->get_range_m() + bounding_radius,
547 ssgRangeSelector * lod = new ssgRangeSelector;
548 lod->setRanges(ranges, 3);
549 location->addKid(lod);
551 // Create the in-range and out-of-range
553 ssgBranch * in_range = new ssgBranch;
554 ssgBranch * out_of_range = new ssgBranch;
556 // Set up the user data for if/when
557 // the random objects in this triangle
559 TriUserData * data = new TriUserData;
560 data->is_filled_in = false;
564 sgCopyVec3 (data->center, center);
566 data->object_group = group;
567 data->branch = in_range;
568 data->leafData = this;
569 data->seed = (unsigned int)(p1[0] * j);
571 // Set up the in-range node.
572 in_range->setUserData(data);
573 in_range->setTravCallback(SSG_CALLBACK_PRETRAV,
574 tri_in_range_callback);
575 lod->addKid(in_range);
577 // Set up the out-of-range node.
578 out_of_range->setUserData(data);
579 out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
580 tri_out_of_range_callback);
581 out_of_range->addKid(new DummyBSphereEntity(bounding_radius));
582 lod->addKid(out_of_range);
587 * SSG callback for an in-range leaf of randomly-placed objects.
589 * This pretraversal callback is attached to a branch that is
590 * traversed only when a leaf is in range. If the leaf is not
591 * currently prepared to be populated with randomly-placed objects,
592 * this callback will prepare it (actual population is handled by
593 * the tri_in_range_callback for individual triangles).
595 * @param entity The entity to which the callback is attached (not used).
596 * @param mask The entity's traversal mask (not used).
597 * @return Always 1, to allow traversal and culling to continue.
600 leaf_in_range_callback (ssgEntity * entity, int mask)
602 LeafUserData * data = (LeafUserData *)entity->getUserData();
604 if (!data->is_filled_in) {
605 // Iterate through all the triangles
606 // and populate them.
607 int num_tris = data->leaf->getNumTriangles();
608 for ( int i = 0; i < num_tris; ++i ) {
609 data->setup_triangle(i);
611 data->is_filled_in = true;
618 * SSG callback for an out-of-range leaf of randomly-placed objects.
620 * This pretraversal callback is attached to a branch that is
621 * traversed only when a leaf is out of range. If the leaf is
622 * currently prepared to be populated with randomly-placed objects (or
623 * is actually populated), the objects will be removed.
625 * @param entity The entity to which the callback is attached (not used).
626 * @param mask The entity's traversal mask (not used).
627 * @return Always 0, to prevent any further traversal or culling.
630 leaf_out_of_range_callback (ssgEntity * entity, int mask)
632 LeafUserData * data = (LeafUserData *)entity->getUserData();
633 if (data->is_filled_in) {
634 data->branch->removeAllKids();
635 data->is_filled_in = false;
642 * Randomly place objects on a surface.
644 * The leaf node provides the geometry of the surface, while the
645 * material provides the objects and placement density. Latitude
646 * and longitude are required so that the objects can be rotated
647 * to the world-up vector. This function does not actually add
648 * any objects; instead, it attaches an ssgRangeSelector to the
649 * branch with callbacks to generate the objects when needed.
651 * @param leaf The surface where the objects should be placed.
652 * @param branch The branch that will hold the randomly-placed objects.
653 * @param center The center of the leaf in FlightGear coordinates.
654 * @param material_name The name of the surface's material.
657 gen_random_surface_objects (ssgLeaf *leaf,
660 const string &material_name)
662 // If the surface has no triangles, return
664 int num_tris = leaf->getNumTriangles();
668 // Get the material for this surface.
669 FGNewMat * mat = material_lib.find(material_name);
671 SG_LOG(SG_INPUT, SG_ALERT, "Unknown material " << material_name);
675 // If the material has no randomly-placed
676 // objects, return now.
677 if (mat->get_object_group_count() < 1)
680 // Calculate the geodetic centre of
681 // the tile, for aligning automatic
683 double lon_deg, lat_rad, lat_deg, alt_m, sl_radius_m;
684 Point3D geoc = sgCartToPolar3d(*center);
685 lon_deg = geoc.lon() * SGD_RADIANS_TO_DEGREES;
686 sgGeocToGeod(geoc.lat(), geoc.radius(),
687 &lat_rad, &alt_m, &sl_radius_m);
688 lat_deg = lat_rad * SGD_RADIANS_TO_DEGREES;
691 // max random object range: 20000m
692 float ranges[] = { 0, 20000, 1000000 };
693 ssgRangeSelector * lod = new ssgRangeSelector;
694 lod->setRanges(ranges, 3);
697 // Create the in-range and out-of-range
699 ssgBranch * in_range = new ssgBranch;
700 ssgBranch * out_of_range = new ssgBranch;
701 lod->addKid(in_range);
702 lod->addKid(out_of_range);
704 LeafUserData * data = new LeafUserData;
705 data->is_filled_in = false;
708 data->branch = in_range;
709 data->sin_lat = sin(lat_deg * SGD_DEGREES_TO_RADIANS);
710 data->cos_lat = cos(lat_deg * SGD_DEGREES_TO_RADIANS);
711 data->sin_lon = sin(lon_deg * SGD_DEGREES_TO_RADIANS);
712 data->cos_lon = cos(lon_deg * SGD_DEGREES_TO_RADIANS);
714 in_range->setUserData(data);
715 in_range->setTravCallback(SSG_CALLBACK_PRETRAV, leaf_in_range_callback);
716 out_of_range->setUserData(data);
717 out_of_range->setTravCallback(SSG_CALLBACK_PRETRAV,
718 leaf_out_of_range_callback);
720 ->addKid(new DummyBSphereEntity(leaf->getBSphere()->getRadius()));
725 ////////////////////////////////////////////////////////////////////////
727 ////////////////////////////////////////////////////////////////////////
729 ssgLeaf *gen_leaf( const string& path,
730 const GLenum ty, const string& material,
731 const point_list& nodes, const point_list& normals,
732 const point_list& texcoords,
733 const int_list& node_index,
734 const int_list& normal_index,
735 const int_list& tex_index,
736 const bool calc_lights, ssgVertexArray *lights )
738 double tex_width = 1000.0, tex_height = 1000.0;
739 ssgSimpleState *state = NULL;
742 FGNewMat *newmat = material_lib.find( material );
743 if ( newmat == NULL ) {
744 // see if this is an on the fly texture
746 string::size_type pos = file.rfind( "/" );
747 file = file.substr( 0, pos );
748 // cout << "current file = " << file << endl;
751 // cout << "current file = " << file << endl;
752 if ( ! material_lib.add_item( file ) ) {
753 SG_LOG( SG_TERRAIN, SG_ALERT,
754 "Ack! unknown usemtl name = " << material
757 // locate our newly created material
758 newmat = material_lib.find( material );
759 if ( newmat == NULL ) {
760 SG_LOG( SG_TERRAIN, SG_ALERT,
761 "Ack! bad on the fly material create = "
762 << material << " in " << path );
767 if ( newmat != NULL ) {
768 // set the texture width and height values for this
770 tex_width = newmat->get_xsize();
771 tex_height = newmat->get_ysize();
772 state = newmat->get_state();
773 coverage = newmat->get_light_coverage();
774 // cout << "(w) = " << tex_width << " (h) = "
775 // << tex_width << endl;
786 int size = node_index.size();
788 SG_LOG( SG_TERRAIN, SG_ALERT, "Woh! node list size < 1" );
791 ssgVertexArray *vl = new ssgVertexArray( size );
793 for ( i = 0; i < size; ++i ) {
794 node = nodes[ node_index[i] ];
795 sgSetVec3( tmp3, node[0], node[1], node[2] );
801 ssgNormalArray *nl = new ssgNormalArray( size );
802 if ( normal_index.size() ) {
803 // object file specifies normal indices (i.e. normal indices
805 for ( i = 0; i < size; ++i ) {
806 normal = normals[ normal_index[i] ];
807 sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
811 // use implied normal indices. normal index = vertex index.
812 for ( i = 0; i < size; ++i ) {
813 normal = normals[ node_index[i] ];
814 sgSetVec3( tmp3, normal[0], normal[1], normal[2] );
820 ssgColourArray *cl = new ssgColourArray( 1 );
821 sgSetVec4( tmp4, 1.0, 1.0, 1.0, 1.0 );
824 // texture coordinates
825 size = tex_index.size();
827 ssgTexCoordArray *tl = new ssgTexCoordArray( size );
829 texcoord = texcoords[ tex_index[0] ];
830 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
831 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
832 if ( tex_width > 0 ) {
833 tmp2[0] *= (1000.0 / tex_width);
835 if ( tex_height > 0 ) {
836 tmp2[1] *= (1000.0 / tex_height);
839 } else if ( size > 1 ) {
840 for ( i = 0; i < size; ++i ) {
841 texcoord = texcoords[ tex_index[i] ];
842 sgSetVec2( tmp2, texcoord[0], texcoord[1] );
843 if ( tex_width > 0 ) {
844 tmp2[0] *= (1000.0 / tex_width);
846 if ( tex_height > 0 ) {
847 tmp2[1] *= (1000.0 / tex_height);
853 ssgLeaf *leaf = new ssgVtxTable ( ty, vl, nl, tl, cl );
855 // lookup the state record
857 leaf->setState( state );
860 if ( coverage > 0.0 ) {
861 if ( coverage < 10000.0 ) {
862 SG_LOG(SG_INPUT, SG_ALERT, "Light coverage is "
863 << coverage << ", pushing up to 10000");
866 gen_random_surface_points(leaf, lights, coverage);
874 // Load an Binary obj file
875 bool fgBinObjLoad( const string& path, const bool is_base,
877 double *bounding_radius,
879 ssgBranch* rwy_lights,
880 ssgBranch* taxi_lights,
881 ssgVertexArray *ground_lights )
884 bool use_random_objects =
885 fgGetBool("/sim/rendering/random-objects", true);
887 if ( ! obj.read_bin( path ) ) {
891 geometry->setName( (char *)path.c_str() );
893 // reference point (center offset/bounding sphere)
894 *center = obj.get_gbs_center();
895 *bounding_radius = obj.get_gbs_radius();
897 point_list const& nodes = obj.get_wgs84_nodes();
898 // point_list const& colors = obj.get_colors();
899 point_list const& normals = obj.get_normals();
900 point_list const& texcoords = obj.get_texcoords();
905 group_list::size_type i;
908 string_list const& pt_materials = obj.get_pt_materials();
909 group_list const& pts_v = obj.get_pts_v();
910 group_list const& pts_n = obj.get_pts_n();
911 for ( i = 0; i < pts_v.size(); ++i ) {
912 // cout << "pts_v.size() = " << pts_v.size() << endl;
913 if ( pt_materials[i].substr(0, 3) == "RWY" ) {
915 sgSetVec3( up, center->x(), center->y(), center->z() );
916 // returns a transform -> lod -> leaf structure
917 ssgBranch *branch = gen_directional_lights( nodes, normals,
921 // branches don't honor callbacks as far as I know so I'm
922 // commenting this out to avoid a plib runtime warning.
923 branch->setTravCallback( SSG_CALLBACK_PRETRAV,
924 runway_lights_pretrav );
925 if ( pt_materials[i].substr(0, 16) == "RWY_BLUE_TAXIWAY" ) {
926 taxi_lights->addKid( branch );
928 rwy_lights->addKid( branch );
931 material = pt_materials[i];
933 ssgLeaf *leaf = gen_leaf( path, GL_POINTS, material,
934 nodes, normals, texcoords,
935 pts_v[i], pts_n[i], tex_index,
936 false, ground_lights );
937 geometry->addKid( leaf );
941 // Put all randomly-placed objects under a separate branch
942 // (actually an ssgRangeSelector) named "random-models".
943 ssgBranch * random_object_branch = 0;
944 if (use_random_objects) {
945 float ranges[] = { 0, 20000 }; // Maximum 20km range for random objects
946 ssgRangeSelector * object_lod = new ssgRangeSelector;
947 object_lod->setRanges(ranges, 2);
948 object_lod->setName("random-models");
949 geometry->addKid(object_lod);
950 random_object_branch = new ssgBranch;
951 object_lod->addKid(random_object_branch);
954 // generate triangles
955 string_list const& tri_materials = obj.get_tri_materials();
956 group_list const& tris_v = obj.get_tris_v();
957 group_list const& tris_n = obj.get_tris_n();
958 group_list const& tris_tc = obj.get_tris_tc();
959 for ( i = 0; i < tris_v.size(); ++i ) {
960 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLES, tri_materials[i],
961 nodes, normals, texcoords,
962 tris_v[i], tris_n[i], tris_tc[i],
963 is_base, ground_lights );
965 if (use_random_objects)
966 gen_random_surface_objects(leaf, random_object_branch,
967 center, tri_materials[i]);
968 geometry->addKid( leaf );
972 string_list const& strip_materials = obj.get_strip_materials();
973 group_list const& strips_v = obj.get_strips_v();
974 group_list const& strips_n = obj.get_strips_n();
975 group_list const& strips_tc = obj.get_strips_tc();
976 for ( i = 0; i < strips_v.size(); ++i ) {
977 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_STRIP, strip_materials[i],
978 nodes, normals, texcoords,
979 strips_v[i], strips_n[i], strips_tc[i],
980 is_base, ground_lights );
982 if (use_random_objects)
983 gen_random_surface_objects(leaf, random_object_branch,
984 center,strip_materials[i]);
985 geometry->addKid( leaf );
989 string_list const& fan_materials = obj.get_fan_materials();
990 group_list const& fans_v = obj.get_fans_v();
991 group_list const& fans_n = obj.get_fans_n();
992 group_list const& fans_tc = obj.get_fans_tc();
993 for ( i = 0; i < fans_v.size(); ++i ) {
994 ssgLeaf *leaf = gen_leaf( path, GL_TRIANGLE_FAN, fan_materials[i],
995 nodes, normals, texcoords,
996 fans_v[i], fans_n[i], fans_tc[i],
997 is_base, ground_lights );
998 if (use_random_objects)
999 gen_random_surface_objects(leaf, random_object_branch,
1000 center, fan_materials[i]);
1001 geometry->addKid( leaf );