Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

virtualtex.cpp

Go to the documentation of this file.
00001 // virtualtex.cpp
00002 //
00003 // Copyright (C) 2003, Chris Laurel
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <string>
00011 #include <iostream>
00012 #include <fstream>
00013 #include <cmath>
00014 #include <cassert>
00015 #include <cstdio>
00016 #include "celutil/debug.h"
00017 #include "celutil/directory.h"
00018 #include "virtualtex.h"
00019 #include "gl.h"
00020 #include "parser.h"
00021 
00022 using namespace std;
00023 
00024 static const int MaxResolutionLevels = 11;
00025 
00026 
00027 // Virtual textures are composed of tiles that are loaded from the hard drive
00028 // as they become visible.  Hidden tiles may be evicted from graphics memory
00029 // to make room for other tiles when they become visible.
00030 //
00031 // The virtual texture consists of one or more levels of detail.  Each level
00032 // of detail is twice as wide and twice as high as the previous one, therefore
00033 // having four times as many tiles.  The height and width of each LOD must be
00034 // a power of two, with width = 2 * height.  The baseSplit determines the
00035 // number of tiles at the lowest LOD.  It is the log base 2 of the width in
00036 // tiles of LOD zero.  Though it's not required
00037 
00038 static bool isPow2(int x)
00039 {
00040     return ((x & (x - 1)) == 0);
00041 }
00042 
00043 
00044 #if 0
00045 // Useful if we want to use a packed quadtree to store tiles instead of
00046 // the currently implemented tree structure.
00047 static inline uint lodOffset(uint lod)
00048 {
00049     return ((1 << (lod << 1)) - 1) & 0xaaaaaaaa;
00050 }
00051 #endif
00052 
00053 
00054 VirtualTexture::VirtualTexture(const string& _tilePath,
00055                                unsigned int _baseSplit,
00056                                unsigned int _tileSize,
00057                                const string& _tilePrefix,
00058                                const string& _tileType) :
00059     Texture(_tileSize << (_baseSplit + 1), _tileSize << _baseSplit),
00060     tilePath(_tilePath),
00061     baseSplit(_baseSplit),
00062     tilePrefix(_tilePrefix),
00063     tileSize(_tileSize),
00064     ticks(0),
00065     nResolutionLevels(0)
00066 {
00067     assert(tileSize != 0 && isPow2(tileSize));
00068     tileTree[0] = new TileQuadtreeNode();
00069     tileTree[1] = new TileQuadtreeNode();
00070     tileExt = string(".") + _tileType;
00071     populateTileTree();
00072 }
00073 
00074 
00075 VirtualTexture::~VirtualTexture()
00076 {
00077 }
00078 
00079 
00080 const TextureTile VirtualTexture::getTile(int lod, int u, int v)
00081 {
00082     tilesRequested++;
00083 #if 0
00084     cout << "getTile(" << lod << ", " << u << ", " << v << ")\n";
00085 #endif
00086 
00087     lod += baseSplit;
00088     
00089     if (lod < 0 || (uint) lod >= nResolutionLevels || 
00090         u < 0 || u >= (2 << lod) ||
00091         v < 0 || v >= (1 << lod))
00092     {
00093         return TextureTile(0);
00094     }
00095     else
00096     {
00097         TileQuadtreeNode* node = tileTree[u >> lod];
00098         Tile* tile = node->tile;
00099         uint tileLOD = 0;
00100 
00101         for (int n = 0; n < lod; n++)
00102         {
00103             uint mask = 1 << (lod - n - 1);
00104             uint child = (((v & mask) << 1) | (u & mask)) >> (lod - n - 1);
00105             //int child = (((v << 1) | u) >> (lod - n - 1)) & 3;
00106             if (node->children[child] == NULL)
00107             {
00108                 break;
00109             }
00110             else
00111             {
00112                 node = node->children[child];
00113                 if (node->tile != NULL)
00114                 {
00115                     tile = node->tile;
00116                     tileLOD = n + 1;
00117                 }
00118             }
00119         }
00120 
00121         // No tile was found at all--not even the base texture was found
00122         if (tile == NULL)
00123         {
00124             return TextureTile(0);
00125         }
00126 
00127         // Make the tile resident.
00128         uint tileU = u >> (lod - tileLOD);
00129         uint tileV = v >> (lod - tileLOD);
00130         makeResident(tile, tileLOD, tileU, tileV);
00131 
00132         // It's possible that we failed to make the tile resident, either
00133         // because the texture file was bad, or there was an unresolvable
00134         // out of memory situation.  In that case there is nothing else to
00135         // do but return a texture tile with a null texture name.
00136         if (tile->tex == NULL)
00137         {
00138             return TextureTile(0);
00139         }
00140         else
00141         {
00142             // Set up the texture subrect to be the entire texture
00143             float texU = 0.0f;
00144             float texV = 0.0f;
00145             float texDU = 1.0f;
00146             float texDV = 1.0f;
00147 
00148             // If the tile came from a lower LOD than the requested one,
00149             // we'll only use a subsection of it.
00150             uint lodDiff = lod - tileLOD;
00151             texDU = texDV = 1.0f / (float) (1 << lodDiff);
00152             texU = (u & ((1 << lodDiff) - 1)) * texDU;
00153             texV = (v & ((1 << lodDiff) - 1)) * texDV;
00154 
00155 #if 0
00156             cout << "Tile: " << tile->tex->getName() << ", " <<
00157                 texU << ", " << texV << ", " << texDU << ", " << texDV << '\n';
00158 #endif
00159             return TextureTile(tile->tex->getName(),
00160                                texU, texV, texDU, texDV);
00161         }
00162     }
00163 }
00164 
00165 
00166 void VirtualTexture::bind()
00167 {
00168     // Treating a virtual texture like an ordinary one will not work; this is a
00169     // weakness in the class hierarchy.
00170 }
00171 
00172 
00173 int VirtualTexture::getLODCount() const
00174 {
00175     return nResolutionLevels - baseSplit;
00176 }
00177 
00178 
00179 int VirtualTexture::getUTileCount(int lod) const
00180 {
00181     return 2 << (lod + baseSplit);
00182 }
00183 
00184 
00185 int VirtualTexture::getVTileCount(int lod) const
00186 {
00187     return 1 << (lod + baseSplit);
00188 }
00189 
00190 
00191 void VirtualTexture::beginUsage()
00192 {
00193     ticks++;
00194     tilesRequested = 0;
00195 }
00196 
00197 
00198 void VirtualTexture::endUsage()
00199 {
00200 }
00201 
00202 
00203 #if 0
00204 uint VirtualTexture::tileIndex(unsigned int lod,
00205                                unsigned int u, unsigned int v)
00206 {
00207     unsigned int lodBase = lodOffset(lod + baseSplit) - lodOffset(baseSplit);
00208     return lodBase + (v << (lod << 1)) + u;
00209 }
00210 #endif
00211 
00212 
00213 ImageTexture* VirtualTexture::loadTileTexture(uint lod, uint u, uint v)
00214 {
00215     lod >>= baseSplit;
00216 
00217     assert(lod < (unsigned)MaxResolutionLevels);
00218     
00219     char filename[64];
00220     sprintf(filename, "level%d/%s%d_%d", lod, tilePrefix.c_str(), u, v);
00221     
00222     string pathname = tilePath + filename + tileExt;
00223     Image* img = LoadImageFromFile(pathname);
00224     if (img == NULL)
00225         return NULL;
00226 
00227     ImageTexture* tex = NULL;
00228 
00229     // Only use mip maps for the LOD 0; for higher LODs, the function of mip
00230     // mapping is built into the texture.
00231     MipMapMode mipMapMode = lod == 0 ? DefaultMipMaps : NoMipMaps;
00232 
00233     if (isPow2(img->getWidth()) && isPow2(img->getHeight()))
00234         tex = new ImageTexture(*img, EdgeClamp, mipMapMode);
00235     delete img;
00236 
00237     return tex;
00238 }
00239 
00240 
00241 void VirtualTexture::makeResident(Tile* tile, uint lod, uint u, uint v)
00242 {
00243     if (tile->tex == NULL && !tile->loadFailed)
00244     {
00245         // Potentially evict other tiles in order to make this one fit
00246         tile->tex = loadTileTexture(lod, u, v);
00247         if (tile->tex == NULL)
00248         {
00249             // cout << "Texture load failed!\n";
00250             tile->loadFailed = true;
00251         }
00252     }
00253 }
00254 
00255 
00256 void VirtualTexture::populateTileTree()
00257 {
00258     // Count the number of resolution levels present
00259     uint maxLevel = 0;
00260 
00261     // Crash potential if the tile prefix contains a %, so disallow it
00262     string pattern;
00263     if (tilePrefix.find('%') == string::npos)
00264         pattern = tilePrefix + "%d_%d.";
00265 
00266     for (int i = 0; i < MaxResolutionLevels; i++)
00267     {
00268         char filename[32];
00269         sprintf(filename, "level%d", i);
00270         if (IsDirectory(tilePath + filename))
00271         {
00272             Directory* dir = OpenDirectory(tilePath + filename);
00273             if (dir != NULL)
00274             {
00275                 maxLevel = i + baseSplit;
00276                 int uLimit = 2 << maxLevel;
00277                 int vLimit = 1 << maxLevel;
00278                 
00279                 string filename;
00280                 while (dir->nextFile(filename))
00281                 {
00282                     int u = -1, v = -1;
00283                     if (sscanf(filename.c_str(), pattern.c_str(), &u, &v) == 2)
00284                     {
00285                         if (u >= 0 && v >= 0 && u < uLimit && v < vLimit)
00286                         {
00287                             // Found a tile, so add it to the quadtree
00288                             Tile* tile = new Tile();
00289                             addTileToTree(tile, maxLevel, (uint) u, (uint) v);
00290                         }
00291                     }
00292                 }
00293             }
00294         }
00295     }
00296 
00297     nResolutionLevels = maxLevel + 1;
00298 }
00299 
00300 
00301 void VirtualTexture::addTileToTree(Tile* tile, uint lod, uint u, uint v)
00302 {
00303     TileQuadtreeNode* node = tileTree[u >> lod];
00304 
00305     for (uint i = 0; i < lod; i++)
00306     {
00307         uint mask = 1 << (lod - i - 1);
00308         uint child = (((v & mask) << 1) | (u & mask)) >> (lod - i - 1);
00309         if (node->children[child] == NULL)
00310             node->children[child] = new TileQuadtreeNode();
00311         node = node->children[child];
00312     }
00313 #if 0
00314     clog << "addTileToTree: " << node << ", " << lod << ", " << u << ", " << v << '\n';
00315 #endif
00316 
00317     // Verify that the tile doesn't already exist
00318     if (node->tile == NULL)
00319         node->tile = tile;
00320 }
00321 
00322 
00323 static VirtualTexture* CreateVirtualTexture(Hash* texParams,
00324                                             const string& path)
00325 {
00326     string imageDirectory;
00327     if (!texParams->getString("ImageDirectory", imageDirectory))
00328     {
00329         DPRINTF(0, "ImageDirectory missing in virtual texture.\n");
00330         return NULL;
00331     }
00332 
00333     double baseSplit = 0.0;
00334     if (!texParams->getNumber("BaseSplit", baseSplit) ||
00335         baseSplit < 0.0 || baseSplit != floor(baseSplit))
00336     {
00337         DPRINTF(0, "BaseSplit in virtual texture missing or has bad value\n");
00338         return NULL;
00339     }
00340 
00341     double tileSize = 0.0;
00342     if (!texParams->getNumber("TileSize", tileSize))
00343     {
00344         DPRINTF(0, "TileSize is missing from virtual texture\n");
00345         return NULL;
00346     }
00347 
00348     if (tileSize != floor(tileSize) ||
00349         tileSize < 64.0 ||
00350         !isPow2((int) tileSize))
00351     {
00352         DPRINTF(0, "Virtual texture tile size must be a power of two >= 64\n");
00353         return NULL;
00354     }
00355 
00356     string tileType = "dds";
00357     texParams->getString("TileType", tileType);
00358 
00359     string tilePrefix = "tx_";
00360     texParams->getString("TilePrefix", tilePrefix);
00361 
00362     return new VirtualTexture(path + "/" + imageDirectory + "/",
00363                               (unsigned int) baseSplit,
00364                               (unsigned int) tileSize,
00365                               tilePrefix,
00366                               tileType);
00367 
00368     return NULL;
00369 }
00370 
00371 
00372 static VirtualTexture* LoadVirtualTexture(istream& in, const string& path)
00373 {
00374     Tokenizer tokenizer(&in);
00375     Parser parser(&tokenizer);
00376 
00377     if (tokenizer.nextToken() != Tokenizer::TokenName)
00378         return NULL;
00379 
00380     string virtTexString = tokenizer.getNameValue();
00381     if (virtTexString != "VirtualTexture")
00382         return NULL;
00383 
00384     Value* texParamsValue = parser.readValue();
00385     if (texParamsValue == NULL || texParamsValue->getType() != Value::HashType)
00386     {
00387         DPRINTF(0, "Error parsing virtual texture\n");
00388         return NULL;
00389     }
00390 
00391     Hash* texParams = texParamsValue->getHash();
00392     
00393     return CreateVirtualTexture(texParams, path);
00394 }
00395 
00396 
00397 VirtualTexture* LoadVirtualTexture(const string& filename)
00398 {
00399     ifstream in(filename.c_str(), ios::in);
00400 
00401     if (!in.good())
00402     {
00403         //DPRINTF(0, "Error opening virtual texture file: %s\n", filename.c_str());
00404         return NULL;
00405     }
00406 
00407     // Strip off every character including and after the final slash to get
00408     // the pathname.
00409     string path = ".";
00410     string::size_type pos = filename.rfind('/');
00411     if (pos != string::npos)
00412         path = string(filename, 0, pos);
00413 
00414     return LoadVirtualTexture(in, path);
00415 }

Generated on Sat Jan 14 22:30:29 2006 for Celestia by  doxygen 1.4.1