00001 /* 00002 * TerrainManager.cpp 00003 * 00004 * Part of Fly! Legacy project 00005 * 00006 * Copyright 2003-2005 Chris Wallace 00007 * 00008 * Fly! Legacy is free software; you can redistribute it and/or modify 00009 * it under the terms of the GNU General Public License as published by 00010 * the Free Software Foundation; either version 2 of the License, or 00011 * (at your option) any later version. 00012 * 00013 * Fly! Legacy is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00016 * GNU General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU General Public License 00019 * along with Fly! Legacy; if not, write to the Free Software 00020 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00021 * 00022 */ 00023 00032 #include "../Include/Globals.h" 00033 #include "../Include/Terrain.h" 00034 #include "../Include/Ui.h" 00035 #include "../Include/LogFile.h" 00036 00037 using namespace std; 00038 00039 // 00040 // CTerrainManager 00041 // 00042 CTerrainManager::CTerrainManager (void) 00043 { 00044 int i; 00045 00046 // Instantiate SSG root for all terrain 00047 root = new ssgRoot; 00048 root->setName ("TerrainRoot"); 00049 00050 top = new ssgTransform; 00051 top->setName ("TerrainTop"); 00052 root->addKid (top); 00053 00054 // Open log file if INI setting is enabled 00055 log = NULL; 00056 i = 0; 00057 GetIniVar ("Logs", "logTerrainManager", &i); 00058 if (i != 0) { 00059 log = new CLogFile ("Logs\\TerrainManager.log", "w"); 00060 } 00061 00062 // Get terrain debug level 00063 debugLevel = 0; 00064 GetIniVar ("Debug", "terrainDebugLevel", &debugLevel); 00065 00066 // Instantiate elevation database 00067 tedb = new CTileElevationDatabase; 00068 00069 // Instantiate terrain type database 00070 ttdb = new CTerrainTypeDatabase; 00071 00072 // Instantiate default texture database 00073 dtdb = new CDefaultTextureDatabase; 00074 00075 // Instantiate scenery models database 00076 smdb = new CSceneryModelDatabase; 00077 00078 // Instantiate transition mask database 00079 tmdb = new CTransitionMaskDatabase; 00080 00081 // Instantiate water mask database 00082 wmdb = new CWaterMaskDatabase; 00083 00084 // Instantiate scenery set database 00085 ssdb = new CScenerySetDatabase; 00086 00087 // Update visibility settings from INI 00088 UpdateMaxVisibility (); 00089 UpdateMediumDetailRange (); 00090 UpdateHighDetailRange (); 00091 00092 // WARNING : Cannot instantiate any terrain tile class instances here, since 00093 // CTerrainManager must be instantiated and assigned to the globals in order 00094 // for the tile classes to get the terrain debug level. 00095 00096 // Set 'last' QGT indices so that first call to SetPosition will trigger a transition 00097 xLast = zLast = -1; 00098 } 00099 00100 00101 CTerrainManager::~CTerrainManager (void) 00102 { 00103 Log ("TerrainManager destructor starting\n"); 00104 00105 root->removeAllKids(); 00106 delete root; 00107 00108 // Delete CQuarterGlobeTiles 00109 set<CQuarterGlobeTile*>::iterator i; 00110 for (i=qFree.begin(); i!=qFree.end(); i++) { 00111 CQuarterGlobeTile *qgt = *i; 00112 if (qgt != NULL) ssgDeRefDelete (qgt); 00113 } 00114 for (i=qBusy.begin(); i!=qBusy.end(); i++) { 00115 CQuarterGlobeTile *qgt = *i; 00116 if (qgt != NULL) ssgDeRefDelete (qgt); 00117 } 00118 00119 // Delete elevation database 00120 delete tedb; 00121 tedb = NULL; 00122 00123 // Delete terrain type database 00124 delete ttdb; 00125 ttdb = NULL; 00126 00127 // Delete default texture database 00128 delete dtdb; 00129 dtdb = NULL; 00130 00131 // Delete scenery models database 00132 delete smdb; 00133 smdb = NULL; 00134 00135 // Delete transition mask database 00136 delete tmdb; 00137 tmdb = NULL; 00138 00139 // Delete water mask database 00140 delete wmdb; 00141 wmdb = NULL; 00142 00143 // Delete scenery set database 00144 delete ssdb; 00145 ssdb = NULL; 00146 00147 // Shut down log file if created 00148 if (log != NULL) { 00149 delete log; 00150 } 00151 } 00152 00153 void CTerrainManager::SetCamera (SPosition pos, SPosition lookat, SVector orient) 00154 { 00155 /* 00156 char debug[80]; 00157 sprintf (debug, "Eye pos : lat=%f lon=%f alt=%f scale=%f", 00158 pos.lat, pos.lon, pos.alt, scale); 00159 DrawNoticeToUser (debug, 1); 00160 */ 00161 00162 // Set eye position 00163 SVector v; 00164 v = PosToScaledFlatCartesianQgt (pos); 00165 sgVec3 eye; 00166 sgSetVec3 (eye, v.x, v.y, v.z); 00167 00168 // Set lookat position 00169 v = PosToScaledFlatCartesianQgt (lookat); 00170 sgVec3 tgt; 00171 sgSetVec3 (tgt, v.x, v.y, v.z); 00172 00173 // Set up vector 00174 sgVec3 up; 00175 sgSetVec3 (up, 0, 0, 1); 00176 00177 ssgSetCameraLookAt (eye, tgt, up); 00178 00179 // Set clipping distance to visibility limit 00181 float scale = TerrainScale (pos); 00182 float nearClip = 25.0f; 00183 float visScaled = vis_feet / scale; 00184 // float farClip = sqrt ((visScaled * visScaled) + (pos.alt * pos.alt)); 00185 float farClip = visScaled * 1.2f; 00186 ssgSetNearFar (nearClip, farClip); 00187 } 00188 00189 /* 00190 void CTerrainManager::LoadQuarterGlobeTile (int x, int z, SPosition base) 00191 { 00192 bool loaded = false; 00193 00194 // Check to see if QGT is already instantiated 00195 00196 // If so, then reset the translation matrix for the new base position 00197 00198 // Instantiate the QGT 00199 00200 // Check QGT is already loaded 00201 vector<CQuarterGlobeTile*>::iterator i; 00202 for (i=qgtPool.begin(); i!=qgtPool.end(); i++) { 00203 CQuarterGlobeTile *qgt = *i; 00204 if (qgt->IsAssigned ()) { 00205 qgt->GetIndices (&qx, &qz); 00206 if ( 00207 qgt->AssignIndices (x, z); 00208 } 00209 } 00210 int n = qgtList->getNumEntities(); 00211 for (int i=0; i<n; i++) { 00212 CGlobeTile *globetile = (CGlobeTile *) gtList->getEntity(i); 00213 if ((globetile->GetX() == x) && (globetile->GetZ() == z)) { 00214 // Reset translation matrix of the GT for the new base position 00215 loaded = true; 00216 break; 00217 } 00218 } 00219 00220 // Instantiate new globe tile 00221 if (!loaded) { 00222 CGlobeTile *globetile = new CGlobeTile (x, z); 00223 if (globetile != NULL) { 00224 // Translate from base coordinates 00225 gtList->addEntity (globetile); 00226 // top->addKid (globetile->GetSSGEntity()); 00227 } else { 00228 gtfo ("CTerrainManager::LoadGlobeTile : Bad globe tile %d,%d", x, z); 00229 } 00230 } 00231 } 00232 */ 00233 00234 00235 void CTerrainManager::Prepare (void) 00236 { 00237 // Instantiate pool of QGTs 00238 int i; 00239 for (i=0; i<25; i++) { 00240 CQuarterGlobeTile *qgt = new CQuarterGlobeTile; 00241 qFree.insert (qgt); 00242 } 00243 00244 // Set initial user position 00245 SPosition pos = globals->sit->GetUserVehicle()->GetPosition(); 00246 SetPosition (pos); 00247 } 00248 00249 #define QGT_INDEX(x,z) (unsigned long)(x << 16 | z) 00250 00251 void CTerrainManager::SetPosition (SPosition pos) 00252 { 00253 DEBUGLOG ("CTerrainManager::SetPosition lat=%f lon=%f", pos.lat, pos.lon); 00254 00255 // Get QGT indices for new position 00256 int x, z; 00257 lat_lon_to_qgt (pos.lat, pos.lon, x, z); 00258 00259 // If current QGT has changed, update QGT array pointers 00260 if ((x != xLast) || (z != zLast)) { 00261 set<unsigned long> setVisible; 00262 xLast = x; 00263 zLast = z; 00264 00265 // Determine set of visible QGT indices 00266 int i, j; 00267 for (i=0; i<5; i++) { 00268 for (j=0; j<5; j++) { 00269 unsigned int qx = x + i - 2; 00270 unsigned int qz = z + j - 2; 00271 unsigned long index = QGT_INDEX (qx, qz); 00272 setVisible.insert (index); 00273 } 00274 } 00275 00276 // Iterate over all loaded QGTs, removing any which are not potentially 00277 // visible, and removing from the visible set any which are already loaded 00278 set<CQuarterGlobeTile*>::iterator qIter = qBusy.begin(); 00279 while (qIter != qBusy.end()) { 00280 CQuarterGlobeTile *qgt = *qIter; 00281 unsigned int qx, qz; 00282 qgt->GetIndices (qx, qz); 00283 unsigned long index = QGT_INDEX (qx, qz); 00284 set<unsigned long>::iterator sIter = setVisible.find(index); 00285 if (sIter == setVisible.end()) { 00286 // This QGT is not in the potentially visible set; destroy it 00287 qgt->Destroy(); 00288 qgt->UnassignIndices (); 00289 set<CQuarterGlobeTile*>::iterator eraseIter = qIter; 00290 qIter++; 00291 qBusy.erase (eraseIter); 00292 qFree.insert (qgt); 00293 // top->removeKid (qgt); 00294 } else { 00295 // This QGT is already in the potentially visible set. Remove the indices 00296 // from the visible set since we don't need to check for it anymore 00297 setVisible.erase (sIter); 00298 qIter++; 00299 } 00300 } 00301 00302 // All remaining members of the visible set must be loaded 00303 set<unsigned long>::iterator sIter; 00304 for (sIter=setVisible.begin(); sIter!=setVisible.end(); sIter++) { 00305 unsigned long index = *sIter; 00306 unsigned int qx = (index & 0xFFFF0000) >> 16; 00307 unsigned int qz = index & 0x0000FFFF; 00308 00309 // Get a QTR from the free set and move it to the busy set 00310 if (qFree.size() == 0) { 00311 gtfo ("CTerrainManager::SetPosition : No free QGT available"); 00312 } 00313 set<CQuarterGlobeTile*>::iterator qIter = qFree.begin(); 00314 CQuarterGlobeTile *qgt = *qIter; 00315 qFree.erase (qIter); 00316 qBusy.insert (qgt); 00317 00318 // Assign new indices and create geometry 00319 qgt->AssignIndices (qx, qz); 00320 qgt->Create(); 00321 top->addKid (qgt); 00322 } 00323 } 00324 00325 // Update position for all loaded QGTs 00326 set<CQuarterGlobeTile*>::iterator i; 00327 for (i=qBusy.begin(); i!=qBusy.end(); i++) { 00328 CQuarterGlobeTile *qgt = *i; 00329 if (qgt->IsAssigned()) qgt->UpdatePosition (pos); 00330 } 00331 00332 // Update top-level scaling 00333 float scale = TerrainScale (pos); 00334 sgMat4 S; 00335 sgMakeIdentMat4 (S); 00336 S[0][0] = scale; 00337 S[1][1] = scale; 00338 top->setTransform (S); 00339 } 00340 00341 00342 // 00343 // Return the maximum terrain range in statute miles 00344 // 00345 float CTerrainManager::GetMaxVisibility (void) 00346 { 00347 return (visibility); 00348 } 00349 00350 // 00351 // Return the range for medium-detail terrain textures in statute miles 00352 // 00353 float CTerrainManager::GetMediumDetailRange (void) 00354 { 00355 return (medium_detail); 00356 } 00357 00358 // 00359 // Return the range for high-detail terrain textures in statute miles 00360 // 00361 float CTerrainManager::GetHighDetailRange (void) 00362 { 00363 return (high_detail); 00364 } 00365 00366 void CTerrainManager::UpdateMaxVisibility (void) 00367 { 00368 // Get maximum visibility from INI setting 00369 float miles = 20; 00370 GetIniFloat ("Graphics", "maxUserVisibility", &miles); 00371 00372 // Store visibility in feet 00373 visibility = miles; 00374 vis_feet = visibility * FEET_PER_MILE; 00375 vis_check = vis_feet * 1.5f; 00376 } 00377 00378 void CTerrainManager::UpdateMediumDetailRange (void) 00379 { 00380 // Get medium detail texture range from INI setting 00381 float miles = 0; 00382 GetIniFloat ("Graphics", "medDetailRadius", &miles); 00383 00384 // Store medium detail radius in feet 00385 medium_detail = miles; 00386 } 00387 00388 void CTerrainManager::UpdateHighDetailRange (void) 00389 { 00390 // Get high detail texture range from INI setting 00391 float miles = 0; 00392 GetIniFloat ("Graphics", "highDetailRadius", &miles); 00393 00394 // Store medium detail radius in feet 00395 high_detail = miles; 00396 } 00397 00398 void CTerrainManager::Draw (void) 00399 { 00400 // Get user position, this is the origin point for the rendered world 00401 SPosition pos = globals->sit->GetUserVehicle()->GetPosition(); 00402 float scale = TerrainScale (pos); 00403 SetPosition (pos); 00404 00405 // Wireframe mode for testing 00406 if (globals->settings->terrainWireframe) { 00407 glPushAttrib (GL_ENABLE_BIT | GL_FOG_BIT | GL_TRANSFORM_BIT | GL_POLYGON_BIT); 00408 glPolygonMode( GL_FRONT_AND_BACK, GL_LINE ); 00409 } else { 00410 glPushAttrib (GL_ENABLE_BIT | GL_FOG_BIT | GL_TRANSFORM_BIT); 00411 } 00412 00413 // Enable fog 00414 00416 00420 00421 glEnable (GL_FOG); 00422 GLint fogMode = GL_EXP; 00423 glFogi (GL_FOG_MODE, fogMode); 00424 GLfloat fogColour[4] = {1.0, 1.0, 1.0, 1.0}; 00425 glFogfv (GL_FOG_COLOR, fogColour); 00426 glFogf (GL_FOG_DENSITY, 5.0E-6); 00427 glHint (GL_FOG_HINT, GL_DONT_CARE); 00428 glFogf (GL_FOG_START, 20.0); 00429 glFogf (GL_FOG_END, (vis_feet / scale) * 1.2f); 00430 00431 glEnable (GL_DEPTH_TEST); 00432 00434 00435 // Draw terrain root 00436 ssgCullAndDraw (root); 00437 00438 // Restore GL attributes 00439 glPopAttrib (); 00440 } 00441 00442 void CTerrainManager::Log (const char *fmt, ...) 00443 { 00444 if (log) { 00445 va_list argp; 00446 va_start(argp, fmt); 00447 log->Write (fmt, argp); 00448 va_end(argp); 00449 } 00450 } 00451 00452 void CTerrainManager::Print (FILE* f) 00453 { 00454 root->print (f, " ", 4); 00455 } 00456 00457 /********************************************************* 00458 * Experimental code to export very large terrain area 00459 * textures for debugging of algorithms. These are not 00460 * currently being used, they are candidates for eventual 00461 * removal. 00462 00463 // 00464 // Custom error handling for JPEG library routines 00465 // 00466 struct my_error_mgr { 00467 struct jpeg_error_mgr pub; 00468 jmp_buf setjmp_buffer; 00469 }; 00470 00471 typedef struct my_error_mgr * my_error_ptr; 00472 00473 METHODDEF(void) 00474 my_error_exit (j_common_ptr cinfo) 00475 { 00476 // cinfo->err really points to a my_error_mgr struct, so coerce pointer 00477 my_error_ptr myerr = (my_error_ptr) cinfo->err; 00478 00479 // Always display the message. 00480 // We could postpone this until after returning, if we chose. 00481 (*cinfo->err->output_message) (cinfo); 00482 00483 // Return control to the setjmp point 00484 longjmp(myerr->setjmp_buffer, 1); 00485 } 00486 00487 00488 void CTerrainManager::ExportElevationTileTexture (int tile) 00489 { 00490 int resolution = 8192; 00491 00492 // Determine globe tile x, z indices of south-west corner in elevation tile 00493 int base_gt_x = (tile & 0x1F) * 8; 00494 int base_gt_z = (32 - ((tile >> 5) & 0x1F) - 1) * 8; 00495 00496 // Declare storage for the complete bitmap...192 MB!! 00497 GLubyte *image = new GLubyte[resolution * resolution * 3]; 00498 00499 // Loop over the 8x8 globe tile grid 00500 for (int gz = 7; gz >= 0; gz--) { 00501 int gt_z = base_gt_z + gz; 00502 for (int gx = 0; gx < 8; gx++) { 00503 int gt_x = base_gt_x + gx; 00504 00505 // Loop over the 64x64 grid of detail tiles in the globe tile 00506 for (int z = 63; z >= 0; z--) { 00507 int dt_z = (gt_z * 64) + z; 00508 for (int x = 0; x < 64; x++) { 00509 int dt_x = (gt_x * 64) + x; 00510 // Get raw image data for the detail tile 00511 00512 // TEMP : Comment out code to get fully transitioned detail tile 00513 // CRawImage *r = GetDetailTileTexture (dt_x, dt_z, TILE_DETAIL_LOW); 00514 00515 // TEMP : Just get basic terrain tile without transitioning 00516 ETerrainType tt = ttdb->GetTerrainType (dt_x, dt_z); 00517 CRawImage *r = new CRawImage (*(dtdb->GetRawImage (dt_x, dt_z, tt, TILE_DETAIL_LOW))); 00518 00519 GLubyte *dt_image = r->GetRGBImageData(); 00520 int dt_span = (64 * 3); 00521 00522 // Transfer/compress detail tile to elevation tile texture images 00523 // Don't invert z-loop since we are copying from north-up image 00524 // data in the detail tile texture to north-up image data 00525 // in the elevation texture 00526 int scale = 16; 00527 for (int sz = 0; sz < scale; sz++) { 00528 for (int sx = 0; sx < scale; sx++) { 00529 // Calculate pixel colour by averaging detail tile texels 00530 int pixelscale = sqrt(scale); 00531 GLuint r = 0; 00532 GLuint g = 0; 00533 GLuint b = 0; 00534 00535 // TEMP, just take one pixel for testing 00536 int detailRow = (sz * 4); 00537 int detailCol = (sx * 4); 00538 int iDetail; 00539 // int iDetail = ((detailRow * 64) + detailCol) * 3; 00540 // r = dt_image[iDetail + 0]; 00541 // g = dt_image[iDetail + 1]; 00542 // b = dt_image[iDetail + 2]; 00543 00544 for (int pz = 0; pz < pixelscale; pz++) { 00545 for (int px = 0; px < pixelscale; px++) { 00546 iDetail = ((detailRow * 64) + detailCol) * 3; 00547 r += dt_image[iDetail + 0]; 00548 g += dt_image[iDetail + 1]; 00549 b += dt_image[iDetail + 2]; 00550 detailCol++; 00551 } 00552 detailRow++; 00553 } 00554 00555 r /= scale; 00556 g /= scale; 00557 b /= scale; 00558 00559 // Store in elevation tile texel 00560 int imageRow = ((7 - gz) * 1024) + ((63 - z) * 16) + (15 - sz); 00561 int imageCol = (gx * 1024) + (x * 16) + sx; 00562 int iImage = ((imageRow * resolution) + imageCol) * 3; 00563 image[iImage + 0] = r; 00564 image[iImage + 1] = g; 00565 image[iImage + 2] = b; 00566 } 00567 } 00568 00569 // Free detail tile image data 00570 delete dt_image; 00571 delete r; 00572 } 00573 } 00574 00575 // Purge detail tile database to free memory 00576 dtdb->Purge (); 00577 } 00578 } 00579 00580 // Create the JPG context 00581 struct jpeg_compress_struct cinfo; 00582 00583 // Set error handler 00584 struct my_error_mgr jerr; 00585 00586 // Format the .jpg filename 00587 char jpgFilename[80]; 00588 sprintf (jpgFilename, "ET%03X.jpg", tile); 00589 00590 // Open the .jpg file for writing as a binary file 00591 FILE *fJpg = fopen (jpgFilename, "wb"); 00592 if (!fJpg) { 00593 // File open failed...display error message to user 00594 DrawNoticeToUser ("ExportElevationTileTexture : Could not open output .jpg file", 5); 00595 return; 00596 } 00597 00598 // We set up the normal JPEG error routines, then override error_exit. 00599 cinfo.err = jpeg_std_error(&jerr.pub); 00600 jerr.pub.error_exit = my_error_exit; 00601 // Establish the setjmp return context for my_error_exit to use. 00602 if (setjmp(jerr.setjmp_buffer)) { 00603 // If we get here, the JPEG code has signaled an error. 00604 // We need to clean up the JPEG object, close the input file, and return. 00605 jpeg_destroy_compress(&cinfo); 00606 fclose(fJpg); 00607 return; 00608 } 00609 00610 // Create compress context 00611 jpeg_create_compress (&cinfo); 00612 00613 // Assign the file as the output target for the compress context 00614 jpeg_stdio_dest (&cinfo, fJpg); 00615 00616 // Set parameters for compression 00617 cinfo.image_width = resolution; 00618 cinfo.image_height = resolution; 00619 cinfo.input_components = 3; 00620 cinfo.in_color_space = JCS_RGB; 00621 jpeg_set_defaults (&cinfo); 00622 cinfo.dct_method = JDCT_ISLOW; 00623 jpeg_set_quality (&cinfo, 50, TRUE); 00624 jpeg_start_compress (&cinfo, TRUE); 00625 00626 for (int i=0; i<resolution; i++) { 00627 JSAMPROW pRow[1]; 00628 pRow[0] = &image[i * resolution * 3]; 00629 jpeg_write_scanlines (&cinfo, pRow, 1); 00630 } 00631 00632 // Finish and clean up 00633 jpeg_finish_compress (&cinfo); 00634 jpeg_destroy_compress (&cinfo); 00635 fclose (fJpg); 00636 00637 delete image; 00638 } 00639 00640 00641 void CTerrainManager::ExportGlobeTileTexture (int x, int z, ETileDetail detail) 00642 { 00643 int tile_resolution; 00644 int tile_overlap; 00645 switch (detail) { 00646 case TILE_DETAIL_LOW: 00647 tile_resolution = 64; 00648 tile_overlap = 0; 00649 break; 00650 00651 case TILE_DETAIL_MEDIUM: 00652 tile_resolution = 128; 00653 tile_overlap = 8; 00654 break; 00655 00656 case TILE_DETAIL_HIGH: 00657 tile_resolution = 256; 00658 tile_overlap = 4; 00659 break; 00660 00661 default: 00662 tile_resolution = 64; 00663 tile_overlap = 0; 00664 break; 00665 } 00666 00667 int extract_resolution = tile_resolution - (2 * tile_overlap); 00668 int resolution = extract_resolution * 64; 00669 00670 // Create the JPG context 00671 struct jpeg_compress_struct cinfo; 00672 00673 // Set error handler 00674 struct my_error_mgr jerr; 00675 00676 // Format the .jpg filename 00677 char jpgFilename[80]; 00678 sprintf (jpgFilename, "GT%03d%03d_%d.jpg", x, z, resolution); 00679 00680 // Open the .jpg file for writing as a binary file 00681 FILE *fJpg = fopen (jpgFilename, "wb"); 00682 if (!fJpg) { 00683 // File open failed...display error message to user 00684 DrawNoticeToUser ("Export Globe Tile Texture : Could not open output .jpg file", 5); 00685 return; 00686 } 00687 00688 // We set up the normal JPEG error routines, then override error_exit. 00689 cinfo.err = jpeg_std_error(&jerr.pub); 00690 jerr.pub.error_exit = my_error_exit; 00691 // Establish the setjmp return context for my_error_exit to use. 00692 if (setjmp(jerr.setjmp_buffer)) { 00693 // If we get here, the JPEG code has signaled an error. 00694 // We need to clean up the JPEG object, close the input file, and return. 00695 jpeg_destroy_compress(&cinfo); 00696 fclose(fJpg); 00697 return; 00698 } 00699 00700 // Create compress context 00701 jpeg_create_compress (&cinfo); 00702 00703 // Assign the file as the output target for the compress context 00704 jpeg_stdio_dest (&cinfo, fJpg); 00705 00706 // Set parameters for compression 00707 cinfo.image_width = resolution; 00708 cinfo.image_height = resolution; 00709 cinfo.input_components = 3; 00710 cinfo.in_color_space = JCS_RGB; 00711 jpeg_set_defaults (&cinfo); 00712 cinfo.dct_method = JDCT_ISLOW; 00713 jpeg_set_quality (&cinfo, 50, TRUE); 00714 jpeg_start_compress (&cinfo, TRUE); 00715 00716 // Allocate storage for a single scan line 00717 int rowStride = resolution * 3; 00718 BYTE *row = new BYTE[rowStride]; 00719 00720 // Each GT consists of a 64x64 grid of detail tiles, processed row by row 00721 // from north to south. 00722 CRawImage* raw[64][64]; 00723 GLubyte* rowImage[64][64]; 00724 int xCount, zCount; 00725 for (zCount = 63; zCount >= 0; zCount--) { 00726 // Get first row of 64 detail tiles 00727 int dt_z = (z * 64) + zCount; 00728 00729 for (xCount = 0; xCount < 64; xCount++) { 00730 int dt_x = (x * 64) + xCount; 00731 CRawImage *r = GetDetailTileTexture (dt_x, dt_z, detail); 00732 raw[xCount][zCount] = r; 00733 rowImage[xCount][zCount] = r->GetRGBImageData(); 00734 } 00735 00736 // Construct data for each scan line and send to JPEG compressor 00737 for (int rowCount = 0; rowCount < extract_resolution; rowCount++) 00738 { 00739 int extract_stride = extract_resolution * 3; 00740 int iImage = (rowCount * tile_resolution + tile_overlap) * 3; 00741 00742 // Extract row data from each of the detail textures 00743 for (xCount=0; xCount<64; xCount++) { 00744 GLubyte *image = rowImage[xCount][zCount]; 00745 int iRow = xCount * extract_stride; 00746 memcpy (&row[iRow], &image[iImage], extract_stride); 00747 } 00748 00749 // Send scan line data to JPEG compressor 00750 JSAMPROW pRow[1]; 00751 pRow[0] = row; 00752 jpeg_write_scanlines (&cinfo, pRow, 1); 00753 } 00754 } 00755 00756 // Finish and clean up 00757 jpeg_finish_compress (&cinfo); 00758 jpeg_destroy_compress (&cinfo); 00759 fclose (fJpg); 00760 delete row; 00761 00762 for (zCount=0; zCount<64; zCount++) { 00763 for (xCount=0; xCount<64; xCount++) { 00764 delete rowImage[xCount][zCount]; 00765 delete raw[xCount][zCount]; 00766 } 00767 } 00768 } 00769 00770 */ 00771 00772
|
|
Documentation generated by
|