X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=simgear%2Fmisc%2Ftexcoord.cxx;h=2705980d1c96db73cc04ed30c65878f49aface37;hb=2e71b64de1d937e2b7c34dd4a2365be455d0b82a;hp=61efc23e7bd55d1c59c58f46c0856f72fd55f41c;hpb=5173d709e090b953eaf800cbcd1bf897de332a12;p=simgear.git diff --git a/simgear/misc/texcoord.cxx b/simgear/misc/texcoord.cxx index 61efc23e..2705980d 100644 --- a/simgear/misc/texcoord.cxx +++ b/simgear/misc/texcoord.cxx @@ -2,41 +2,171 @@ // // Written by Curtis Olson, started March 1999. // -// Copyright (C) 1999 Curtis L. Olson - curt@flightgear.org +// Copyright (C) 1999 Curtis L. Olson - http://www.flightgear.org/~curt // -// 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 library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Library 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 +// This library 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. +// Library 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. +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ +/* The following is an explanation of our somewhat conveluted and + tricky texture scaling/offset scheme: + +MAX_TEX_COORD is a value I arrived at by trial and error for my +voodoo2/3 video card. If you use texture coordinates that are too +big, you quickly start getting into round off problems and the texture +jumps and moves relative to the polygon. + +The point of all of this code is that I wanted to be able to define +this size in meters of a texture and have it be applied seamlessly to +the terrain. I wanted to be able to change the defined size (in +meters) of textures at run time. In other words I want to be able to +scale the textures at run time and still have them seamlessly tile +together across fans. + +The problem is that I have to pregenerate all the texture coordinates +when I create the scenery, and I didn't want to burn CPU doing this +again when I load the scenery at run time. + +It ended up taking me a lot of thought, a lot of trial and error, and +a lot of fiddling around to come up with a scheme that worked. + +---------- + +Ok, so think about what needs to be done to have the texture tile +across a series of triangles and fans ... + +Basically you want to use some function of lon/lat mod your max +texture coordinate size to calculate the texture coordinate of each +vertex. This should result in nice tiling across distinct triangles +and fans. + +Pretend our MAX_TEX_COORD = 4.0 and half of this is 2.0 + +Imagine the following two adjacent polygons with the "X" component of +the initial texture coordinate based on longitude (Note they are drawn +spaced apart, but in reality the two polygons are adjacent): + + 7.0 8.6 8.6 9.0 + *-----* *------* + | | | | + +Now, this exceeds our MAX_TEX_COORD of 4.0 so we have to scale these +texture coordinates by some integer value. Let's say we always want +to minimize the tex coordinates to minimize rounding error so we will +offset the first polygon by 7.0 and the second by 8.0: + + 0.0 --- 1.6 and 0.6 --- 1.0 + +Our tiling is maintianed becuase the coordinates are continous (mod +1.0) and we still get the double repeat across both polygons. + +We want to be able to scale these values by an arbitrary constant and +still have proper tiling. + +Let's try doubling the coordinates: + + 0.0 --- 3.2 and 1.2 --- 2.0 + +Everything still tiles nicely (because the coordinates are continuous +mod 1.0) and the texture is now repeated 4x across the two polygons. +Before it was repeated 2x. + +Let's try halving the coordinates: + + 0.0 --- 0.8 and 0.3 --- 0.5 + +Ooop! We lost continuity in texture coordinate space ... no we will +have a visual discontinuity in the texture tiling! + +Ok, so we need some other scheme to keep our texture coordinates +smaller than MAX_TEX_COORD that preserves continuity in texture +space. let's try the scheme that I have coded up that +you are asking about ... :-) + +Going way back to the top before we shifted the texture coordinates. +tmin for the first polygon is 7.0, this is then adjusted to: + + (int)(tmin.x() / HALF_MAX_TEX_COORD) ) * HALF_MAX_TEX_COORD + + = (int)(7.0/2.0) * 2.0 = 3.0 * 2.0 = 6.0 + +The two texture coordinates are offset by 6.0 which yields 1.0 -- 2.6 + +tmin for the second polygon is 8.6 which is adjusted to: + + (int)(tmin.x() / HALF_MAX_TEX_COORD) ) * HALF_MAX_TEX_COORD + = (int)( 8.6 / 2.0 ) * 2.0 = 4.0 * 2.0 = 8.0 + +The offset for the second polygon is 8.0 which yields 0.6 --- 1.0 + +So now we have: + + 1.0 --- 2.6 and 0.6 --- 1.0 + +This still tiles nicely and strethes our texture across completely, so +far we haven't done any damage. + +Now let's double the coordinates: + + 2.0 --- 5.2 and 1.2 --- 2.0 + +The texture is repeated 4x as it should be and is still continuous. + +How about halfing the coordinates. This is where the first scheme +broke down. Halving the coordinates yields + + 0.5 --- 1.3 and 0.3 --- 0.5 + +Woohoo, we still have texture space continuity (mod 1.0) and the +texture is repeated 1x. + +Note, it took me almost as long to re-figure this out and write this +explanation as it did to figure out the scheme originally. I better +enter this in the official comments in case I forget again. :-) + +*/ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +// #include + #include "texcoord.hxx" +// using std::cout; +// using std::endl; + #define FG_STANDARD_TEXTURE_DIMENSION 1000.0 // meters #define MAX_TEX_COORD 8.0 -#define HALF_MAX_TEX_COORD ( MAX_TEX_COORD / 2.0 ) +#define HALF_MAX_TEX_COORD ( MAX_TEX_COORD * 0.5 ) // return the basic unshifted/unmoded texture coordinate for a lat/lon -inline Point3D basic_tex_coord( const Point3D& p, - double degree_width, double degree_height, - double scale ) +static inline Point3D basic_tex_coord( const Point3D& p, + double degree_width, + double degree_height, + double scale ) { return Point3D( p.x() * ( degree_width * scale / FG_STANDARD_TEXTURE_DIMENSION ), - p.y() * ( degree_width * scale / + p.y() * ( degree_height * scale / FG_STANDARD_TEXTURE_DIMENSION ), 0.0 ); } @@ -44,7 +174,13 @@ inline Point3D basic_tex_coord( const Point3D& p, // traverse the specified fan/strip/list of vertices and attempt to // calculate "none stretching" texture coordinates -point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, +point_list sgCalcTexCoords( const SGBucket& b, const point_list& geod_nodes, + const int_list& fan, double scale ) +{ + return sgCalcTexCoords(b.get_center_lat(), geod_nodes, fan, scale); +} + +point_list sgCalcTexCoords( double centerLat, const point_list& geod_nodes, const int_list& fan, double scale ) { // cout << "calculating texture coordinates for a specific fan of size = " @@ -52,17 +188,17 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, // calculate perimeter based on center of this degree (not center // of bucket) - double clat = (int)b.get_center_lat(); + double clat = (int)centerLat; if ( clat > 0 ) { clat = (int)clat + 0.5; } else { clat = (int)clat - 0.5; } - double clat_rad = clat * DEG_TO_RAD; + double clat_rad = clat * SGD_DEGREES_TO_RADIANS; double cos_lat = cos( clat_rad ); - double local_radius = cos_lat * EQUATORIAL_RADIUS_M; - double local_perimeter = 2.0 * local_radius * FG_PI; + double local_radius = cos_lat * SG_EQUATORIAL_RADIUS_M; + double local_perimeter = local_radius * SGD_2PI; double degree_width = local_perimeter / 360.0; // cout << "clat = " << clat << endl; @@ -72,7 +208,7 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, // cout << "local_perimeter = " << local_perimeter << endl; // cout << "degree_width = " << degree_width << endl; - double perimeter = 2.0 * EQUATORIAL_RADIUS_M * FG_PI; + double perimeter = SG_EQUATORIAL_RADIUS_M * SGD_2PI; double degree_height = perimeter / 360.0; // cout << "degree_height = " << degree_height << endl; @@ -80,7 +216,9 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, Point3D tmin, tmax, p, t; bool first = true; - for ( int i = 0; i < (int)fan.size(); ++i ) { + int i; + + for ( i = 0; i < (int)fan.size(); ++i ) { p = geod_nodes[ fan[i] ]; // cout << "point p = " << p << endl; @@ -110,18 +248,39 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, double dy = fabs( tmax.y() - tmin.y() ); // cout << "dx = " << dx << " dy = " << dy << endl; - bool do_shift = false; - Point3D mod_shift; + // Point3D mod_shift; if ( (dx > HALF_MAX_TEX_COORD) || (dy > HALF_MAX_TEX_COORD) ) { // structure is too big, we'll just have to shift it so that // tmin = (0,0). This messes up subsequent texture scaling, // but is the best we can do. // cout << "SHIFTING" << endl; - do_shift = true; - tmin.setx( (double)( (int)tmin.x() + 1 ) ); - tmin.sety( (double)( (int)tmin.y() + 1 ) ); + if ( tmin.x() < 0 ) { + tmin.setx( (double)( (int)tmin.x() - 1 ) ); + } else { + tmin.setx( (int)tmin.x() ); + } + if ( tmin.y() < 0 ) { + tmin.sety( (double)( (int)tmin.y() - 1 ) ); + } else { + tmin.sety( (int)tmin.y() ); + } // cout << "found tmin = " << tmin << endl; } else { + if ( tmin.x() < 0 ) { + tmin.setx( ( (int)(tmin.x() / HALF_MAX_TEX_COORD) - 1 ) + * HALF_MAX_TEX_COORD ); + } else { + tmin.setx( ( (int)(tmin.x() / HALF_MAX_TEX_COORD) ) + * HALF_MAX_TEX_COORD ); + } + if ( tmin.y() < 0 ) { + tmin.sety( ( (int)(tmin.y() / HALF_MAX_TEX_COORD) - 1 ) + * HALF_MAX_TEX_COORD ); + } else { + tmin.sety( ( (int)(tmin.y() / HALF_MAX_TEX_COORD) ) + * HALF_MAX_TEX_COORD ); + } +#if 0 // structure is small enough ... we can mod it so we can // properly scale the texture coordinates later. // cout << "MODDING" << endl; @@ -140,7 +299,7 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, // At this point we know that the object is < 16 wide in // texture coordinate space. If the modulo of the tmin is > // the mod of the tmax at this point, then we know that the - // starting tex coordinate for the tmin > 16 so we can shift + // starting tex coordinate for the tmax > 16 so we can shift // everything down by 16 and get it within the 0-32 range. if ( x1 > x2 ) { @@ -154,7 +313,7 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, } else { mod_shift.sety( 0.0 ); } - +#endif // cout << "mod_shift = " << mod_shift << endl; } @@ -162,13 +321,13 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, Point3D adjusted_t; point_list tex; tex.clear(); - for ( int i = 0; i < (int)fan.size(); ++i ) { + for ( i = 0; i < (int)fan.size(); ++i ) { p = geod_nodes[ fan[i] ]; t = basic_tex_coord( p, degree_width, degree_height, scale ); // cout << "second t = " << t << endl; - if ( do_shift ) { - adjusted_t = t - tmin; + adjusted_t = t - tmin; +#if 0 } else { adjusted_t.setx( fmod(t.x() + mod_shift.x(), MAX_TEX_COORD) ); while ( adjusted_t.x() < 0 ) { @@ -180,11 +339,11 @@ point_list calc_tex_coords( const FGBucket& b, const point_list& geod_nodes, } // cout << "adjusted_t " << adjusted_t << endl; } - - if ( adjusted_t.x() < FG_EPSILON ) { +#endif + if ( adjusted_t.x() < SG_EPSILON ) { adjusted_t.setx( 0.0 ); } - if ( adjusted_t.y() < FG_EPSILON ) { + if ( adjusted_t.y() < SG_EPSILON ) { adjusted_t.sety( 0.0 ); } adjusted_t.setz( 0.0 );