Panda3D
|
00001 // Filename: texturePool.cxx 00002 // Created by: drose (26Apr00) 00003 // 00004 //////////////////////////////////////////////////////////////////// 00005 // 00006 // PANDA 3D SOFTWARE 00007 // Copyright (c) Carnegie Mellon University. All rights reserved. 00008 // 00009 // All use of this software is subject to the terms of the revised BSD 00010 // license. You should have received a copy of this license along 00011 // with this source code in a file named "LICENSE." 00012 // 00013 //////////////////////////////////////////////////////////////////// 00014 00015 #include "texturePool.h" 00016 #include "config_gobj.h" 00017 #include "config_util.h" 00018 #include "config_express.h" 00019 #include "string_utils.h" 00020 #include "virtualFileSystem.h" 00021 #include "bamCache.h" 00022 #include "bamCacheRecord.h" 00023 #include "pnmFileTypeRegistry.h" 00024 #include "texturePoolFilter.h" 00025 #include "configVariableList.h" 00026 #include "load_dso.h" 00027 #include "mutexHolder.h" 00028 00029 TexturePool *TexturePool::_global_ptr; 00030 00031 //////////////////////////////////////////////////////////////////// 00032 // Function: TexturePool::write 00033 // Access: Published, Static 00034 // Description: Lists the contents of the texture pool to the 00035 // indicated output stream. 00036 // For debugging. 00037 //////////////////////////////////////////////////////////////////// 00038 void TexturePool:: 00039 write(ostream &out) { 00040 get_global_ptr()->ns_list_contents(out); 00041 } 00042 00043 //////////////////////////////////////////////////////////////////// 00044 // Function: TexturePool::register_texture_type 00045 // Access: Public 00046 // Description: Records a factory function that makes a Texture 00047 // object of the appropriate type for one or more 00048 // particular filename extensions. The string 00049 // extensions may be a string that contains 00050 // space-separated list of extensions, case-insensitive. 00051 //////////////////////////////////////////////////////////////////// 00052 void TexturePool:: 00053 register_texture_type(MakeTextureFunc *func, const string &extensions) { 00054 MutexHolder holder(_lock); 00055 00056 vector_string words; 00057 extract_words(downcase(extensions), words); 00058 00059 vector_string::const_iterator wi; 00060 for (wi = words.begin(); wi != words.end(); ++wi) { 00061 _type_registry[*wi] = func; 00062 } 00063 } 00064 00065 //////////////////////////////////////////////////////////////////// 00066 // Function: TexturePool::register_filter 00067 // Access: Public 00068 // Description: Records a TexturePoolFilter object that may operate 00069 // on texture images as they are loaded from disk. 00070 //////////////////////////////////////////////////////////////////// 00071 void TexturePool:: 00072 register_filter(TexturePoolFilter *filter) { 00073 MutexHolder holder(_lock); 00074 00075 gobj_cat.info() 00076 << "Registering Texture filter " << *filter << "\n"; 00077 _filter_registry.push_back(filter); 00078 } 00079 00080 //////////////////////////////////////////////////////////////////// 00081 // Function: TexturePool::get_texture_type 00082 // Access: Public 00083 // Description: Returns the factory function to construct a new 00084 // texture of the type appropriate for the indicated 00085 // filename extension, if any, or NULL if the extension 00086 // is not one of the extensions for a texture file. 00087 //////////////////////////////////////////////////////////////////// 00088 TexturePool::MakeTextureFunc *TexturePool:: 00089 get_texture_type(const string &extension) const { 00090 MutexHolder holder(_lock); 00091 00092 string c = downcase(extension); 00093 TypeRegistry::const_iterator ti; 00094 ti = _type_registry.find(extension); 00095 if (ti != _type_registry.end()) { 00096 return (*ti).second; 00097 } 00098 00099 // Check the PNM type registry. 00100 PNMFileTypeRegistry *pnm_reg = PNMFileTypeRegistry::get_global_ptr(); 00101 PNMFileType *type = pnm_reg->get_type_from_extension(extension); 00102 if (type != (PNMFileType *)NULL) { 00103 // This is a known image type; create an ordinary Texture. 00104 ((TexturePool *)this)->_type_registry[extension] = Texture::make_texture; 00105 return Texture::make_texture; 00106 } 00107 00108 // This is an unknown texture type. 00109 return NULL; 00110 } 00111 00112 //////////////////////////////////////////////////////////////////// 00113 // Function: TexturePool::make_texture 00114 // Access: Public 00115 // Description: Creates a new Texture object of the appropriate type 00116 // for the indicated filename extension, according to 00117 // the types that have been registered via 00118 // register_texture_type(). 00119 //////////////////////////////////////////////////////////////////// 00120 PT(Texture) TexturePool:: 00121 make_texture(const string &extension) const { 00122 MakeTextureFunc *func = get_texture_type(extension); 00123 if (func != NULL) { 00124 return func(); 00125 } 00126 00127 // We don't know what kind of file type this is; return an ordinary 00128 // Texture in case it's an image file with no extension. 00129 return new Texture; 00130 } 00131 00132 //////////////////////////////////////////////////////////////////// 00133 // Function: TexturePool::write_texture_types 00134 // Access: Public 00135 // Description: Outputs a list of the available texture types to the 00136 // indicated output stream. This is mostly the list of 00137 // available image types, with maybe a few additional 00138 // ones for video textures. 00139 //////////////////////////////////////////////////////////////////// 00140 void TexturePool:: 00141 write_texture_types(ostream &out, int indent_level) const { 00142 MutexHolder holder(_lock); 00143 00144 PNMFileTypeRegistry *pnm_reg = PNMFileTypeRegistry::get_global_ptr(); 00145 pnm_reg->write(out, indent_level); 00146 00147 // Also output any of the additional texture types, that aren't 00148 // strictly images (these are typically video textures). 00149 TypeRegistry::const_iterator ti; 00150 for (ti = _type_registry.begin(); ti != _type_registry.end(); ++ti) { 00151 string extension = (*ti).first; 00152 MakeTextureFunc *func = (*ti).second; 00153 00154 if (pnm_reg->get_type_from_extension(extension) == NULL) { 00155 PT(Texture) tex = func(); 00156 string name = tex->get_type().get_name(); 00157 indent(out, indent_level) << name; 00158 indent(out, max(30 - (int)name.length(), 0)) 00159 << " ." << extension << "\n"; 00160 } 00161 } 00162 } 00163 00164 //////////////////////////////////////////////////////////////////// 00165 // Function: TexturePool::get_global_ptr 00166 // Access: Public, Static 00167 // Description: Initializes and/or returns the global pointer to the 00168 // one TexturePool object in the system. 00169 //////////////////////////////////////////////////////////////////// 00170 TexturePool *TexturePool:: 00171 get_global_ptr() { 00172 if (_global_ptr == (TexturePool *)NULL) { 00173 _global_ptr = new TexturePool; 00174 00175 // We have to call this here, not in the constructor, so that the 00176 // _global_ptr is safely assigned by the time the filters begin to 00177 // load. 00178 _global_ptr->load_filters(); 00179 } 00180 return _global_ptr; 00181 } 00182 00183 //////////////////////////////////////////////////////////////////// 00184 // Function: TexturePool::Constructor 00185 // Access: Private 00186 // Description: The constructor is not intended to be called 00187 // directly; there's only supposed to be one TexturePool 00188 // in the universe and it constructs itself. 00189 //////////////////////////////////////////////////////////////////// 00190 TexturePool:: 00191 TexturePool() { 00192 ConfigVariableFilename fake_texture_image 00193 ("fake-texture-image", "", 00194 PRC_DESC("Set this to enable a speedy-load mode in which you don't care " 00195 "what the world looks like, you just want it to load in minimal " 00196 "time. This causes all texture loads via the TexturePool to use " 00197 "the same texture file, which will presumably only be loaded " 00198 "once.")); 00199 _fake_texture_image = fake_texture_image; 00200 } 00201 00202 //////////////////////////////////////////////////////////////////// 00203 // Function: TexturePool::ns_has_texture 00204 // Access: Private 00205 // Description: The nonstatic implementation of has_texture(). 00206 //////////////////////////////////////////////////////////////////// 00207 bool TexturePool:: 00208 ns_has_texture(const Filename &orig_filename) { 00209 MutexHolder holder(_lock); 00210 00211 Filename filename; 00212 resolve_filename(filename, orig_filename); 00213 00214 Textures::const_iterator ti; 00215 ti = _textures.find(filename); 00216 if (ti != _textures.end()) { 00217 // This texture was previously loaded. 00218 return true; 00219 } 00220 00221 return false; 00222 } 00223 00224 //////////////////////////////////////////////////////////////////// 00225 // Function: TexturePool::ns_load_texture 00226 // Access: Private 00227 // Description: The nonstatic implementation of load_texture(). 00228 //////////////////////////////////////////////////////////////////// 00229 Texture *TexturePool:: 00230 ns_load_texture(const Filename &orig_filename, int primary_file_num_channels, 00231 bool read_mipmaps, const LoaderOptions &options) { 00232 Filename filename; 00233 00234 { 00235 MutexHolder holder(_lock); 00236 resolve_filename(filename, orig_filename); 00237 Textures::const_iterator ti; 00238 ti = _textures.find(filename); 00239 if (ti != _textures.end()) { 00240 // This texture was previously loaded. 00241 Texture *tex = (*ti).second; 00242 nassertr(!tex->get_fullpath().empty(), tex); 00243 return tex; 00244 } 00245 } 00246 00247 // The texture was not found in the pool. 00248 PT(Texture) tex; 00249 PT(BamCacheRecord) record; 00250 bool store_record = false; 00251 00252 // Can one of our texture filters supply the texture? 00253 tex = pre_load(orig_filename, Filename(), primary_file_num_channels, 0, 00254 read_mipmaps, options); 00255 00256 BamCache *cache = BamCache::get_global_ptr(); 00257 bool compressed_cache_record = false; 00258 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00259 options); 00260 00261 if (tex == (Texture *)NULL) { 00262 // The texture was neither in the pool, nor found in the on-disk 00263 // cache; it needs to be loaded from its source image(s). 00264 gobj_cat.info() 00265 << "Loading texture " << filename << "\n"; 00266 tex = make_texture(filename.get_extension()); 00267 if (!tex->read(filename, Filename(), primary_file_num_channels, 0, 00268 0, 0, false, read_mipmaps, record, options)) { 00269 // This texture was not found or could not be read. 00270 report_texture_unreadable(filename); 00271 return NULL; 00272 } 00273 00274 if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) { 00275 tex->generate_simple_ram_image(); 00276 } 00277 00278 store_record = (record != (BamCacheRecord *)NULL); 00279 } 00280 00281 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00282 #ifndef HAVE_SQUISH 00283 bool needs_driver_compression = true; 00284 #else 00285 bool needs_driver_compression = driver_compress_textures; 00286 #endif // HAVE_SQUISH 00287 if (needs_driver_compression) { 00288 // We don't want to save the uncompressed version; we'll save the 00289 // compressed version when it becomes available. 00290 store_record = false; 00291 if (!compressed_cache_record) { 00292 tex->set_post_load_store_cache(true); 00293 } 00294 } 00295 00296 } else if (!cache->get_cache_textures()) { 00297 // We don't want to save this texture. 00298 store_record = false; 00299 } 00300 00301 // Set the original filename, before we searched along the path. 00302 nassertr(tex != (Texture *)NULL, NULL); 00303 tex->set_filename(orig_filename); 00304 tex->set_fullpath(filename); 00305 tex->_texture_pool_key = filename; 00306 00307 { 00308 MutexHolder holder(_lock); 00309 00310 // Now look again--someone may have just loaded this texture in 00311 // another thread. 00312 Textures::const_iterator ti; 00313 ti = _textures.find(filename); 00314 if (ti != _textures.end()) { 00315 // This texture was previously loaded. 00316 Texture *tex = (*ti).second; 00317 nassertr(!tex->get_fullpath().empty(), tex); 00318 return tex; 00319 } 00320 00321 _textures[filename] = tex; 00322 } 00323 00324 if (store_record && tex->has_ram_image()) { 00325 // Store the on-disk cache record for next time. 00326 record->set_data(tex, tex); 00327 cache->store(record); 00328 } 00329 00330 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) { 00331 // And now drop the RAM until we need it. 00332 tex->clear_ram_image(); 00333 } 00334 00335 nassertr(!tex->get_fullpath().empty(), tex); 00336 00337 // Finally, apply any post-loading texture filters. 00338 tex = post_load(tex); 00339 00340 return tex; 00341 } 00342 00343 //////////////////////////////////////////////////////////////////// 00344 // Function: TexturePool::ns_load_texture 00345 // Access: Private 00346 // Description: The nonstatic implementation of load_texture(). 00347 //////////////////////////////////////////////////////////////////// 00348 Texture *TexturePool:: 00349 ns_load_texture(const Filename &orig_filename, 00350 const Filename &orig_alpha_filename, 00351 int primary_file_num_channels, 00352 int alpha_file_channel, 00353 bool read_mipmaps, const LoaderOptions &options) { 00354 if (!_fake_texture_image.empty()) { 00355 return ns_load_texture(_fake_texture_image, primary_file_num_channels, 00356 read_mipmaps, options); 00357 } 00358 00359 Filename filename; 00360 Filename alpha_filename; 00361 00362 { 00363 MutexHolder holder(_lock); 00364 resolve_filename(filename, orig_filename); 00365 resolve_filename(alpha_filename, orig_alpha_filename); 00366 00367 Textures::const_iterator ti; 00368 ti = _textures.find(filename); 00369 if (ti != _textures.end()) { 00370 // This texture was previously loaded. 00371 Texture *tex = (*ti).second; 00372 nassertr(!tex->get_fullpath().empty(), tex); 00373 return tex; 00374 } 00375 } 00376 00377 PT(Texture) tex; 00378 PT(BamCacheRecord) record; 00379 bool store_record = false; 00380 00381 // Can one of our texture filters supply the texture? 00382 tex = pre_load(orig_filename, alpha_filename, primary_file_num_channels, 00383 alpha_file_channel, read_mipmaps, options); 00384 00385 BamCache *cache = BamCache::get_global_ptr(); 00386 bool compressed_cache_record = false; 00387 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00388 options); 00389 00390 if (tex == (Texture *)NULL) { 00391 // The texture was neither in the pool, nor found in the on-disk 00392 // cache; it needs to be loaded from its source image(s). 00393 gobj_cat.info() 00394 << "Loading texture " << filename << " and alpha component " 00395 << alpha_filename << endl; 00396 tex = make_texture(filename.get_extension()); 00397 if (!tex->read(filename, alpha_filename, primary_file_num_channels, 00398 alpha_file_channel, 0, 0, false, read_mipmaps, NULL, 00399 options)) { 00400 // This texture was not found or could not be read. 00401 report_texture_unreadable(filename); 00402 return NULL; 00403 } 00404 00405 if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) { 00406 tex->generate_simple_ram_image(); 00407 } 00408 00409 store_record = (record != (BamCacheRecord *)NULL); 00410 } 00411 00412 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00413 #ifndef HAVE_SQUISH 00414 bool needs_driver_compression = true; 00415 #else 00416 bool needs_driver_compression = driver_compress_textures; 00417 #endif // HAVE_SQUISH 00418 if (needs_driver_compression) { 00419 // We don't want to save the uncompressed version; we'll save the 00420 // compressed version when it becomes available. 00421 store_record = false; 00422 if (!compressed_cache_record) { 00423 tex->set_post_load_store_cache(true); 00424 } 00425 } 00426 00427 } else if (!cache->get_cache_textures()) { 00428 // We don't want to save this texture. 00429 store_record = false; 00430 } 00431 00432 // Set the original filenames, before we searched along the path. 00433 nassertr(tex != (Texture *)NULL, NULL); 00434 tex->set_filename(orig_filename); 00435 tex->set_fullpath(filename); 00436 tex->set_alpha_filename(orig_alpha_filename); 00437 tex->set_alpha_fullpath(alpha_filename); 00438 tex->_texture_pool_key = filename; 00439 00440 { 00441 MutexHolder holder(_lock); 00442 00443 // Now look again. 00444 Textures::const_iterator ti; 00445 ti = _textures.find(filename); 00446 if (ti != _textures.end()) { 00447 // This texture was previously loaded. 00448 Texture *tex = (*ti).second; 00449 nassertr(!tex->get_fullpath().empty(), tex); 00450 return tex; 00451 } 00452 00453 _textures[filename] = tex; 00454 } 00455 00456 if (store_record && tex->has_ram_image()) { 00457 // Store the on-disk cache record for next time. 00458 record->set_data(tex, tex); 00459 cache->store(record); 00460 } 00461 00462 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) { 00463 // And now drop the RAM until we need it. 00464 tex->clear_ram_image(); 00465 } 00466 00467 nassertr(!tex->get_fullpath().empty(), tex); 00468 00469 // Finally, apply any post-loading texture filters. 00470 tex = post_load(tex); 00471 00472 return tex; 00473 } 00474 00475 //////////////////////////////////////////////////////////////////// 00476 // Function: TexturePool::ns_load_3d_texture 00477 // Access: Private 00478 // Description: The nonstatic implementation of load_3d_texture(). 00479 //////////////////////////////////////////////////////////////////// 00480 Texture *TexturePool:: 00481 ns_load_3d_texture(const Filename &filename_pattern, 00482 bool read_mipmaps, const LoaderOptions &options) { 00483 Filename orig_filename(filename_pattern); 00484 orig_filename.set_pattern(true); 00485 00486 Filename filename; 00487 { 00488 MutexHolder holder(_lock); 00489 resolve_filename(filename, orig_filename); 00490 00491 Textures::const_iterator ti; 00492 ti = _textures.find(filename); 00493 if (ti != _textures.end()) { 00494 // This texture was previously loaded. 00495 return (*ti).second; 00496 } 00497 } 00498 00499 PT(Texture) tex; 00500 PT(BamCacheRecord) record; 00501 bool store_record = false; 00502 00503 BamCache *cache = BamCache::get_global_ptr(); 00504 bool compressed_cache_record = false; 00505 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00506 options); 00507 00508 if (tex == (Texture *)NULL || 00509 tex->get_texture_type() != Texture::TT_3d_texture) { 00510 // The texture was neither in the pool, nor found in the on-disk 00511 // cache; it needs to be loaded from its source image(s). 00512 gobj_cat.info() 00513 << "Loading 3-d texture " << filename << "\n"; 00514 tex = make_texture(filename.get_extension()); 00515 tex->setup_3d_texture(); 00516 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) { 00517 // This texture was not found or could not be read. 00518 report_texture_unreadable(filename); 00519 return NULL; 00520 } 00521 store_record = (record != (BamCacheRecord *)NULL); 00522 } 00523 00524 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00525 #ifndef HAVE_SQUISH 00526 bool needs_driver_compression = true; 00527 #else 00528 bool needs_driver_compression = driver_compress_textures; 00529 #endif // HAVE_SQUISH 00530 if (needs_driver_compression) { 00531 // We don't want to save the uncompressed version; we'll save the 00532 // compressed version when it becomes available. 00533 store_record = false; 00534 if (!compressed_cache_record) { 00535 tex->set_post_load_store_cache(true); 00536 } 00537 } 00538 00539 } else if (!cache->get_cache_textures()) { 00540 // We don't want to save this texture. 00541 store_record = false; 00542 } 00543 00544 // Set the original filename, before we searched along the path. 00545 nassertr(tex != (Texture *)NULL, false); 00546 tex->set_filename(filename_pattern); 00547 tex->set_fullpath(filename); 00548 tex->_texture_pool_key = filename; 00549 00550 { 00551 MutexHolder holder(_lock); 00552 00553 // Now look again. 00554 Textures::const_iterator ti; 00555 ti = _textures.find(filename); 00556 if (ti != _textures.end()) { 00557 // This texture was previously loaded. 00558 return (*ti).second; 00559 } 00560 00561 _textures[filename] = tex; 00562 } 00563 00564 if (store_record && tex->has_ram_image()) { 00565 // Store the on-disk cache record for next time. 00566 record->set_data(tex, tex); 00567 cache->store(record); 00568 } 00569 00570 nassertr(!tex->get_fullpath().empty(), tex); 00571 return tex; 00572 } 00573 00574 //////////////////////////////////////////////////////////////////// 00575 // Function: TexturePool::ns_load_cube_map 00576 // Access: Private 00577 // Description: The nonstatic implementation of load_cube_map(). 00578 //////////////////////////////////////////////////////////////////// 00579 Texture *TexturePool:: 00580 ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps, 00581 const LoaderOptions &options) { 00582 Filename orig_filename(filename_pattern); 00583 orig_filename.set_pattern(true); 00584 00585 Filename filename; 00586 { 00587 MutexHolder holder(_lock); 00588 resolve_filename(filename, orig_filename); 00589 00590 Textures::const_iterator ti; 00591 ti = _textures.find(filename); 00592 if (ti != _textures.end()) { 00593 // This texture was previously loaded. 00594 return (*ti).second; 00595 } 00596 } 00597 00598 PT(Texture) tex; 00599 PT(BamCacheRecord) record; 00600 bool store_record = false; 00601 00602 BamCache *cache = BamCache::get_global_ptr(); 00603 bool compressed_cache_record = false; 00604 try_load_cache(tex, cache, filename, record, compressed_cache_record, 00605 options); 00606 00607 if (tex == (Texture *)NULL || 00608 tex->get_texture_type() != Texture::TT_cube_map) { 00609 // The texture was neither in the pool, nor found in the on-disk 00610 // cache; it needs to be loaded from its source image(s). 00611 gobj_cat.info() 00612 << "Loading cube map texture " << filename << "\n"; 00613 tex = make_texture(filename.get_extension()); 00614 tex->setup_cube_map(); 00615 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) { 00616 // This texture was not found or could not be read. 00617 report_texture_unreadable(filename); 00618 return NULL; 00619 } 00620 store_record = (record != (BamCacheRecord *)NULL); 00621 } 00622 00623 if (cache->get_cache_compressed_textures() && tex->has_compression()) { 00624 #ifndef HAVE_SQUISH 00625 bool needs_driver_compression = true; 00626 #else 00627 bool needs_driver_compression = driver_compress_textures; 00628 #endif // HAVE_SQUISH 00629 if (needs_driver_compression) { 00630 // We don't want to save the uncompressed version; we'll save the 00631 // compressed version when it becomes available. 00632 store_record = false; 00633 if (!compressed_cache_record) { 00634 tex->set_post_load_store_cache(true); 00635 } 00636 } 00637 00638 } else if (!cache->get_cache_textures()) { 00639 // We don't want to save this texture. 00640 store_record = false; 00641 } 00642 00643 // Set the original filename, before we searched along the path. 00644 nassertr(tex != (Texture *)NULL, false); 00645 tex->set_filename(filename_pattern); 00646 tex->set_fullpath(filename); 00647 tex->_texture_pool_key = filename; 00648 00649 { 00650 MutexHolder holder(_lock); 00651 00652 // Now look again. 00653 Textures::const_iterator ti; 00654 ti = _textures.find(filename); 00655 if (ti != _textures.end()) { 00656 // This texture was previously loaded. 00657 return (*ti).second; 00658 } 00659 00660 _textures[filename] = tex; 00661 } 00662 00663 if (store_record && tex->has_ram_image()) { 00664 // Store the on-disk cache record for next time. 00665 record->set_data(tex, tex); 00666 cache->store(record); 00667 } 00668 00669 nassertr(!tex->get_fullpath().empty(), tex); 00670 return tex; 00671 } 00672 00673 //////////////////////////////////////////////////////////////////// 00674 // Function: TexturePool::ns_get_normalization_cube_map 00675 // Access: Private 00676 // Description: The nonstatic implementation of get_normalization_cube_map(). 00677 //////////////////////////////////////////////////////////////////// 00678 Texture *TexturePool:: 00679 ns_get_normalization_cube_map(int size) { 00680 MutexHolder holder(_lock); 00681 00682 if (_normalization_cube_map == (Texture *)NULL) { 00683 _normalization_cube_map = new Texture("normalization_cube_map"); 00684 } 00685 if (_normalization_cube_map->get_x_size() < size || 00686 _normalization_cube_map->get_texture_type() != Texture::TT_cube_map) { 00687 _normalization_cube_map->generate_normalization_cube_map(size); 00688 } 00689 00690 return _normalization_cube_map; 00691 } 00692 00693 //////////////////////////////////////////////////////////////////// 00694 // Function: TexturePool::ns_get_alpha_scale_map 00695 // Access: Private 00696 // Description: The nonstatic implementation of get_alpha_scale_map(). 00697 //////////////////////////////////////////////////////////////////// 00698 Texture *TexturePool:: 00699 ns_get_alpha_scale_map() { 00700 MutexHolder holder(_lock); 00701 00702 if (_alpha_scale_map == (Texture *)NULL) { 00703 _alpha_scale_map = new Texture("alpha_scale_map"); 00704 _alpha_scale_map->generate_alpha_scale_map(); 00705 } 00706 00707 return _alpha_scale_map; 00708 } 00709 00710 //////////////////////////////////////////////////////////////////// 00711 // Function: TexturePool::ns_add_texture 00712 // Access: Private 00713 // Description: The nonstatic implementation of add_texture(). 00714 //////////////////////////////////////////////////////////////////// 00715 void TexturePool:: 00716 ns_add_texture(Texture *tex) { 00717 PT(Texture) keep = tex; 00718 MutexHolder holder(_lock); 00719 00720 if (!tex->_texture_pool_key.empty()) { 00721 ns_release_texture(tex); 00722 } 00723 string filename = tex->get_fullpath(); 00724 if (filename.empty()) { 00725 gobj_cat.error() << "Attempt to call add_texture() on an unnamed texture.\n"; 00726 } 00727 00728 // We blow away whatever texture was there previously, if any. 00729 tex->_texture_pool_key = filename; 00730 _textures[filename] = tex; 00731 nassertv(!tex->get_fullpath().empty()); 00732 } 00733 00734 //////////////////////////////////////////////////////////////////// 00735 // Function: TexturePool::ns_release_texture 00736 // Access: Private 00737 // Description: The nonstatic implementation of release_texture(). 00738 //////////////////////////////////////////////////////////////////// 00739 void TexturePool:: 00740 ns_release_texture(Texture *tex) { 00741 MutexHolder holder(_lock); 00742 00743 if (!tex->_texture_pool_key.empty()) { 00744 Textures::iterator ti; 00745 ti = _textures.find(tex->_texture_pool_key); 00746 if (ti != _textures.end() && (*ti).second == tex) { 00747 _textures.erase(ti); 00748 } 00749 tex->_texture_pool_key = string(); 00750 } 00751 00752 // Blow away the cache of resolved relative filenames. 00753 _relpath_lookup.clear(); 00754 } 00755 00756 //////////////////////////////////////////////////////////////////// 00757 // Function: TexturePool::ns_release_all_textures 00758 // Access: Private 00759 // Description: The nonstatic implementation of release_all_textures(). 00760 //////////////////////////////////////////////////////////////////// 00761 void TexturePool:: 00762 ns_release_all_textures() { 00763 MutexHolder holder(_lock); 00764 00765 Textures::iterator ti; 00766 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00767 Texture *tex = (*ti).second; 00768 tex->_texture_pool_key = string(); 00769 } 00770 00771 _textures.clear(); 00772 _normalization_cube_map = NULL; 00773 00774 // Blow away the cache of resolved relative filenames. 00775 _relpath_lookup.clear(); 00776 } 00777 00778 //////////////////////////////////////////////////////////////////// 00779 // Function: TexturePool::ns_garbage_collect 00780 // Access: Private 00781 // Description: The nonstatic implementation of garbage_collect(). 00782 //////////////////////////////////////////////////////////////////// 00783 int TexturePool:: 00784 ns_garbage_collect() { 00785 MutexHolder holder(_lock); 00786 00787 int num_released = 0; 00788 Textures new_set; 00789 00790 Textures::iterator ti; 00791 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00792 Texture *tex = (*ti).second; 00793 if (tex->get_ref_count() == 1) { 00794 if (gobj_cat.is_debug()) { 00795 gobj_cat.debug() 00796 << "Releasing " << (*ti).first << "\n"; 00797 } 00798 ++num_released; 00799 tex->_texture_pool_key = string(); 00800 } else { 00801 new_set.insert(new_set.end(), *ti); 00802 } 00803 } 00804 00805 _textures.swap(new_set); 00806 00807 if (_normalization_cube_map != (Texture *)NULL && 00808 _normalization_cube_map->get_ref_count() == 1) { 00809 if (gobj_cat.is_debug()) { 00810 gobj_cat.debug() 00811 << "Releasing normalization cube map\n"; 00812 } 00813 ++num_released; 00814 _normalization_cube_map = NULL; 00815 } 00816 00817 return num_released; 00818 } 00819 00820 //////////////////////////////////////////////////////////////////// 00821 // Function: TexturePool::ns_list_contents 00822 // Access: Private 00823 // Description: The nonstatic implementation of list_contents(). 00824 //////////////////////////////////////////////////////////////////// 00825 void TexturePool:: 00826 ns_list_contents(ostream &out) const { 00827 MutexHolder holder(_lock); 00828 00829 int total_size; 00830 int total_ram_size; 00831 Textures::const_iterator ti; 00832 00833 out << "texture pool contents:\n"; 00834 00835 total_size = 0; 00836 total_ram_size = 0; 00837 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00838 Texture *tex = (*ti).second; 00839 out << (*ti).first << "\n"; 00840 out << " (count = " << tex->get_ref_count() 00841 << ", ram = " << tex->get_ram_image_size() 00842 << ", size = " << tex->get_ram_page_size() 00843 << ", w = " << tex->get_x_size() 00844 << ", h = " << tex->get_y_size() 00845 << ")\n"; 00846 nassertv(tex->_texture_pool_key == (*ti).first); 00847 total_ram_size += tex->get_ram_image_size(); 00848 total_size += tex->get_ram_page_size(); 00849 } 00850 00851 out << "total number of textures: " << _textures.size() << "\n"; 00852 out << "texture pool ram : " << total_ram_size << "\n"; 00853 out << "texture pool size: " << total_size << "\n"; 00854 out << "texture pool size - texture pool ram: " << total_size - total_ram_size << "\n"; 00855 } 00856 00857 //////////////////////////////////////////////////////////////////// 00858 // Function: TexturePool::ns_find_texture 00859 // Access: Private 00860 // Description: The nonstatic implementation of find_texture(). 00861 //////////////////////////////////////////////////////////////////// 00862 Texture *TexturePool:: 00863 ns_find_texture(const string &name) const { 00864 MutexHolder holder(_lock); 00865 GlobPattern glob(name); 00866 00867 Textures::const_iterator ti; 00868 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00869 Texture *tex = (*ti).second; 00870 if (glob.matches(tex->get_name())) { 00871 return tex; 00872 } 00873 } 00874 00875 return NULL; 00876 } 00877 00878 //////////////////////////////////////////////////////////////////// 00879 // Function: TexturePool::ns_find_all_textures 00880 // Access: Private 00881 // Description: The nonstatic implementation of find_all_textures(). 00882 //////////////////////////////////////////////////////////////////// 00883 TextureCollection TexturePool:: 00884 ns_find_all_textures(const string &name) const { 00885 MutexHolder holder(_lock); 00886 TextureCollection result; 00887 GlobPattern glob(name); 00888 00889 Textures::const_iterator ti; 00890 for (ti = _textures.begin(); ti != _textures.end(); ++ti) { 00891 Texture *tex = (*ti).second; 00892 if (glob.matches(tex->get_name())) { 00893 result.add_texture(tex); 00894 } 00895 } 00896 00897 return result; 00898 } 00899 00900 //////////////////////////////////////////////////////////////////// 00901 // Function: TexturePool::resolve_filename 00902 // Access: Private 00903 // Description: Searches for the indicated filename along the 00904 // model path. If the filename was previously 00905 // searched for, doesn't search again, as an 00906 // optimization. Assumes _lock is held. 00907 //////////////////////////////////////////////////////////////////// 00908 void TexturePool:: 00909 resolve_filename(Filename &new_filename, const Filename &orig_filename) { 00910 if (!_fake_texture_image.empty()) { 00911 new_filename = _fake_texture_image; 00912 return; 00913 } 00914 00915 RelpathLookup::iterator rpi = _relpath_lookup.find(orig_filename); 00916 if (rpi != _relpath_lookup.end()) { 00917 new_filename = (*rpi).second; 00918 return; 00919 } 00920 00921 new_filename = orig_filename; 00922 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 00923 vfs->resolve_filename(new_filename, get_model_path()); 00924 00925 _relpath_lookup[orig_filename] = new_filename; 00926 } 00927 00928 //////////////////////////////////////////////////////////////////// 00929 // Function: TexturePool::try_load_cache 00930 // Access: Private 00931 // Description: Attempts to load the texture from the cache record. 00932 //////////////////////////////////////////////////////////////////// 00933 void TexturePool:: 00934 try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename, 00935 PT(BamCacheRecord) &record, bool &compressed_cache_record, 00936 const LoaderOptions &options) { 00937 if (tex == (Texture *)NULL) { 00938 // The texture was not supplied by a texture filter. See if it 00939 // can be found in the on-disk cache, if it is active. 00940 if ((cache->get_cache_textures() || cache->get_cache_compressed_textures()) && !textures_header_only) { 00941 record = cache->lookup(filename, "txo"); 00942 if (record != (BamCacheRecord *)NULL) { 00943 if (record->has_data()) { 00944 tex = DCAST(Texture, record->get_data()); 00945 compressed_cache_record = (tex->get_ram_image_compression() != Texture::CM_off); 00946 int x_size = tex->get_orig_file_x_size(); 00947 int y_size = tex->get_orig_file_y_size(); 00948 Texture::adjust_size(x_size, y_size, filename.get_basename()); 00949 00950 if (!cache->get_cache_textures() && !compressed_cache_record) { 00951 // We're not supposed to be caching uncompressed textures. 00952 if (gobj_cat.is_debug()) { 00953 gobj_cat.debug() 00954 << "Not caching uncompressed texture " << *tex << "\n"; 00955 } 00956 tex = NULL; 00957 record = NULL; 00958 00959 } else if (x_size != tex->get_x_size() || 00960 y_size != tex->get_y_size()) { 00961 // The cached texture no longer matches our expected size 00962 // (the resizing config variables must have changed). 00963 // We'll have to reload the texture from its original file 00964 // so we can rebuild the cache. 00965 if (gobj_cat.is_debug()) { 00966 gobj_cat.debug() 00967 << "Cached texture " << *tex << " has size " 00968 << tex->get_x_size() << " x " << tex->get_y_size() 00969 << " instead of " << x_size << " x " << y_size 00970 << "; dropping cache.\n"; 00971 } 00972 tex = NULL; 00973 00974 } else if (!tex->has_compression() && tex->get_ram_image_compression() != Texture::CM_off) { 00975 // This texture shouldn't be compressed, but it is. Go 00976 // reload it. 00977 if (gobj_cat.is_debug()) { 00978 gobj_cat.debug() 00979 << "Cached texture " << *tex 00980 << " is compressed in cache; dropping cache.\n"; 00981 } 00982 tex = NULL; 00983 00984 } else { 00985 gobj_cat.info() 00986 << "Texture " << filename << " found in disk cache.\n"; 00987 if ((options.get_texture_flags() & LoaderOptions::TF_preload_simple) && 00988 !tex->has_simple_ram_image()) { 00989 tex->generate_simple_ram_image(); 00990 } 00991 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) { 00992 // But drop the RAM until we need it. 00993 tex->clear_ram_image(); 00994 00995 } else { 00996 bool was_compressed = (tex->get_ram_image_compression() != Texture::CM_off); 00997 if (tex->consider_auto_process_ram_image(tex->uses_mipmaps(), true)) { 00998 bool is_compressed = (tex->get_ram_image_compression() != Texture::CM_off); 00999 if (!was_compressed && is_compressed && 01000 cache->get_cache_compressed_textures()) { 01001 // We've re-compressed the image after loading it 01002 // from the cache. To keep the cache current, 01003 // rewrite it to the cache now, in its newly 01004 // compressed form. 01005 record->set_data(tex, tex); 01006 cache->store(record); 01007 compressed_cache_record = true; 01008 } 01009 } 01010 } 01011 tex->set_keep_ram_image(false); 01012 } 01013 } else { 01014 if (!cache->get_cache_textures()) { 01015 // This texture has no actual record, and therefore no 01016 // compressed record (yet). And we're not supposed to be 01017 // caching uncompressed textures. 01018 if (gobj_cat.is_debug()) { 01019 gobj_cat.debug() 01020 << "Not caching uncompressed texture\n"; 01021 } 01022 record = NULL; 01023 } 01024 } 01025 } 01026 } 01027 } 01028 } 01029 01030 //////////////////////////////////////////////////////////////////// 01031 // Function: TexturePool::report_texture_unreadable 01032 // Access: Private 01033 // Description: Prints a suitable error message when a texture could 01034 // not be loaded. 01035 //////////////////////////////////////////////////////////////////// 01036 void TexturePool:: 01037 report_texture_unreadable(const Filename &filename) const { 01038 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); 01039 if (!vfs->exists(filename)) { 01040 if (filename.is_local()) { 01041 // The file doesn't exist, and it wasn't 01042 // fully-qualified--therefore, it wasn't found along either 01043 // search path. 01044 gobj_cat.error() 01045 << "Unable to find texture \"" << filename << "\"" 01046 << " on model-path " << get_model_path() <<"\n"; 01047 } else { 01048 // A fully-specified filename is not searched along the path, so 01049 // don't mislead the user with the error message. 01050 gobj_cat.error() 01051 << "Texture \"" << filename << "\" does not exist.\n"; 01052 } 01053 01054 } else { 01055 // The file exists, but it couldn't be read for some reason. 01056 gobj_cat.error() 01057 << "Texture \"" << filename << "\" exists but cannot be read.\n"; 01058 01059 // Maybe the filename extension is unknown. 01060 MakeTextureFunc *func = get_texture_type(filename.get_extension()); 01061 if (func == (MakeTextureFunc *)NULL) { 01062 gobj_cat.error() 01063 << "Texture extension \"" << filename.get_extension() 01064 << "\" is unknown. Supported texture types:\n"; 01065 write_texture_types(gobj_cat.error(false), 2); 01066 } 01067 } 01068 } 01069 01070 //////////////////////////////////////////////////////////////////// 01071 // Function: TexturePool::pre_load 01072 // Access: Private 01073 // Description: Invokes pre_load() on all registered filters until 01074 // one returns non-NULL; returns NULL if there are no 01075 // registered filters or if all registered filters 01076 // returned NULL. 01077 //////////////////////////////////////////////////////////////////// 01078 PT(Texture) TexturePool:: 01079 pre_load(const Filename &orig_filename, const Filename &orig_alpha_filename, 01080 int primary_file_num_channels, int alpha_file_channel, 01081 bool read_mipmaps, const LoaderOptions &options) { 01082 PT(Texture) tex; 01083 01084 MutexHolder holder(_lock); 01085 01086 FilterRegistry::iterator fi; 01087 for (fi = _filter_registry.begin(); 01088 fi != _filter_registry.end(); 01089 ++fi) { 01090 tex = (*fi)->pre_load(orig_filename, orig_alpha_filename, 01091 primary_file_num_channels, alpha_file_channel, 01092 read_mipmaps, options); 01093 if (tex != (Texture *)NULL) { 01094 return tex; 01095 } 01096 } 01097 01098 return tex; 01099 } 01100 01101 //////////////////////////////////////////////////////////////////// 01102 // Function: TexturePool::post_load 01103 // Access: Public, Virtual 01104 // Description: Invokes post_load() on all registered filters. 01105 //////////////////////////////////////////////////////////////////// 01106 PT(Texture) TexturePool:: 01107 post_load(Texture *tex) { 01108 PT(Texture) result = tex; 01109 01110 MutexHolder holder(_lock); 01111 01112 FilterRegistry::iterator fi; 01113 for (fi = _filter_registry.begin(); 01114 fi != _filter_registry.end(); 01115 ++fi) { 01116 result = (*fi)->post_load(result); 01117 } 01118 01119 return result; 01120 } 01121 01122 01123 //////////////////////////////////////////////////////////////////// 01124 // Function: TexturePool::load_filters 01125 // Access: Private 01126 // Description: Loads up all of the dll's named by the texture-filter 01127 // Config.prc variable. 01128 //////////////////////////////////////////////////////////////////// 01129 void TexturePool:: 01130 load_filters() { 01131 ConfigVariableList texture_filter 01132 ("texture-filter", 01133 PRC_DESC("Names one or more external libraries that should be loaded for the " 01134 "purposes of performing texture filtering. This variable may be repeated several " 01135 "times. As in load-display, the actual library filename is derived by " 01136 "prefixing 'lib' to the specified name.")); 01137 01138 int num_aux = texture_filter.get_num_unique_values(); 01139 for (int i = 0; i < num_aux; i++) { 01140 string name = texture_filter.get_unique_value(i); 01141 01142 Filename dlname = Filename::dso_filename("lib" + name + ".so"); 01143 gobj_cat->info() 01144 << "loading texture filter: " << dlname.to_os_specific() << endl; 01145 void *tmp = load_dso(get_plugin_path().get_value(), dlname); 01146 if (tmp == (void *)NULL) { 01147 gobj_cat.info() 01148 << "Unable to load: " << load_dso_error() << endl; 01149 } 01150 } 01151 }