head 1.4; access; symbols; locks; strict; comment @# @; 1.4 date 2002.09.07.01.50.41; author curt; state Exp; branches; next 1.3; 1.3 date 2002.08.31.19.01.59; author curt; state Exp; branches; next 1.2; 1.2 date 2002.08.31.18.54.05; author curt; state Exp; branches; next 1.1; 1.1 date 2002.02.15.17.22.02; author curt; state Exp; branches; next ; desc @@ 1.4 log @Updated doxygen docs. @ text @
Main Page Class Hierarchy Compound List File List Compound Members File Members

# texcoord.cxx

    00001 // texcoord.hxx -- routine(s) to handle texture coordinate generation
    00002 //
    00003 // Written by Curtis Olson, started March 1999.
    00004 //
    00005 // Copyright (C) 1999  Curtis L. Olson  - curt@@flightgear.org
    00006 //
    00007 // This library is free software; you can redistribute it and/or
    00008 // modify it under the terms of the GNU Library General Public
    00009 // License as published by the Free Software Foundation; either
    00010 // version 2 of the License, or (at your option) any later version.
    00011 //
    00012 // This library is distributed in the hope that it will be useful,
    00013 // but WITHOUT ANY WARRANTY; without even the implied warranty of
    00014 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00015 // Library General Public License for more details.
    00016 //
    00017 // You should have received a copy of the GNU Library General Public
    00018 // License along with this library; if not, write to the
    00019 // Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00020 // Boston, MA  02111-1307, USA.
    00021 //
    00022 // $Id: texcoord.cxx,v 1.9 2001/03/24 03:20:47 curt Exp $
    00023
    00024
    00025 /* The following is an explanation of our somewhat conveluted and
    00026    tricky texture scaling/offset scheme:
    00027
    00028 MAX_TEX_COORD is a value I arrived at by trial and error for my
    00029 voodoo2/3 video card.  If you use texture coordinates that are too
    00030 big, you quickly start getting into round off problems and the texture
    00031 jumps and moves relative to the polygon.
    00032
    00033 The point of all of this code is that I wanted to be able to define
    00034 this size in meters of a texture and have it be applied seamlessly to
    00035 the terrain.  I wanted to be able to change the defined size (in
    00036 meters) of textures at run time.  In other words I want to be able to
    00037 scale the textures at run time and still have them seamlessly tile
    00038 together across fans.
    00039
    00040 The problem is that I have to pregenerate all the texture coordinates
    00041 when I create the scenery, and I didn't want to burn CPU doing this
    00042 again when I load the scenery at run time.
    00043
    00044 It ended up taking me a lot of thought, a lot of trial and error, and
    00045 a lot of fiddling around to come up with a scheme that worked.
    00046
    00047 ----------
    00048
    00049 Ok, so think about what needs to be done to have the texture tile
    00050 across a series of triangles and fans ...
    00051
    00052 Basically you want to use some function of lon/lat mod your max
    00053 texture coordinate size to calculate the texture coordinate of each
    00054 vertex.  This should result in nice tiling across distinct triangles
    00055 and fans.
    00056
    00057 Pretend our MAX_TEX_COORD = 4.0 and half of this is 2.0
    00058
    00059 Imagine the following two adjacent polygons with the "X" component of
    00060 the initial texture coordinate based on longitude (Note they are drawn
    00061 spaced apart, but in reality the two polygons are adjacent):
    00062
    00063     7.0   8.6   8.6    9.0
    00064      *-----*     *------*
    00065      |     |     |      |
    00066
    00067 Now, this exceeds our MAX_TEX_COORD of 4.0 so we have to scale these
    00068 texture coordinates by some integer value.  Let's say we always want
    00069 to minimize the tex coordinates to minimize rounding error so we will
    00070 offset the first polygon by 7.0 and the second by 8.0:
    00071
    00072     0.0 --- 1.6 and 0.6 --- 1.0
    00073
    00074 Our tiling is maintianed becuase the coordinates are continous (mod
    00075 1.0) and we still get the double repeat across both polygons.
    00076
    00077 We want to be able to scale these values by an arbitrary constant and
    00078 still have proper tiling.
    00079
    00080 Let's try doubling the coordinates:
    00081
    00082     0.0 --- 3.2 and 1.2 --- 2.0
    00083
    00084 Everything still tiles nicely (because the coordinates are continuous
    00085 mod 1.0) and the texture is now repeated 4x across the two polygons.
    00086 Before it was repeated 2x.
    00087
    00088 Let's try halving the coordinates:
    00089
    00090     0.0 --- 0.8 and 0.3 --- 0.5
    00091
    00092 Ooop!  We lost continuity in texture coordinate space ... no we will
    00093 have a visual discontinuity in the texture tiling!
    00094
    00095 Ok, so we need some other scheme to keep our texture coordinates
    00096 smaller than MAX_TEX_COORD that preserves continuity in texture
    00097 space.  <Deep breath> let's try the scheme that I have coded up that
    00098 you are asking about ... <fingers crossed> :-)
    00099
    00100 Going way back to the top before we shifted the texture coordinates.
    00101 tmin for the first polygon is 7.0, this is then adjusted to:
    00102
    00103     (int)(tmin.x() / HALF_MAX_TEX_COORD) ) * HALF_MAX_TEX_COORD
    00104
    00105     = (int)(7.0/2.0) * 2.0 = 3.0 * 2.0 = 6.0
    00106
    00107 The two texture coordinates are offset by 6.0 which yields 1.0 -- 2.6
    00108
    00109 tmin for the second polygon is 8.6 which is adjusted to:
    00110
    00111     (int)(tmin.x() / HALF_MAX_TEX_COORD) ) * HALF_MAX_TEX_COORD
    00112     = (int)( 8.6 / 2.0 ) * 2.0 = 4.0 * 2.0 = 8.0
    00113
    00114 The offset for the second polygon is 8.0 which yields 0.6 --- 1.0
    00115
    00116 So now we have:
    00117
    00118     1.0 --- 2.6 and 0.6 --- 1.0
    00119
    00120 This still tiles nicely and strethes our texture across completely, so
    00121 far we haven't done any damage.
    00122
    00123 Now let's double the coordinates:
    00124
    00125      2.0 --- 5.2 and 1.2 --- 2.0
    00126
    00127 The texture is repeated 4x as it should be and is still continuous.
    00128
    00129 How about halfing the coordinates.  This is where the first scheme
    00130 broke down.  Halving the coordinates yields
    00131
    00132     0.5 --- 1.3 and 0.3 --- 0.5
    00133
    00134 Woohoo, we still have texture space continuity (mod 1.0) and the
    00135 texture is repeated 1x.
    00136
    00137 Note, it took me almost as long to re-figure this out and write this
    00138 explanation as it did to figure out the scheme originally.  I better
    00139 enter this in the official comments in case I forget again. :-)
    00140
    00141 */
    00142
    00143 #include "texcoord.hxx"
    00144
    00145
    00146 #define FG_STANDARD_TEXTURE_DIMENSION 1000.0 // meters
    00147 #define MAX_TEX_COORD 8.0
    00148 #define HALF_MAX_TEX_COORD ( MAX_TEX_COORD * 0.5 )
    00149
    00150
    00151 // return the basic unshifted/unmoded texture coordinate for a lat/lon
    00152 inline Point3D basic_tex_coord( const Point3D& p,
    00153                                 double degree_width, double degree_height,
    00154                                 double scale )
    00155 {
    00156     return Point3D( p.x() * ( degree_width * scale /
    00157                               FG_STANDARD_TEXTURE_DIMENSION ),
    00158                     p.y() * ( degree_height * scale /
    00159                               FG_STANDARD_TEXTURE_DIMENSION ),
    00160                     0.0 );
    00161 }
    00162
    00163
    00164 // traverse the specified fan/strip/list of vertices and attempt to
    00165 // calculate "none stretching" texture coordinates
    00166 point_list calc_tex_coords( const SGBucket& b, const point_list& geod_nodes,
    00167                             const int_list& fan, double scale )
    00168 {
    00169     // cout << "calculating texture coordinates for a specific fan of size = "
    00170     //      << fan.size() << endl;
    00171
    00172     // calculate perimeter based on center of this degree (not center
    00173     // of bucket)
    00174     double clat = (int)b.get_center_lat();
    00175     if ( clat > 0 ) {
    00176         clat = (int)clat + 0.5;
    00177     } else {
    00178         clat = (int)clat - 0.5;
    00179     }
    00180
    00181     double clat_rad = clat * SGD_DEGREES_TO_RADIANS;
    00182     double cos_lat = cos( clat_rad );
    00183     double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M;
    00184     double local_perimeter = 2.0 * local_radius * SGD_PI;
    00185     double degree_width = local_perimeter / 360.0;
    00186
    00187     // cout << "clat = " << clat << endl;
    00188     // cout << "clat (radians) = " << clat_rad << endl;
    00189     // cout << "cos(lat) = " << cos_lat << endl;
    00190     // cout << "local_radius = " << local_radius << endl;
    00191     // cout << "local_perimeter = " << local_perimeter << endl;
    00192     // cout << "degree_width = " << degree_width << endl;
    00193
    00194     double perimeter = 2.0 * SG_EQUATORIAL_RADIUS_M * SGD_PI;
    00195     double degree_height = perimeter / 360.0;
    00196     // cout << "degree_height = " << degree_height << endl;
    00197
    00198     // find min/max of fan
    00199     Point3D tmin, tmax, p, t;
    00200     bool first = true;
    00201
    00202     int i;
    00203
    00204     for ( i = 0; i < (int)fan.size(); ++i ) {
    00205         p = geod_nodes[ fan[i] ];
    00206         // cout << "point p = " << p << endl;
    00207
    00208         t = basic_tex_coord( p, degree_width, degree_height, scale );
    00209         // cout << "basic_tex_coord = " << t << endl;
    00210
    00211         if ( first ) {
    00212             tmin = tmax = t;
    00213             first = false;
    00214         } else {
    00215             if ( t.x() < tmin.x() ) {
    00216                 tmin.setx( t.x() );
    00217             }
    00218             if ( t.y() < tmin.y() ) {
    00219                 tmin.sety( t.y() );
    00220             }
    00221             if ( t.x() > tmax.x() ) {
    00222                 tmax.setx( t.x() );
    00223             }
    00224             if ( t.y() > tmax.y() ) {
    00225                 tmax.sety( t.y() );
    00226             }
    00227         }
    00228     }
    00229
    00230     double dx = fabs( tmax.x() - tmin.x() );
    00231     double dy = fabs( tmax.y() - tmin.y() );
    00232     // cout << "dx = " << dx << " dy = " << dy << endl;
    00233
    00234     // Point3D mod_shift;
    00235     if ( (dx > HALF_MAX_TEX_COORD) || (dy > HALF_MAX_TEX_COORD) ) {
    00236         // structure is too big, we'll just have to shift it so that
    00237         // tmin = (0,0).  This messes up subsequent texture scaling,
    00238         // but is the best we can do.
    00239         // cout << "SHIFTING" << endl;
    00240         if ( tmin.x() < 0 ) {
    00241             tmin.setx( (double)( (int)tmin.x() - 1 ) );
    00242         } else {
    00243             tmin.setx( (int)tmin.x() );
    00244         }
    00245         if ( tmin.y() < 0 ) {
    00246             tmin.sety( (double)( (int)tmin.y() - 1 ) );
    00247         } else {
    00248             tmin.sety( (int)tmin.y() );
    00249         }
    00250         // cout << "found tmin = " << tmin << endl;
    00251     } else {
    00252         if ( tmin.x() < 0 ) {
    00253             tmin.setx( ( (int)(tmin.x() / HALF_MAX_TEX_COORD) - 1 )
    00254                        * HALF_MAX_TEX_COORD );
    00255         } else {
    00256             tmin.setx( ( (int)(tmin.x() / HALF_MAX_TEX_COORD) )
    00257                        * HALF_MAX_TEX_COORD );
    00258         }
    00259         if ( tmin.y() < 0 ) {
    00260             tmin.sety( ( (int)(tmin.y() / HALF_MAX_TEX_COORD) - 1 )
    00261                        * HALF_MAX_TEX_COORD );
    00262         } else {
    00263             tmin.sety( ( (int)(tmin.y() / HALF_MAX_TEX_COORD) )
    00264                        * HALF_MAX_TEX_COORD );
    00265         }
    00266 #if 0
    00267         // structure is small enough ... we can mod it so we can
    00268         // properly scale the texture coordinates later.
    00269         // cout << "MODDING" << endl;
    00270         double x1 = fmod(tmin.x(), MAX_TEX_COORD);
    00271         while ( x1 < 0 ) { x1 += MAX_TEX_COORD; }
    00272
    00273         double y1 = fmod(tmin.y(), MAX_TEX_COORD);
    00274         while ( y1 < 0 ) { y1 += MAX_TEX_COORD; }
    00275
    00276         double x2 = fmod(tmax.x(), MAX_TEX_COORD);
    00277         while ( x2 < 0 ) { x2 += MAX_TEX_COORD; }
    00278
    00279         double y2 = fmod(tmax.y(), MAX_TEX_COORD);
    00280         while ( y2 < 0 ) { y2 += MAX_TEX_COORD; }
    00281
    00282         // At this point we know that the object is < 16 wide in
    00283         // texture coordinate space.  If the modulo of the tmin is >
    00284         // the mod of the tmax at this point, then we know that the
    00285         // starting tex coordinate for the tmax > 16 so we can shift
    00286         // everything down by 16 and get it within the 0-32 range.
    00287
    00288         if ( x1 > x2 ) {
    00289             mod_shift.setx( HALF_MAX_TEX_COORD );
    00290         } else {
    00291             mod_shift.setx( 0.0 );
    00292         }
    00293
    00294         if ( y1 > y2 ) {
    00295             mod_shift.sety( HALF_MAX_TEX_COORD );
    00296         } else {
    00297             mod_shift.sety( 0.0 );
    00298         }
    00299 #endif
    00300         // cout << "mod_shift = " << mod_shift << endl;
    00301     }
    00302
    00303     // generate tex_list
    00304     Point3D adjusted_t;
    00305     point_list tex;
    00306     tex.clear();
    00307     for ( i = 0; i < (int)fan.size(); ++i ) {
    00308         p = geod_nodes[ fan[i] ];
    00309         t = basic_tex_coord( p, degree_width, degree_height, scale );
    00310         // cout << "second t = " << t << endl;
    00311
    00312         adjusted_t = t - tmin;
    00313 #if 0
    00314         } else {
    00315             adjusted_t.setx( fmod(t.x() + mod_shift.x(), MAX_TEX_COORD) );
    00316             while ( adjusted_t.x() < 0 ) {
    00317                 adjusted_t.setx( adjusted_t.x() + MAX_TEX_COORD );
    00318             }
    00319             adjusted_t.sety( fmod(t.y() + mod_shift.y(), MAX_TEX_COORD) );
    00320             while ( adjusted_t.y() < 0 ) {
    00321                 adjusted_t.sety( adjusted_t.y() + MAX_TEX_COORD );
    00322             }
    00323             // cout << "adjusted_t " << adjusted_t << endl;
    00324         }
    00325 #endif
    00326         if ( adjusted_t.x() < SG_EPSILON ) {
    00327             adjusted_t.setx( 0.0 );
    00328         }
    00329         if ( adjusted_t.y() < SG_EPSILON ) {
    00330             adjusted_t.sety( 0.0 );
    00331         }
    00332         adjusted_t.setz( 0.0 );
    00333         // cout << "adjusted_t = " << adjusted_t << endl;
    00334
    00335         tex.push_back( adjusted_t );
    00336     }
    00337
    00338     return tex;
    00339 }

@ 1.3 log @Oops, try again. @ text @d348 1 a348 1