Main Page | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Data Fields | Globals

tr_curve.c

Go to the documentation of this file.
00001 /*
00002 ===========================================================================
00003 Copyright (C) 1999-2005 Id Software, Inc.
00004 
00005 This file is part of Quake III Arena source code.
00006 
00007 Quake III Arena source code is free software; you can redistribute it
00008 and/or modify it under the terms of the GNU General Public License as
00009 published by the Free Software Foundation; either version 2 of the License,
00010 or (at your option) any later version.
00011 
00012 Quake III Arena source code is distributed in the hope that it will be
00013 useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
00014 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015 GNU General Public License for more details.
00016 
00017 You should have received a copy of the GNU General Public License
00018 along with Foobar; if not, write to the Free Software
00019 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
00020 ===========================================================================
00021 */
00022 
00023 #include "tr_local.h"
00024 
00025 /*
00026 
00027 This file does all of the processing necessary to turn a raw grid of points
00028 read from the map file into a srfGridMesh_t ready for rendering.
00029 
00030 The level of detail solution is direction independent, based only on subdivided
00031 distance from the true curve.
00032 
00033 Only a single entry point:
00034 
00035 srfGridMesh_t *R_SubdividePatchToGrid( int width, int height,
00036                                 drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) {
00037 
00038 */
00039 
00040 
00041 /*
00042 ============
00043 LerpDrawVert
00044 ============
00045 */
00046 static void LerpDrawVert( drawVert_t *a, drawVert_t *b, drawVert_t *out ) {
00047     out->xyz[0] = 0.5f * (a->xyz[0] + b->xyz[0]);
00048     out->xyz[1] = 0.5f * (a->xyz[1] + b->xyz[1]);
00049     out->xyz[2] = 0.5f * (a->xyz[2] + b->xyz[2]);
00050 
00051     out->st[0] = 0.5f * (a->st[0] + b->st[0]);
00052     out->st[1] = 0.5f * (a->st[1] + b->st[1]);
00053 
00054     out->lightmap[0] = 0.5f * (a->lightmap[0] + b->lightmap[0]);
00055     out->lightmap[1] = 0.5f * (a->lightmap[1] + b->lightmap[1]);
00056 
00057     out->color[0] = (a->color[0] + b->color[0]) >> 1;
00058     out->color[1] = (a->color[1] + b->color[1]) >> 1;
00059     out->color[2] = (a->color[2] + b->color[2]) >> 1;
00060     out->color[3] = (a->color[3] + b->color[3]) >> 1;
00061 }
00062 
00063 /*
00064 ============
00065 Transpose
00066 ============
00067 */
00068 static void Transpose( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
00069     int     i, j;
00070     drawVert_t  temp;
00071 
00072     if ( width > height ) {
00073         for ( i = 0 ; i < height ; i++ ) {
00074             for ( j = i + 1 ; j < width ; j++ ) {
00075                 if ( j < height ) {
00076                     // swap the value
00077                     temp = ctrl[j][i];
00078                     ctrl[j][i] = ctrl[i][j];
00079                     ctrl[i][j] = temp;
00080                 } else {
00081                     // just copy
00082                     ctrl[j][i] = ctrl[i][j];
00083                 }
00084             }
00085         }
00086     } else {
00087         for ( i = 0 ; i < width ; i++ ) {
00088             for ( j = i + 1 ; j < height ; j++ ) {
00089                 if ( j < width ) {
00090                     // swap the value
00091                     temp = ctrl[i][j];
00092                     ctrl[i][j] = ctrl[j][i];
00093                     ctrl[j][i] = temp;
00094                 } else {
00095                     // just copy
00096                     ctrl[i][j] = ctrl[j][i];
00097                 }
00098             }
00099         }
00100     }
00101 
00102 }
00103 
00104 
00105 /*
00106 =================
00107 MakeMeshNormals
00108 
00109 Handles all the complicated wrapping and degenerate cases
00110 =================
00111 */
00112 static void MakeMeshNormals( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
00113     int     i, j, k, dist;
00114     vec3_t  normal;
00115     vec3_t  sum;
00116     int     count;
00117     vec3_t  base;
00118     vec3_t  delta;
00119     int     x, y;
00120     drawVert_t  *dv;
00121     vec3_t      around[8], temp;
00122     qboolean    good[8];
00123     qboolean    wrapWidth, wrapHeight;
00124     float       len;
00125 static  int neighbors[8][2] = {
00126     {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1}
00127     };
00128 
00129     wrapWidth = qfalse;
00130     for ( i = 0 ; i < height ; i++ ) {
00131         VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta );
00132         len = VectorLengthSquared( delta );
00133         if ( len > 1.0 ) {
00134             break;
00135         }
00136     }
00137     if ( i == height ) {
00138         wrapWidth = qtrue;
00139     }
00140 
00141     wrapHeight = qfalse;
00142     for ( i = 0 ; i < width ; i++ ) {
00143         VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta );
00144         len = VectorLengthSquared( delta );
00145         if ( len > 1.0 ) {
00146             break;
00147         }
00148     }
00149     if ( i == width) {
00150         wrapHeight = qtrue;
00151     }
00152 
00153 
00154     for ( i = 0 ; i < width ; i++ ) {
00155         for ( j = 0 ; j < height ; j++ ) {
00156             count = 0;
00157             dv = &ctrl[j][i];
00158             VectorCopy( dv->xyz, base );
00159             for ( k = 0 ; k < 8 ; k++ ) {
00160                 VectorClear( around[k] );
00161                 good[k] = qfalse;
00162 
00163                 for ( dist = 1 ; dist <= 3 ; dist++ ) {
00164                     x = i + neighbors[k][0] * dist;
00165                     y = j + neighbors[k][1] * dist;
00166                     if ( wrapWidth ) {
00167                         if ( x < 0 ) {
00168                             x = width - 1 + x;
00169                         } else if ( x >= width ) {
00170                             x = 1 + x - width;
00171                         }
00172                     }
00173                     if ( wrapHeight ) {
00174                         if ( y < 0 ) {
00175                             y = height - 1 + y;
00176                         } else if ( y >= height ) {
00177                             y = 1 + y - height;
00178                         }
00179                     }
00180 
00181                     if ( x < 0 || x >= width || y < 0 || y >= height ) {
00182                         break;                  // edge of patch
00183                     }
00184                     VectorSubtract( ctrl[y][x].xyz, base, temp );
00185                     if ( VectorNormalize2( temp, temp ) == 0 ) {
00186                         continue;               // degenerate edge, get more dist
00187                     } else {
00188                         good[k] = qtrue;
00189                         VectorCopy( temp, around[k] );
00190                         break;                  // good edge
00191                     }
00192                 }
00193             }
00194 
00195             VectorClear( sum );
00196             for ( k = 0 ; k < 8 ; k++ ) {
00197                 if ( !good[k] || !good[(k+1)&7] ) {
00198                     continue;   // didn't get two points
00199                 }
00200                 CrossProduct( around[(k+1)&7], around[k], normal );
00201                 if ( VectorNormalize2( normal, normal ) == 0 ) {
00202                     continue;
00203                 }
00204                 VectorAdd( normal, sum, sum );
00205                 count++;
00206             }
00207             if ( count == 0 ) {
00208 //printf("bad normal\n");
00209                 count = 1;
00210             }
00211             VectorNormalize2( sum, dv->normal );
00212         }
00213     }
00214 }
00215 
00216 
00217 /*
00218 ============
00219 InvertCtrl
00220 ============
00221 */
00222 static void InvertCtrl( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) {
00223     int     i, j;
00224     drawVert_t  temp;
00225 
00226     for ( i = 0 ; i < height ; i++ ) {
00227         for ( j = 0 ; j < width/2 ; j++ ) {
00228             temp = ctrl[i][j];
00229             ctrl[i][j] = ctrl[i][width-1-j];
00230             ctrl[i][width-1-j] = temp;
00231         }
00232     }
00233 }
00234 
00235 
00236 /*
00237 =================
00238 InvertErrorTable
00239 =================
00240 */
00241 static void InvertErrorTable( float errorTable[2][MAX_GRID_SIZE], int width, int height ) {
00242     int     i;
00243     float   copy[2][MAX_GRID_SIZE];
00244 
00245     Com_Memcpy( copy, errorTable, sizeof( copy ) );
00246 
00247     for ( i = 0 ; i < width ; i++ ) {
00248         errorTable[1][i] = copy[0][i];  //[width-1-i];
00249     }
00250 
00251     for ( i = 0 ; i < height ; i++ ) {
00252         errorTable[0][i] = copy[1][height-1-i];
00253     }
00254 
00255 }
00256 
00257 /*
00258 ==================
00259 PutPointsOnCurve
00260 ==================
00261 */
00262 static void PutPointsOnCurve( drawVert_t    ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], 
00263                              int width, int height ) {
00264     int         i, j;
00265     drawVert_t  prev, next;
00266 
00267     for ( i = 0 ; i < width ; i++ ) {
00268         for ( j = 1 ; j < height ; j += 2 ) {
00269             LerpDrawVert( &ctrl[j][i], &ctrl[j+1][i], &prev );
00270             LerpDrawVert( &ctrl[j][i], &ctrl[j-1][i], &next );
00271             LerpDrawVert( &prev, &next, &ctrl[j][i] );
00272         }
00273     }
00274 
00275 
00276     for ( j = 0 ; j < height ; j++ ) {
00277         for ( i = 1 ; i < width ; i += 2 ) {
00278             LerpDrawVert( &ctrl[j][i], &ctrl[j][i+1], &prev );
00279             LerpDrawVert( &ctrl[j][i], &ctrl[j][i-1], &next );
00280             LerpDrawVert( &prev, &next, &ctrl[j][i] );
00281         }
00282     }
00283 }
00284 
00285 /*
00286 =================
00287 R_CreateSurfaceGridMesh
00288 =================
00289 */
00290 srfGridMesh_t *R_CreateSurfaceGridMesh(int width, int height,
00291                                 drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE], float errorTable[2][MAX_GRID_SIZE] ) {
00292     int i, j, size;
00293     drawVert_t  *vert;
00294     vec3_t      tmpVec;
00295     srfGridMesh_t *grid;
00296 
00297     // copy the results out to a grid
00298     size = (width * height - 1) * sizeof( drawVert_t ) + sizeof( *grid );
00299 
00300 #ifdef PATCH_STITCHING
00301     grid = /*ri.Hunk_Alloc*/ ri.Malloc( size );
00302     Com_Memset(grid, 0, size);
00303 
00304     grid->widthLodError = /*ri.Hunk_Alloc*/ ri.Malloc( width * 4 );
00305     Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 );
00306 
00307     grid->heightLodError = /*ri.Hunk_Alloc*/ ri.Malloc( height * 4 );
00308     Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 );
00309 #else
00310     grid = ri.Hunk_Alloc( size );
00311     Com_Memset(grid, 0, size);
00312 
00313     grid->widthLodError = ri.Hunk_Alloc( width * 4 );
00314     Com_Memcpy( grid->widthLodError, errorTable[0], width * 4 );
00315 
00316     grid->heightLodError = ri.Hunk_Alloc( height * 4 );
00317     Com_Memcpy( grid->heightLodError, errorTable[1], height * 4 );
00318 #endif
00319 
00320     grid->width = width;
00321     grid->height = height;
00322     grid->surfaceType = SF_GRID;
00323     ClearBounds( grid->meshBounds[0], grid->meshBounds[1] );
00324     for ( i = 0 ; i < width ; i++ ) {
00325         for ( j = 0 ; j < height ; j++ ) {
00326             vert = &grid->verts[j*width+i];
00327             *vert = ctrl[j][i];
00328             AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] );
00329         }
00330     }
00331 
00332     // compute local origin and bounds
00333     VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin );
00334     VectorScale( grid->localOrigin, 0.5f, grid->localOrigin );
00335     VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec );
00336     grid->meshRadius = VectorLength( tmpVec );
00337 
00338     VectorCopy( grid->localOrigin, grid->lodOrigin );
00339     grid->lodRadius = grid->meshRadius;
00340     //
00341     return grid;
00342 }
00343 
00344 /*
00345 =================
00346 R_FreeSurfaceGridMesh
00347 =================
00348 */
00349 void R_FreeSurfaceGridMesh( srfGridMesh_t *grid ) {
00350     ri.Free(grid->widthLodError);
00351     ri.Free(grid->heightLodError);
00352     ri.Free(grid);
00353 }
00354 
00355 /*
00356 =================
00357 R_SubdividePatchToGrid
00358 =================
00359 */
00360 srfGridMesh_t *R_SubdividePatchToGrid( int width, int height,
00361                                 drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) {
00362     int         i, j, k, l;
00363     drawVert_t  prev, next, mid;
00364     float       len, maxLen;
00365     int         dir;
00366     int         t;
00367     MAC_STATIC drawVert_t   ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
00368     float       errorTable[2][MAX_GRID_SIZE];
00369 
00370     for ( i = 0 ; i < width ; i++ ) {
00371         for ( j = 0 ; j < height ; j++ ) {
00372             ctrl[j][i] = points[j*width+i];
00373         }
00374     }
00375 
00376     for ( dir = 0 ; dir < 2 ; dir++ ) {
00377 
00378         for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) {
00379             errorTable[dir][j] = 0;
00380         }
00381 
00382         // horizontal subdivisions
00383         for ( j = 0 ; j + 2 < width ; j += 2 ) {
00384             // check subdivided midpoints against control points
00385 
00386             // FIXME: also check midpoints of adjacent patches against the control points
00387             // this would basically stitch all patches in the same LOD group together.
00388 
00389             maxLen = 0;
00390             for ( i = 0 ; i < height ; i++ ) {
00391                 vec3_t      midxyz;
00392                 vec3_t      midxyz2;
00393                 vec3_t      dir;
00394                 vec3_t      projected;
00395                 float       d;
00396 
00397                 // calculate the point on the curve
00398                 for ( l = 0 ; l < 3 ; l++ ) {
00399                     midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2
00400                             + ctrl[i][j+2].xyz[l] ) * 0.25f;
00401                 }
00402 
00403                 // see how far off the line it is
00404                 // using dist-from-line will not account for internal
00405                 // texture warping, but it gives a lot less polygons than
00406                 // dist-from-midpoint
00407                 VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz );
00408                 VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir );
00409                 VectorNormalize( dir );
00410 
00411                 d = DotProduct( midxyz, dir );
00412                 VectorScale( dir, d, projected );
00413                 VectorSubtract( midxyz, projected, midxyz2);
00414                 len = VectorLengthSquared( midxyz2 );           // we will do the sqrt later
00415                 if ( len > maxLen ) {
00416                     maxLen = len;
00417                 }
00418             }
00419 
00420             maxLen = sqrt(maxLen);
00421 
00422             // if all the points are on the lines, remove the entire columns
00423             if ( maxLen < 0.1f ) {
00424                 errorTable[dir][j+1] = 999;
00425                 continue;
00426             }
00427 
00428             // see if we want to insert subdivided columns
00429             if ( width + 2 > MAX_GRID_SIZE ) {
00430                 errorTable[dir][j+1] = 1.0f/maxLen;
00431                 continue;   // can't subdivide any more
00432             }
00433 
00434             if ( maxLen <= r_subdivisions->value ) {
00435                 errorTable[dir][j+1] = 1.0f/maxLen;
00436                 continue;   // didn't need subdivision
00437             }
00438 
00439             errorTable[dir][j+2] = 1.0f/maxLen;
00440 
00441             // insert two columns and replace the peak
00442             width += 2;
00443             for ( i = 0 ; i < height ; i++ ) {
00444                 LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev );
00445                 LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next );
00446                 LerpDrawVert( &prev, &next, &mid );
00447 
00448                 for ( k = width - 1 ; k > j + 3 ; k-- ) {
00449                     ctrl[i][k] = ctrl[i][k-2];
00450                 }
00451                 ctrl[i][j + 1] = prev;
00452                 ctrl[i][j + 2] = mid;
00453                 ctrl[i][j + 3] = next;
00454             }
00455 
00456             // back up and recheck this set again, it may need more subdivision
00457             j -= 2;
00458 
00459         }
00460 
00461         Transpose( width, height, ctrl );
00462         t = width;
00463         width = height;
00464         height = t;
00465     }
00466 
00467 
00468     // put all the aproximating points on the curve
00469     PutPointsOnCurve( ctrl, width, height );
00470 
00471     // cull out any rows or columns that are colinear
00472     for ( i = 1 ; i < width-1 ; i++ ) {
00473         if ( errorTable[0][i] != 999 ) {
00474             continue;
00475         }
00476         for ( j = i+1 ; j < width ; j++ ) {
00477             for ( k = 0 ; k < height ; k++ ) {
00478                 ctrl[k][j-1] = ctrl[k][j];
00479             }
00480             errorTable[0][j-1] = errorTable[0][j];
00481         }
00482         width--;
00483     }
00484 
00485     for ( i = 1 ; i < height-1 ; i++ ) {
00486         if ( errorTable[1][i] != 999 ) {
00487             continue;
00488         }
00489         for ( j = i+1 ; j < height ; j++ ) {
00490             for ( k = 0 ; k < width ; k++ ) {
00491                 ctrl[j-1][k] = ctrl[j][k];
00492             }
00493             errorTable[1][j-1] = errorTable[1][j];
00494         }
00495         height--;
00496     }
00497 
00498 #if 1
00499     // flip for longest tristrips as an optimization
00500     // the results should be visually identical with or
00501     // without this step
00502     if ( height > width ) {
00503         Transpose( width, height, ctrl );
00504         InvertErrorTable( errorTable, width, height );
00505         t = width;
00506         width = height;
00507         height = t;
00508         InvertCtrl( width, height, ctrl );
00509     }
00510 #endif
00511 
00512     // calculate normals
00513     MakeMeshNormals( width, height, ctrl );
00514 
00515     return R_CreateSurfaceGridMesh( width, height, ctrl, errorTable );
00516 }
00517 
00518 /*
00519 ===============
00520 R_GridInsertColumn
00521 ===============
00522 */
00523 srfGridMesh_t *R_GridInsertColumn( srfGridMesh_t *grid, int column, int row, vec3_t point, float loderror ) {
00524     int i, j;
00525     int width, height, oldwidth;
00526     MAC_STATIC drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
00527     float errorTable[2][MAX_GRID_SIZE];
00528     float lodRadius;
00529     vec3_t lodOrigin;
00530 
00531     oldwidth = 0;
00532     width = grid->width + 1;
00533     if (width > MAX_GRID_SIZE)
00534         return NULL;
00535     height = grid->height;
00536     for (i = 0; i < width; i++) {
00537         if (i == column) {
00538             //insert new column
00539             for (j = 0; j < grid->height; j++) {
00540                 LerpDrawVert( &grid->verts[j * grid->width + i-1], &grid->verts[j * grid->width + i], &ctrl[j][i] );
00541                 if (j == row)
00542                     VectorCopy(point, ctrl[j][i].xyz);
00543             }
00544             errorTable[0][i] = loderror;
00545             continue;
00546         }
00547         errorTable[0][i] = grid->widthLodError[oldwidth];
00548         for (j = 0; j < grid->height; j++) {
00549             ctrl[j][i] = grid->verts[j * grid->width + oldwidth];
00550         }
00551         oldwidth++;
00552     }
00553     for (j = 0; j < grid->height; j++) {
00554         errorTable[1][j] = grid->heightLodError[j];
00555     }
00556     // put all the aproximating points on the curve
00557     //PutPointsOnCurve( ctrl, width, height );
00558     // calculate normals
00559     MakeMeshNormals( width, height, ctrl );
00560 
00561     VectorCopy(grid->lodOrigin, lodOrigin);
00562     lodRadius = grid->lodRadius;
00563     // free the old grid
00564     R_FreeSurfaceGridMesh(grid);
00565     // create a new grid
00566     grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable );
00567     grid->lodRadius = lodRadius;
00568     VectorCopy(lodOrigin, grid->lodOrigin);
00569     return grid;
00570 }
00571 
00572 /*
00573 ===============
00574 R_GridInsertRow
00575 ===============
00576 */
00577 srfGridMesh_t *R_GridInsertRow( srfGridMesh_t *grid, int row, int column, vec3_t point, float loderror ) {
00578     int i, j;
00579     int width, height, oldheight;
00580     MAC_STATIC drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE];
00581     float errorTable[2][MAX_GRID_SIZE];
00582     float lodRadius;
00583     vec3_t lodOrigin;
00584 
00585     oldheight = 0;
00586     width = grid->width;
00587     height = grid->height + 1;
00588     if (height > MAX_GRID_SIZE)
00589         return NULL;
00590     for (i = 0; i < height; i++) {
00591         if (i == row) {
00592             //insert new row
00593             for (j = 0; j < grid->width; j++) {
00594                 LerpDrawVert( &grid->verts[(i-1) * grid->width + j], &grid->verts[i * grid->width + j], &ctrl[i][j] );
00595                 if (j == column)
00596                     VectorCopy(point, ctrl[i][j].xyz);
00597             }
00598             errorTable[1][i] = loderror;
00599             continue;
00600         }
00601         errorTable[1][i] = grid->heightLodError[oldheight];
00602         for (j = 0; j < grid->width; j++) {
00603             ctrl[i][j] = grid->verts[oldheight * grid->width + j];
00604         }
00605         oldheight++;
00606     }
00607     for (j = 0; j < grid->width; j++) {
00608         errorTable[0][j] = grid->widthLodError[j];
00609     }
00610     // put all the aproximating points on the curve
00611     //PutPointsOnCurve( ctrl, width, height );
00612     // calculate normals
00613     MakeMeshNormals( width, height, ctrl );
00614 
00615     VectorCopy(grid->lodOrigin, lodOrigin);
00616     lodRadius = grid->lodRadius;
00617     // free the old grid
00618     R_FreeSurfaceGridMesh(grid);
00619     // create a new grid
00620     grid = R_CreateSurfaceGridMesh( width, height, ctrl, errorTable );
00621     grid->lodRadius = lodRadius;
00622     VectorCopy(lodOrigin, grid->lodOrigin);
00623     return grid;
00624 }

Generated on Thu Aug 25 12:37:51 2005 for Quake III Arena by  doxygen 1.3.9.1