+// Generate a generic ocean tile on the fly
+ssgBranch *fgGenTile( const string& path, FGTileEntry *t) {
+ FGNewMat *newmat;
+
+ ssgSimpleState *state = NULL;
+
+ ssgBranch *tile = new ssgBranch () ;
+ if ( !tile ) {
+ SG_LOG( SG_TERRAIN, SG_ALERT, "fgGenTile(): NO MEMORY" );
+ return NULL;
+ }
+
+ tile -> setName ( (char *)path.c_str() ) ;
+
+ double tex_width = 1000.0;
+ // double tex_height;
+
+ // find Ocean material in the properties list
+ newmat = material_lib.find( "Ocean" );
+ if ( newmat != NULL ) {
+ // set the texture width and height values for this
+ // material
+ tex_width = newmat->get_xsize();
+ // tex_height = newmat->get_ysize();
+
+ // set ssgState
+ state = newmat->get_state();
+ } else {
+ SG_LOG( SG_TERRAIN, SG_ALERT,
+ "Ack! unknown usemtl name = " << "Ocean"
+ << " in " << path );
+ }
+
+ // Calculate center point
+ SGBucket b = t->tile_bucket;
+ double clon = b.get_center_lon();
+ double clat = b.get_center_lat();
+ double height = b.get_height();
+ double width = b.get_width();
+
+ Point3D center = sgGeodToCart(Point3D(clon*SGD_DEGREES_TO_RADIANS,clat*SGD_DEGREES_TO_RADIANS,0.0));
+ t->center = center;
+ // cout << "center = " << center << endl;;
+
+ // Caculate corner vertices
+ Point3D geod[4];
+ geod[0] = Point3D( clon - width/2.0, clat - height/2.0, 0.0 );
+ geod[1] = Point3D( clon + width/2.0, clat - height/2.0, 0.0 );
+ geod[2] = Point3D( clon + width/2.0, clat + height/2.0, 0.0 );
+ geod[3] = Point3D( clon - width/2.0, clat + height/2.0, 0.0 );
+
+ Point3D rad[4];
+ int i;
+ for ( i = 0; i < 4; ++i ) {
+ rad[i] = Point3D( geod[i].x() * SGD_DEGREES_TO_RADIANS,
+ geod[i].y() * SGD_DEGREES_TO_RADIANS,
+ geod[i].z() );
+ }
+
+ Point3D cart[4], rel[4];
+ t->nodes.clear();
+ for ( i = 0; i < 4; ++i ) {
+ cart[i] = sgGeodToCart(rad[i]);
+ rel[i] = cart[i] - center;
+ t->nodes.push_back( rel[i] );
+ // cout << "corner " << i << " = " << cart[i] << endl;
+ }
+
+ t->ncount = 4;
+
+ // Calculate bounding radius
+ t->bounding_radius = center.distance3D( cart[0] );
+ // cout << "bounding radius = " << t->bounding_radius << endl;
+
+ // Calculate normals
+ Point3D normals[4];
+ for ( i = 0; i < 4; ++i ) {
+ double length = cart[i].distance3D( Point3D(0.0) );
+ normals[i] = cart[i] / length;
+ // cout << "normal = " << normals[i] << endl;
+ }
+
+ // Calculate texture coordinates
+ point_list geod_nodes;
+ geod_nodes.clear();
+ int_list rectangle;
+ rectangle.clear();
+ for ( i = 0; i < 4; ++i ) {
+ geod_nodes.push_back( geod[i] );
+ rectangle.push_back( i );
+ }
+ point_list texs = calc_tex_coords( b, geod_nodes, rectangle,
+ 1000.0 / tex_width );
+
+ // Allocate ssg structure
+ ssgVertexArray *vl = new ssgVertexArray( 4 );
+ ssgNormalArray *nl = new ssgNormalArray( 4 );
+ ssgTexCoordArray *tl = new ssgTexCoordArray( 4 );
+ ssgColourArray *cl = new ssgColourArray( 1 );
+
+ sgVec4 color;
+ sgSetVec4( color, 1.0, 1.0, 1.0, 1.0 );
+ cl->add( color );
+
+ // sgVec3 *vtlist = new sgVec3 [ 4 ];
+ // t->vec3_ptrs.push_back( vtlist );
+ // sgVec3 *vnlist = new sgVec3 [ 4 ];
+ // t->vec3_ptrs.push_back( vnlist );
+ // sgVec2 *tclist = new sgVec2 [ 4 ];
+ // t->vec2_ptrs.push_back( tclist );
+
+ sgVec2 tmp2;
+ sgVec3 tmp3;
+ for ( i = 0; i < 4; ++i ) {
+ sgSetVec3( tmp3,
+ rel[i].x(), rel[i].y(), rel[i].z() );
+ vl->add( tmp3 );
+
+ sgSetVec3( tmp3,
+ normals[i].x(), normals[i].y(), normals[i].z() );
+ nl->add( tmp3 );
+
+ sgSetVec2( tmp2, texs[i].x(), texs[i].y());
+ tl->add( tmp2 );
+ }
+
+ ssgLeaf *leaf =
+ new ssgVtxTable ( GL_TRIANGLE_FAN, vl, nl, tl, cl );
+
+ leaf->setState( state );
+
+ tile->addKid( leaf );
+
+ return tile;
+}
+
+
+static void random_pt_inside_tri( float *res,
+ float *n1, float *n2, float *n3 )
+{
+ sgVec3 p1, p2, p3;
+
+ double a = sg_random();
+ double b = sg_random();
+ if ( a + b > 1.0 ) {
+ a = 1.0 - a;
+ b = 1.0 - b;
+ }
+ double c = 1 - a - b;
+
+ sgScaleVec3( p1, n1, a );
+ sgScaleVec3( p2, n2, b );
+ sgScaleVec3( p3, n3, c );
+
+ sgAddVec3( res, p1, p2 );
+ sgAddVec3( res, p3 );
+}
+
+
+static void gen_random_surface_points( ssgLeaf *leaf, ssgVertexArray *lights,
+ double factor ) {
+ int num = leaf->getNumTriangles();
+ if ( num > 0 ) {
+ short int n1, n2, n3;
+ float *p1, *p2, *p3;
+ sgVec3 result;
+
+ // generate a repeatable random seed
+ p1 = leaf->getVertex( 0 );
+ unsigned int seed = (unsigned int)p1[0];
+ sg_srandom( seed );
+
+ for ( int i = 0; i < num; ++i ) {
+ leaf->getTriangle( i, &n1, &n2, &n3 );
+ p1 = leaf->getVertex(n1);
+ p2 = leaf->getVertex(n2);
+ p3 = leaf->getVertex(n3);
+ double area = sgTriArea( p1, p2, p3 );
+ double num = area / factor;
+
+ // generate a light point for each unit of area
+ while ( num > 1.0 ) {
+ random_pt_inside_tri( result, p1, p2, p3 );
+ lights->add( result );
+ num -= 1.0;
+ }
+ // for partial units of area, use a zombie door method to
+ // create the proper random chance of a light being created
+ // for this triangle
+ if ( num > 0.0 ) {
+ if ( sg_random() <= num ) {
+ // a zombie made it through our door
+ random_pt_inside_tri( result, p1, p2, p3 );
+ lights->add( result );
+ }
+ }
+ }
+ }
+}
+
+
+// Load an Ascii obj file
+ssgBranch *fgAsciiObjLoad( const string& path, FGTileEntry *t,
+ ssgVertexArray *lights, const bool is_base)
+{
+ FGNewMat *newmat = NULL;
+ string material;
+ float coverage = -1;