// genobj.hxx -- Generate the flight gear "obj" file format from the // triangle output // // Written by Curtis Olson, started March 1999. // // Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of the // License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. // // $Id$ // (Log is kept at end of this file) #include #include #include #include #include "genobj.hxx" // build the wgs-84 point list void FGGenOutput::gen_wgs84_points( const FGArray& array ) { cout << "calculating wgs84 point" << endl; Point3D geod, radians, cart; const_point_list_iterator current = geod_nodes.begin(); const_point_list_iterator last = geod_nodes.end(); double real_z; for ( ; current != last; ++current ) { geod = *current; real_z = array.interpolate_altitude( geod.x() * 3600.0, geod.y() * 3600.0 ); // convert to radians radians = Point3D( geod.x() * DEG_TO_RAD, geod.y() * DEG_TO_RAD, real_z ); cart = fgGeodToCart(radians); // cout << cart << endl; wgs84_nodes.push_back(cart); } } // build the node -> element (triangle) reverse lookup table. there // is an entry for each point containing a list of all the triangles // that share that point. void FGGenOutput::gen_node_ele_lookup_table() { belongs_to ele_list; ele_list.erase( ele_list.begin(), ele_list.end() ); // initialize reverse_ele_lookup structure by creating an empty // list for each point const_point_list_iterator w_current = wgs84_nodes.begin(); const_point_list_iterator w_last = wgs84_nodes.end(); for ( ; w_current != w_last; ++w_current ) { reverse_ele_lookup.push_back( ele_list ); } // traverse triangle structure building reverse lookup table const_triele_list_iterator current = tri_elements.begin(); const_triele_list_iterator last = tri_elements.end(); int counter = 0; for ( ; current != last; ++current ) { reverse_ele_lookup[ current->get_n1() ].push_back( counter ); reverse_ele_lookup[ current->get_n2() ].push_back( counter ); reverse_ele_lookup[ current->get_n3() ].push_back( counter ); ++counter; } } // caclulate the normal for the specified triangle face Point3D FGGenOutput::calc_normal( int i ) { double v1[3], v2[3], normal[3]; double temp; Point3D p1 = wgs84_nodes[ tri_elements[i].get_n1() ]; Point3D p2 = wgs84_nodes[ tri_elements[i].get_n2() ]; Point3D p3 = wgs84_nodes[ tri_elements[i].get_n3() ]; v1[0] = p2.x() - p1.x(); v1[1] = p2.y() - p1.y(); v1[2] = p2.z() - p1.z(); v2[0] = p3.x() - p1.x(); v2[1] = p3.y() - p1.y(); v2[2] = p3.z() - p1.z(); MAT3cross_product(normal, v1, v2); MAT3_NORMALIZE_VEC(normal,temp); return Point3D( normal[0], normal[1], normal[2] ); } // build the face normal list void FGGenOutput::gen_face_normals() { // traverse triangle structure building the face normal table cout << "calculating face normals" << endl; for ( int i = 0; i < (int)tri_elements.size(); i++ ) { // cout << calc_normal( i ) << endl; face_normals.push_back( calc_normal( i ) ); } } // calculate the normals for each point in wgs84_nodes void FGGenOutput::gen_normals() { Point3D normal; cout << "caculating node normals" << endl; // for each node for ( int i = 0; i < (int)wgs84_nodes.size(); ++i ) { belongs_to tri_list = reverse_ele_lookup[i]; belongs_to_iterator current = tri_list.begin(); belongs_to_iterator last = tri_list.end(); Point3D average( 0.0 ); // for each triangle that shares this node for ( ; current != last; ++current ) { normal = face_normals[ *current ]; average += normal; // cout << normal << endl; } average /= tri_list.size(); // cout << "average = " << average << endl; point_normals.push_back( average ); } } // calculate the global bounding sphere. Center is the average of the // points. void FGGenOutput::calc_gbs() { double dist_squared; double radius_squared = 0; gbs_center = Point3D( 0.0 ); const_point_list_iterator current = wgs84_nodes.begin(); const_point_list_iterator last = wgs84_nodes.end(); for ( ; current != last; ++current ) { gbs_center += *current; } gbs_center /= wgs84_nodes.size(); current = wgs84_nodes.begin(); for ( ; current != last; ++current ) { dist_squared = gbs_center.distance3Dsquared(*current); if ( dist_squared > radius_squared ) { radius_squared = dist_squared; } } gbs_radius = sqrt(radius_squared); } // build the necessary output structures based on the triangulation // data int FGGenOutput::build( const FGArray& array, const FGTriangle& t ) { FGTriNodes trinodes = t.get_out_nodes(); // copy the geodetic node list into this class geod_nodes = trinodes.get_node_list(); // copy the triangle list into this class tri_elements = t.get_elelist(); // generate the point list in wgs-84 coordinates gen_wgs84_points( array ); // calculate the global bounding sphere calc_gbs(); cout << "center = " << gbs_center << " radius = " << gbs_radius << endl; // build the node -> element (triangle) reverse lookup table gen_node_ele_lookup_table(); // build the face normal list gen_face_normals(); // calculate the normals for each point in wgs84_nodes gen_normals(); return 1; } // caclulate the bounding sphere for a list of triangle faces void FGGenOutput::calc_group_bounding_sphere( const triele_list& tris, Point3D *center, double *radius ) { cout << "calculate group bounding sphere for " << tris.size() << " tris." << endl; // generate a list of unique points from the triangle list FGTriNodes nodes; const_triele_list_iterator t_current = tris.begin(); const_triele_list_iterator t_last = tris.end(); for ( ; t_current != t_last; ++t_current ) { Point3D p1 = wgs84_nodes[ t_current->get_n1() ]; Point3D p2 = wgs84_nodes[ t_current->get_n2() ]; Point3D p3 = wgs84_nodes[ t_current->get_n3() ]; nodes.unique_add(p1); nodes.unique_add(p2); nodes.unique_add(p3); } // find average of point list Point3D c( 0.0 ); point_list points = nodes.get_node_list(); cout << "found " << points.size() << " unique nodes" << endl; point_list_iterator p_current = points.begin(); point_list_iterator p_last = points.end(); for ( ; p_current != p_last; ++p_current ) { c += *p_current; } c /= points.size(); // find max radius double dist_squared; double max_squared = 0; p_current = points.begin(); p_last = points.end(); for ( ; p_current != p_last; ++p_current ) { dist_squared = c.distance3Dsquared(*p_current); if ( dist_squared > max_squared ) { max_squared = dist_squared; } } *center = c; *radius = sqrt(max_squared); } // caclulate the bounding sphere for the specified triangle face void FGGenOutput::calc_bounding_sphere( const FGTriEle& t, Point3D *center, double *radius ) { Point3D c( 0.0 ); Point3D p1 = wgs84_nodes[ t.get_n1() ]; Point3D p2 = wgs84_nodes[ t.get_n2() ]; Point3D p3 = wgs84_nodes[ t.get_n3() ]; c = p1 + p2 + p3; c /= 3; double dist_squared; double max_squared = 0; dist_squared = c.distance3Dsquared(p1); if ( dist_squared > max_squared ) { max_squared = dist_squared; } dist_squared = c.distance3Dsquared(p2); if ( dist_squared > max_squared ) { max_squared = dist_squared; } dist_squared = c.distance3Dsquared(p3); if ( dist_squared > max_squared ) { max_squared = dist_squared; } *center = c; *radius = sqrt(max_squared); } // write out the fgfs scenery file int FGGenOutput::write( const string& base, const FGBucket& b ) { Point3D p; string dir = base + "/Scenery/" + b.gen_base_path(); string command = "mkdir -p " + dir; system(command.c_str()); string file = dir + "/" + b.gen_index_str(); cout << "Output file = " << file << endl; FILE *fp; if ( (fp = fopen( file.c_str(), "w" )) == NULL ) { cout << "ERROR: opening " << file << " for writing!" << endl; exit(-1); } // write headers fprintf(fp, "# FGFS Scenery Version %s\n", FG_SCENERY_FILE_FORMAT); time_t calendar_time = time(NULL); struct tm *local_tm; local_tm = localtime( &calendar_time ); char time_str[256]; strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm); fprintf(fp, "# Created %s\n", time_str ); fprintf(fp, "\n"); // write global bounding sphere fprintf(fp, "# gbs %.5f %.5f %.5f %.2f\n", gbs_center.x(), gbs_center.y(), gbs_center.z(), gbs_radius); fprintf(fp, "\n"); // write nodes fprintf(fp, "# vertex list\n"); const_point_list_iterator w_current = wgs84_nodes.begin(); const_point_list_iterator w_last = wgs84_nodes.end(); for ( ; w_current != w_last; ++w_current ) { p = *w_current - gbs_center; fprintf(fp, "v %.5f %.5f %.5f\n", p.x(), p.y(), p.z()); } fprintf(fp, "\n"); // write vertex normals fprintf(fp, "# vertex normal list\n"); const_point_list_iterator n_current = point_normals.begin(); const_point_list_iterator n_last = point_normals.end(); for ( ; n_current != n_last; ++n_current ) { p = *n_current; fprintf(fp, "vn %.5f %.5f %.5f\n", p.x(), p.y(), p.z()); } fprintf(fp, "\n"); // write triangles (grouped by type for now) Point3D center; double radius; fprintf(fp, "# triangle list\n"); fprintf(fp, "\n"); for ( int i = 0; i < FG_MAX_AREA_TYPES; ++i ) { triele_list area_tris; area_tris.erase( area_tris.begin(), area_tris.end() ); const_triele_list_iterator t_current = tri_elements.begin(); const_triele_list_iterator t_last = tri_elements.end(); for ( ; t_current != t_last; ++t_current ) { if ( (int)t_current->get_attribute() == i ) { area_tris.push_back( *t_current ); } } if ( (int)area_tris.size() > 0 ) { string attr_name = get_area_name( (AreaType)i ); calc_group_bounding_sphere( area_tris, ¢er, &radius ); cout << "writing " << (int)area_tris.size() << " faces for " << attr_name << endl; fprintf(fp, "# usemtl %s\n", attr_name.c_str() ); fprintf(fp, "# bs %.4f %.4f %.4f %.2f\n", center.x(), center.y(), center.z(), radius); triele_list_iterator a_current = area_tris.begin(); triele_list_iterator a_last = area_tris.end(); for ( ; a_current != a_last; ++a_current ) { fprintf( fp, "f %d %d %d\n", a_current->get_n1(), a_current->get_n2(), a_current->get_n3() ); } fprintf( fp, "\n" ); } } fclose(fp); command = "gzip --force --best " + file; system(command.c_str()); return 1; } // $Log$ // Revision 1.5 1999/03/27 14:06:42 curt // Tweaks to bounding sphere calculation routines. // Group like triangles together for output to be in a single display list, // even though they are individual, non-fanified, triangles. // // Revision 1.4 1999/03/27 05:23:22 curt // Interpolate real z value of all nodes from dem data. // Write scenery file to correct location. // Pass along correct triangle attributes and write to output file. // // Revision 1.3 1999/03/25 19:04:21 curt // Preparations for outputing scenery file to correct location. // // Revision 1.2 1999/03/23 22:02:03 curt // Worked on creating data to output ... normals, bounding spheres, etc. // // Revision 1.1 1999/03/22 23:51:51 curt // Initial revision. //