00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #include "texturePool.h"
00017 #include "config_gobj.h"
00018 #include "config_util.h"
00019 #include "config_express.h"
00020 #include "string_utils.h"
00021 #include "virtualFileSystem.h"
00022 #include "bamCache.h"
00023 #include "bamCacheRecord.h"
00024 #include "pnmFileTypeRegistry.h"
00025 #include "texturePoolFilter.h"
00026 #include "configVariableList.h"
00027 #include "load_dso.h"
00028 #include "mutexHolder.h"
00029
00030 TexturePool *TexturePool::_global_ptr;
00031
00032
00033
00034
00035
00036
00037
00038
00039 void TexturePool::
00040 write(ostream &out) {
00041 get_global_ptr()->ns_list_contents(out);
00042 }
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053 void TexturePool::
00054 register_texture_type(MakeTextureFunc *func, const string &extensions) {
00055 MutexHolder holder(_lock);
00056
00057 vector_string words;
00058 extract_words(downcase(extensions), words);
00059
00060 vector_string::const_iterator wi;
00061 for (wi = words.begin(); wi != words.end(); ++wi) {
00062 _type_registry[*wi] = func;
00063 }
00064 }
00065
00066
00067
00068
00069
00070
00071
00072 void TexturePool::
00073 register_filter(TexturePoolFilter *filter) {
00074 MutexHolder holder(_lock);
00075
00076 gobj_cat.info()
00077 << "Registering Texture filter " << *filter << "\n";
00078 _filter_registry.push_back(filter);
00079 }
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089 TexturePool::MakeTextureFunc *TexturePool::
00090 get_texture_type(const string &extension) const {
00091 MutexHolder holder(_lock);
00092
00093 string c = downcase(extension);
00094 TypeRegistry::const_iterator ti;
00095 ti = _type_registry.find(c);
00096 if (ti != _type_registry.end()) {
00097 return (*ti).second;
00098 }
00099
00100
00101 PNMFileTypeRegistry *pnm_reg = PNMFileTypeRegistry::get_global_ptr();
00102 PNMFileType *type = pnm_reg->get_type_from_extension(c);
00103 if (type != (PNMFileType *)NULL) {
00104
00105 ((TexturePool *)this)->_type_registry[c] = Texture::make_texture;
00106 return Texture::make_texture;
00107 }
00108
00109
00110 return NULL;
00111 }
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121 void TexturePool::
00122 write_texture_types(ostream &out, int indent_level) const {
00123 MutexHolder holder(_lock);
00124
00125 PNMFileTypeRegistry *pnm_reg = PNMFileTypeRegistry::get_global_ptr();
00126 pnm_reg->write(out, indent_level);
00127
00128
00129
00130 TypeRegistry::const_iterator ti;
00131 for (ti = _type_registry.begin(); ti != _type_registry.end(); ++ti) {
00132 string extension = (*ti).first;
00133 MakeTextureFunc *func = (*ti).second;
00134
00135 if (pnm_reg->get_type_from_extension(extension) == NULL) {
00136 PT(Texture) tex = func();
00137 string name = tex->get_type().get_name();
00138 indent(out, indent_level) << name;
00139 indent(out, max(30 - (int)name.length(), 0))
00140 << " ." << extension << "\n";
00141 }
00142 }
00143 }
00144
00145
00146
00147
00148
00149
00150
00151 TexturePool *TexturePool::
00152 get_global_ptr() {
00153 if (_global_ptr == (TexturePool *)NULL) {
00154 _global_ptr = new TexturePool;
00155
00156
00157
00158
00159 _global_ptr->load_filters();
00160 }
00161 return _global_ptr;
00162 }
00163
00164
00165
00166
00167
00168
00169
00170
00171 TexturePool::
00172 TexturePool() {
00173 ConfigVariableFilename fake_texture_image
00174 ("fake-texture-image", "",
00175 PRC_DESC("Set this to enable a speedy-load mode in which you don't care "
00176 "what the world looks like, you just want it to load in minimal "
00177 "time. This causes all texture loads via the TexturePool to use "
00178 "the same texture file, which will presumably only be loaded "
00179 "once."));
00180 _fake_texture_image = fake_texture_image;
00181 }
00182
00183
00184
00185
00186
00187
00188 bool TexturePool::
00189 ns_has_texture(const Filename &orig_filename) {
00190 MutexHolder holder(_lock);
00191
00192 Filename filename;
00193 resolve_filename(filename, orig_filename, false, LoaderOptions());
00194
00195 Textures::const_iterator ti;
00196 ti = _textures.find(filename);
00197 if (ti != _textures.end()) {
00198
00199 return true;
00200 }
00201
00202 return false;
00203 }
00204
00205
00206
00207
00208
00209
00210 Texture *TexturePool::
00211 ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
00212 bool read_mipmaps, const LoaderOptions &options) {
00213 Filename filename;
00214
00215 {
00216 MutexHolder holder(_lock);
00217 resolve_filename(filename, orig_filename, read_mipmaps, options);
00218 Textures::const_iterator ti;
00219 ti = _textures.find(filename);
00220 if (ti != _textures.end()) {
00221
00222 Texture *tex = (*ti).second;
00223 nassertr(!tex->get_fullpath().empty(), tex);
00224 return tex;
00225 }
00226 }
00227
00228
00229 PT(Texture) tex;
00230 PT(BamCacheRecord) record;
00231 bool store_record = false;
00232
00233
00234 tex = pre_load(orig_filename, Filename(), primary_file_num_channels, 0,
00235 read_mipmaps, options);
00236
00237 BamCache *cache = BamCache::get_global_ptr();
00238 bool compressed_cache_record = false;
00239 try_load_cache(tex, cache, filename, record, compressed_cache_record,
00240 options);
00241
00242 if (tex == (Texture *)NULL) {
00243
00244
00245 gobj_cat.info()
00246 << "Loading texture " << filename << "\n";
00247
00248 string ext = downcase(filename.get_extension());
00249 if (ext == "txo" || ext == "bam") {
00250
00251
00252
00253 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
00254
00255 filename.set_binary();
00256 PT(VirtualFile) file = vfs->get_file(filename);
00257 if (file == (VirtualFile *)NULL) {
00258
00259 gobj_cat.error()
00260 << "Could not find " << filename << "\n";
00261 return false;
00262 }
00263
00264 if (gobj_cat.is_debug()) {
00265 gobj_cat.debug()
00266 << "Reading texture object " << filename << "\n";
00267 }
00268
00269 istream *in = file->open_read_file(true);
00270 tex = Texture::make_from_txo(*in, filename);
00271 vfs->close_read_file(in);
00272
00273 if (tex == (Texture *)NULL) {
00274 return false;
00275 }
00276 tex->set_fullpath(filename);
00277 tex->clear_alpha_fullpath();
00278 tex->set_keep_ram_image(false);
00279
00280 } else {
00281
00282 tex = ns_make_texture(ext);
00283 if (!tex->read(filename, Filename(), primary_file_num_channels, 0,
00284 0, 0, false, read_mipmaps, record, options)) {
00285
00286 report_texture_unreadable(filename);
00287 return NULL;
00288 }
00289 }
00290
00291 if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) {
00292 tex->generate_simple_ram_image();
00293 }
00294
00295 store_record = (record != (BamCacheRecord *)NULL);
00296 }
00297
00298 if (cache->get_cache_compressed_textures() && tex->has_compression()) {
00299 #ifndef HAVE_SQUISH
00300 bool needs_driver_compression = true;
00301 #else
00302 bool needs_driver_compression = driver_compress_textures;
00303 #endif // HAVE_SQUISH
00304 if (needs_driver_compression) {
00305
00306
00307 store_record = false;
00308 if (!compressed_cache_record) {
00309 tex->set_post_load_store_cache(true);
00310 }
00311 }
00312
00313 } else if (!cache->get_cache_textures()) {
00314
00315 store_record = false;
00316 }
00317
00318
00319 nassertr(tex != (Texture *)NULL, NULL);
00320 tex->set_filename(orig_filename);
00321 tex->set_fullpath(filename);
00322 tex->_texture_pool_key = filename;
00323
00324 {
00325 MutexHolder holder(_lock);
00326
00327
00328
00329 Textures::const_iterator ti;
00330 ti = _textures.find(filename);
00331 if (ti != _textures.end()) {
00332
00333 Texture *tex = (*ti).second;
00334 nassertr(!tex->get_fullpath().empty(), tex);
00335 return tex;
00336 }
00337
00338 _textures[filename] = tex;
00339 }
00340
00341 if (store_record && tex->is_cacheable()) {
00342
00343 record->set_data(tex, tex);
00344 cache->store(record);
00345 }
00346
00347 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) {
00348
00349 tex->clear_ram_image();
00350 }
00351
00352 nassertr(!tex->get_fullpath().empty(), tex);
00353
00354
00355 tex = post_load(tex);
00356
00357 return tex;
00358 }
00359
00360
00361
00362
00363
00364
00365 Texture *TexturePool::
00366 ns_load_texture(const Filename &orig_filename,
00367 const Filename &orig_alpha_filename,
00368 int primary_file_num_channels,
00369 int alpha_file_channel,
00370 bool read_mipmaps, const LoaderOptions &options) {
00371 if (!_fake_texture_image.empty()) {
00372 return ns_load_texture(_fake_texture_image, primary_file_num_channels,
00373 read_mipmaps, options);
00374 }
00375
00376 Filename filename;
00377 Filename alpha_filename;
00378
00379 {
00380 MutexHolder holder(_lock);
00381 resolve_filename(filename, orig_filename, read_mipmaps, options);
00382 resolve_filename(alpha_filename, orig_alpha_filename, read_mipmaps, options);
00383
00384 Textures::const_iterator ti;
00385 ti = _textures.find(filename);
00386 if (ti != _textures.end()) {
00387
00388 Texture *tex = (*ti).second;
00389 nassertr(!tex->get_fullpath().empty(), tex);
00390 return tex;
00391 }
00392 }
00393
00394 PT(Texture) tex;
00395 PT(BamCacheRecord) record;
00396 bool store_record = false;
00397
00398
00399 tex = pre_load(orig_filename, alpha_filename, primary_file_num_channels,
00400 alpha_file_channel, read_mipmaps, options);
00401
00402 BamCache *cache = BamCache::get_global_ptr();
00403 bool compressed_cache_record = false;
00404 try_load_cache(tex, cache, filename, record, compressed_cache_record,
00405 options);
00406
00407 if (tex == (Texture *)NULL) {
00408
00409
00410 gobj_cat.info()
00411 << "Loading texture " << filename << " and alpha component "
00412 << alpha_filename << endl;
00413 tex = ns_make_texture(filename.get_extension());
00414 if (!tex->read(filename, alpha_filename, primary_file_num_channels,
00415 alpha_file_channel, 0, 0, false, read_mipmaps, NULL,
00416 options)) {
00417
00418 report_texture_unreadable(filename);
00419 return NULL;
00420 }
00421
00422 if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) {
00423 tex->generate_simple_ram_image();
00424 }
00425
00426 store_record = (record != (BamCacheRecord *)NULL);
00427 }
00428
00429 if (cache->get_cache_compressed_textures() && tex->has_compression()) {
00430 #ifndef HAVE_SQUISH
00431 bool needs_driver_compression = true;
00432 #else
00433 bool needs_driver_compression = driver_compress_textures;
00434 #endif // HAVE_SQUISH
00435 if (needs_driver_compression) {
00436
00437
00438 store_record = false;
00439 if (!compressed_cache_record) {
00440 tex->set_post_load_store_cache(true);
00441 }
00442 }
00443
00444 } else if (!cache->get_cache_textures()) {
00445
00446 store_record = false;
00447 }
00448
00449
00450 nassertr(tex != (Texture *)NULL, NULL);
00451 tex->set_filename(orig_filename);
00452 tex->set_fullpath(filename);
00453 tex->set_alpha_filename(orig_alpha_filename);
00454 tex->set_alpha_fullpath(alpha_filename);
00455 tex->_texture_pool_key = filename;
00456
00457 {
00458 MutexHolder holder(_lock);
00459
00460
00461 Textures::const_iterator ti;
00462 ti = _textures.find(filename);
00463 if (ti != _textures.end()) {
00464
00465 Texture *tex = (*ti).second;
00466 nassertr(!tex->get_fullpath().empty(), tex);
00467 return tex;
00468 }
00469
00470 _textures[filename] = tex;
00471 }
00472
00473 if (store_record && tex->is_cacheable()) {
00474
00475 record->set_data(tex, tex);
00476 cache->store(record);
00477 }
00478
00479 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) {
00480
00481 tex->clear_ram_image();
00482 }
00483
00484 nassertr(!tex->get_fullpath().empty(), tex);
00485
00486
00487 tex = post_load(tex);
00488
00489 return tex;
00490 }
00491
00492
00493
00494
00495
00496
00497 Texture *TexturePool::
00498 ns_load_3d_texture(const Filename &filename_pattern,
00499 bool read_mipmaps, const LoaderOptions &options) {
00500 Filename orig_filename(filename_pattern);
00501 orig_filename.set_pattern(true);
00502
00503 Filename filename;
00504 {
00505 MutexHolder holder(_lock);
00506 resolve_filename(filename, orig_filename, read_mipmaps, options);
00507
00508 Textures::const_iterator ti;
00509 ti = _textures.find(filename);
00510 if (ti != _textures.end()) {
00511 if ((*ti).second->get_texture_type() == Texture::TT_3d_texture) {
00512
00513 return (*ti).second;
00514 }
00515 }
00516 }
00517
00518 PT(Texture) tex;
00519 PT(BamCacheRecord) record;
00520 bool store_record = false;
00521
00522 BamCache *cache = BamCache::get_global_ptr();
00523 bool compressed_cache_record = false;
00524 try_load_cache(tex, cache, filename, record, compressed_cache_record,
00525 options);
00526
00527 if (tex == (Texture *)NULL ||
00528 tex->get_texture_type() != Texture::TT_3d_texture) {
00529
00530
00531 gobj_cat.info()
00532 << "Loading 3-d texture " << filename << "\n";
00533 tex = ns_make_texture(filename.get_extension());
00534 tex->setup_3d_texture();
00535 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) {
00536
00537 report_texture_unreadable(filename);
00538 return NULL;
00539 }
00540 store_record = (record != (BamCacheRecord *)NULL);
00541 }
00542
00543 if (cache->get_cache_compressed_textures() && tex->has_compression()) {
00544 #ifndef HAVE_SQUISH
00545 bool needs_driver_compression = true;
00546 #else
00547 bool needs_driver_compression = driver_compress_textures;
00548 #endif // HAVE_SQUISH
00549 if (needs_driver_compression) {
00550
00551
00552 store_record = false;
00553 if (!compressed_cache_record) {
00554 tex->set_post_load_store_cache(true);
00555 }
00556 }
00557
00558 } else if (!cache->get_cache_textures()) {
00559
00560 store_record = false;
00561 }
00562
00563
00564 nassertr(tex != (Texture *)NULL, false);
00565 tex->set_filename(filename_pattern);
00566 tex->set_fullpath(filename);
00567 tex->_texture_pool_key = filename;
00568
00569 {
00570 MutexHolder holder(_lock);
00571
00572
00573 Textures::const_iterator ti;
00574 ti = _textures.find(filename);
00575 if (ti != _textures.end()) {
00576 if ((*ti).second->get_texture_type() == Texture::TT_3d_texture) {
00577
00578 return (*ti).second;
00579 }
00580 }
00581
00582 _textures[filename] = tex;
00583 }
00584
00585 if (store_record && tex->is_cacheable()) {
00586
00587 record->set_data(tex, tex);
00588 cache->store(record);
00589 }
00590
00591 nassertr(!tex->get_fullpath().empty(), tex);
00592 return tex;
00593 }
00594
00595
00596
00597
00598
00599
00600 Texture *TexturePool::
00601 ns_load_2d_texture_array(const Filename &filename_pattern,
00602 bool read_mipmaps, const LoaderOptions &options) {
00603 Filename orig_filename(filename_pattern);
00604 orig_filename.set_pattern(true);
00605
00606 Filename filename;
00607 Filename unique_filename;
00608 {
00609 MutexHolder holder(_lock);
00610 resolve_filename(filename, orig_filename, read_mipmaps, options);
00611
00612 unique_filename = filename + ".2DARRAY";
00613
00614 Textures::const_iterator ti;
00615 ti = _textures.find(unique_filename);
00616 if (ti != _textures.end()) {
00617 if ((*ti).second->get_texture_type() == Texture::TT_2d_texture_array) {
00618
00619 return (*ti).second;
00620 }
00621 }
00622 }
00623
00624 PT(Texture) tex;
00625 PT(BamCacheRecord) record;
00626 bool store_record = false;
00627
00628 BamCache *cache = BamCache::get_global_ptr();
00629 bool compressed_cache_record = false;
00630 try_load_cache(tex, cache, filename, record, compressed_cache_record,
00631 options);
00632
00633 if (tex == (Texture *)NULL ||
00634 tex->get_texture_type() != Texture::TT_2d_texture_array) {
00635
00636
00637 gobj_cat.info()
00638 << "Loading 2-d texture array " << filename << "\n";
00639 tex = ns_make_texture(filename.get_extension());
00640 tex->setup_2d_texture_array();
00641 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) {
00642
00643 report_texture_unreadable(filename);
00644 return NULL;
00645 }
00646 store_record = (record != (BamCacheRecord *)NULL);
00647 }
00648
00649 if (cache->get_cache_compressed_textures() && tex->has_compression()) {
00650 #ifndef HAVE_SQUISH
00651 bool needs_driver_compression = true;
00652 #else
00653 bool needs_driver_compression = driver_compress_textures;
00654 #endif // HAVE_SQUISH
00655 if (needs_driver_compression) {
00656
00657
00658 store_record = false;
00659 if (!compressed_cache_record) {
00660 tex->set_post_load_store_cache(true);
00661 }
00662 }
00663
00664 } else if (!cache->get_cache_textures()) {
00665
00666 store_record = false;
00667 }
00668
00669
00670 nassertr(tex != (Texture *)NULL, false);
00671 tex->set_filename(filename_pattern);
00672 tex->set_fullpath(filename);
00673 tex->_texture_pool_key = filename;
00674
00675 {
00676 MutexHolder holder(_lock);
00677
00678
00679 Textures::const_iterator ti;
00680 ti = _textures.find(unique_filename);
00681 if (ti != _textures.end()) {
00682 if ((*ti).second->get_texture_type() == Texture::TT_2d_texture_array) {
00683
00684 return (*ti).second;
00685 }
00686 }
00687
00688 _textures[unique_filename] = tex;
00689 }
00690
00691 if (store_record && tex->is_cacheable()) {
00692
00693 record->set_data(tex, tex);
00694 cache->store(record);
00695 }
00696
00697 nassertr(!tex->get_fullpath().empty(), tex);
00698 return tex;
00699 }
00700
00701
00702
00703
00704
00705
00706 Texture *TexturePool::
00707 ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps,
00708 const LoaderOptions &options) {
00709 Filename orig_filename(filename_pattern);
00710 orig_filename.set_pattern(true);
00711
00712 Filename filename;
00713 {
00714 MutexHolder holder(_lock);
00715 resolve_filename(filename, orig_filename, read_mipmaps, options);
00716
00717 Textures::const_iterator ti;
00718 ti = _textures.find(filename);
00719 if (ti != _textures.end()) {
00720
00721 return (*ti).second;
00722 }
00723 }
00724
00725 PT(Texture) tex;
00726 PT(BamCacheRecord) record;
00727 bool store_record = false;
00728
00729 BamCache *cache = BamCache::get_global_ptr();
00730 bool compressed_cache_record = false;
00731 try_load_cache(tex, cache, filename, record, compressed_cache_record,
00732 options);
00733
00734 if (tex == (Texture *)NULL ||
00735 tex->get_texture_type() != Texture::TT_cube_map) {
00736
00737
00738 gobj_cat.info()
00739 << "Loading cube map texture " << filename << "\n";
00740 tex = ns_make_texture(filename.get_extension());
00741 tex->setup_cube_map();
00742 if (!tex->read(filename, 0, 0, true, read_mipmaps, options)) {
00743
00744 report_texture_unreadable(filename);
00745 return NULL;
00746 }
00747 store_record = (record != (BamCacheRecord *)NULL);
00748 }
00749
00750 if (cache->get_cache_compressed_textures() && tex->has_compression()) {
00751 #ifndef HAVE_SQUISH
00752 bool needs_driver_compression = true;
00753 #else
00754 bool needs_driver_compression = driver_compress_textures;
00755 #endif // HAVE_SQUISH
00756 if (needs_driver_compression) {
00757
00758
00759 store_record = false;
00760 if (!compressed_cache_record) {
00761 tex->set_post_load_store_cache(true);
00762 }
00763 }
00764
00765 } else if (!cache->get_cache_textures()) {
00766
00767 store_record = false;
00768 }
00769
00770
00771 nassertr(tex != (Texture *)NULL, false);
00772 tex->set_filename(filename_pattern);
00773 tex->set_fullpath(filename);
00774 tex->_texture_pool_key = filename;
00775
00776 {
00777 MutexHolder holder(_lock);
00778
00779
00780 Textures::const_iterator ti;
00781 ti = _textures.find(filename);
00782 if (ti != _textures.end()) {
00783
00784 return (*ti).second;
00785 }
00786
00787 _textures[filename] = tex;
00788 }
00789
00790 if (store_record && tex->is_cacheable()) {
00791
00792 record->set_data(tex, tex);
00793 cache->store(record);
00794 }
00795
00796 nassertr(!tex->get_fullpath().empty(), tex);
00797 return tex;
00798 }
00799
00800
00801
00802
00803
00804
00805 Texture *TexturePool::
00806 ns_get_normalization_cube_map(int size) {
00807 MutexHolder holder(_lock);
00808
00809 if (_normalization_cube_map == (Texture *)NULL) {
00810 _normalization_cube_map = new Texture("normalization_cube_map");
00811 }
00812 if (_normalization_cube_map->get_x_size() < size ||
00813 _normalization_cube_map->get_texture_type() != Texture::TT_cube_map) {
00814 _normalization_cube_map->generate_normalization_cube_map(size);
00815 }
00816
00817 return _normalization_cube_map;
00818 }
00819
00820
00821
00822
00823
00824
00825 Texture *TexturePool::
00826 ns_get_alpha_scale_map() {
00827 MutexHolder holder(_lock);
00828
00829 if (_alpha_scale_map == (Texture *)NULL) {
00830 _alpha_scale_map = new Texture("alpha_scale_map");
00831 _alpha_scale_map->generate_alpha_scale_map();
00832 }
00833
00834 return _alpha_scale_map;
00835 }
00836
00837
00838
00839
00840
00841
00842 void TexturePool::
00843 ns_add_texture(Texture *tex) {
00844 PT(Texture) keep = tex;
00845 MutexHolder holder(_lock);
00846
00847 if (!tex->_texture_pool_key.empty()) {
00848 ns_release_texture(tex);
00849 }
00850 string filename = tex->get_fullpath();
00851 if (filename.empty()) {
00852 gobj_cat.error() << "Attempt to call add_texture() on an unnamed texture.\n";
00853 }
00854
00855
00856 tex->_texture_pool_key = filename;
00857 _textures[filename] = tex;
00858 nassertv(!tex->get_fullpath().empty());
00859 }
00860
00861
00862
00863
00864
00865
00866 void TexturePool::
00867 ns_release_texture(Texture *tex) {
00868 MutexHolder holder(_lock);
00869
00870 if (!tex->_texture_pool_key.empty()) {
00871 Textures::iterator ti;
00872 ti = _textures.find(tex->_texture_pool_key);
00873 if (ti != _textures.end() && (*ti).second == tex) {
00874 _textures.erase(ti);
00875 }
00876 tex->_texture_pool_key = string();
00877 }
00878
00879
00880 _relpath_lookup.clear();
00881 }
00882
00883
00884
00885
00886
00887
00888 void TexturePool::
00889 ns_release_all_textures() {
00890 MutexHolder holder(_lock);
00891
00892 Textures::iterator ti;
00893 for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00894 Texture *tex = (*ti).second;
00895 tex->_texture_pool_key = string();
00896 }
00897
00898 _textures.clear();
00899 _normalization_cube_map = NULL;
00900
00901
00902 _relpath_lookup.clear();
00903 }
00904
00905
00906
00907
00908
00909
00910 int TexturePool::
00911 ns_garbage_collect() {
00912 MutexHolder holder(_lock);
00913
00914 int num_released = 0;
00915 Textures new_set;
00916
00917 Textures::iterator ti;
00918 for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00919 Texture *tex = (*ti).second;
00920 if (tex->get_ref_count() == 1) {
00921 if (gobj_cat.is_debug()) {
00922 gobj_cat.debug()
00923 << "Releasing " << (*ti).first << "\n";
00924 }
00925 ++num_released;
00926 tex->_texture_pool_key = string();
00927 } else {
00928 new_set.insert(new_set.end(), *ti);
00929 }
00930 }
00931
00932 _textures.swap(new_set);
00933
00934 if (_normalization_cube_map != (Texture *)NULL &&
00935 _normalization_cube_map->get_ref_count() == 1) {
00936 if (gobj_cat.is_debug()) {
00937 gobj_cat.debug()
00938 << "Releasing normalization cube map\n";
00939 }
00940 ++num_released;
00941 _normalization_cube_map = NULL;
00942 }
00943
00944 return num_released;
00945 }
00946
00947
00948
00949
00950
00951
00952 void TexturePool::
00953 ns_list_contents(ostream &out) const {
00954 MutexHolder holder(_lock);
00955
00956 int total_size;
00957 int total_ram_size;
00958 Textures::const_iterator ti;
00959
00960 out << "texture pool contents:\n";
00961
00962 total_size = 0;
00963 total_ram_size = 0;
00964 for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00965 Texture *tex = (*ti).second;
00966 out << (*ti).first << "\n";
00967 out << " (count = " << tex->get_ref_count()
00968 << ", ram = " << tex->get_ram_image_size()
00969 << ", size = " << tex->get_ram_page_size()
00970 << ", w = " << tex->get_x_size()
00971 << ", h = " << tex->get_y_size()
00972 << ")\n";
00973 nassertv(tex->_texture_pool_key == (*ti).first);
00974 total_ram_size += tex->get_ram_image_size();
00975 total_size += tex->get_ram_page_size();
00976 }
00977
00978 out << "total number of textures: " << _textures.size() << "\n";
00979 out << "texture pool ram : " << total_ram_size << "\n";
00980 out << "texture pool size: " << total_size << "\n";
00981 out << "texture pool size - texture pool ram: " << total_size - total_ram_size << "\n";
00982 }
00983
00984
00985
00986
00987
00988
00989 Texture *TexturePool::
00990 ns_find_texture(const string &name) const {
00991 MutexHolder holder(_lock);
00992 GlobPattern glob(name);
00993
00994 Textures::const_iterator ti;
00995 for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
00996 Texture *tex = (*ti).second;
00997 if (glob.matches(tex->get_name())) {
00998 return tex;
00999 }
01000 }
01001
01002 return NULL;
01003 }
01004
01005
01006
01007
01008
01009
01010 TextureCollection TexturePool::
01011 ns_find_all_textures(const string &name) const {
01012 MutexHolder holder(_lock);
01013 TextureCollection result;
01014 GlobPattern glob(name);
01015
01016 Textures::const_iterator ti;
01017 for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
01018 Texture *tex = (*ti).second;
01019 if (glob.matches(tex->get_name())) {
01020 result.add_texture(tex);
01021 }
01022 }
01023
01024 return result;
01025 }
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035 PT(Texture) TexturePool::
01036 ns_make_texture(const string &extension) const {
01037 MakeTextureFunc *func = get_texture_type(extension);
01038 if (func != NULL) {
01039 return func();
01040 }
01041
01042
01043
01044 return new Texture;
01045 }
01046
01047
01048
01049
01050
01051
01052
01053
01054
01055 void TexturePool::
01056 resolve_filename(Filename &new_filename, const Filename &orig_filename,
01057 bool read_mipmaps, const LoaderOptions &options) {
01058 if (!_fake_texture_image.empty()) {
01059 new_filename = _fake_texture_image;
01060 return;
01061 }
01062
01063 RelpathLookup::iterator rpi = _relpath_lookup.find(orig_filename);
01064 if (rpi != _relpath_lookup.end()) {
01065 new_filename = (*rpi).second;
01066 return;
01067 }
01068
01069 new_filename = orig_filename;
01070 if (read_mipmaps || (options.get_texture_flags() & LoaderOptions::TF_multiview)) {
01071 new_filename.set_pattern(true);
01072 }
01073
01074 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
01075 vfs->resolve_filename(new_filename, get_model_path());
01076
01077 _relpath_lookup[orig_filename] = new_filename;
01078 }
01079
01080
01081
01082
01083
01084
01085 void TexturePool::
01086 try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename,
01087 PT(BamCacheRecord) &record, bool &compressed_cache_record,
01088 const LoaderOptions &options) {
01089 if (tex == (Texture *)NULL) {
01090
01091
01092 if ((cache->get_cache_textures() || cache->get_cache_compressed_textures()) && !textures_header_only) {
01093 record = cache->lookup(filename, "txo");
01094 if (record != (BamCacheRecord *)NULL) {
01095 if (record->has_data()) {
01096 tex = DCAST(Texture, record->get_data());
01097 compressed_cache_record = (tex->get_ram_image_compression() != Texture::CM_off);
01098 int x_size = tex->get_orig_file_x_size();
01099 int y_size = tex->get_orig_file_y_size();
01100 tex->adjust_this_size(x_size, y_size, filename.get_basename(), true);
01101
01102 if (!cache->get_cache_textures() && !compressed_cache_record) {
01103
01104 if (gobj_cat.is_debug()) {
01105 gobj_cat.debug()
01106 << "Not caching uncompressed texture " << *tex << "\n";
01107 }
01108 tex = NULL;
01109 record = NULL;
01110
01111 } else if (x_size != tex->get_x_size() ||
01112 y_size != tex->get_y_size()) {
01113
01114
01115
01116
01117 if (gobj_cat.is_debug()) {
01118 gobj_cat.debug()
01119 << "Cached texture " << *tex << " has size "
01120 << tex->get_x_size() << " x " << tex->get_y_size()
01121 << " instead of " << x_size << " x " << y_size
01122 << "; dropping cache.\n";
01123 }
01124 tex = NULL;
01125
01126 } else if (!tex->has_compression() && tex->get_ram_image_compression() != Texture::CM_off) {
01127
01128
01129 if (gobj_cat.is_debug()) {
01130 gobj_cat.debug()
01131 << "Cached texture " << *tex
01132 << " is compressed in cache; dropping cache.\n";
01133 }
01134 tex = NULL;
01135
01136 } else {
01137 gobj_cat.info()
01138 << "Texture " << filename << " found in disk cache.\n";
01139 if ((options.get_texture_flags() & LoaderOptions::TF_preload_simple) &&
01140 !tex->has_simple_ram_image()) {
01141 tex->generate_simple_ram_image();
01142 }
01143 if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) {
01144
01145 tex->clear_ram_image();
01146
01147 } else {
01148 bool was_compressed = (tex->get_ram_image_compression() != Texture::CM_off);
01149 if (tex->consider_auto_process_ram_image(tex->uses_mipmaps(), true)) {
01150 bool is_compressed = (tex->get_ram_image_compression() != Texture::CM_off);
01151 if (!was_compressed && is_compressed &&
01152 cache->get_cache_compressed_textures()) {
01153
01154
01155
01156
01157 record->set_data(tex, tex);
01158 cache->store(record);
01159 compressed_cache_record = true;
01160 }
01161 }
01162 }
01163 tex->set_keep_ram_image(false);
01164 }
01165 } else {
01166 if (!cache->get_cache_textures()) {
01167
01168
01169
01170 if (gobj_cat.is_debug()) {
01171 gobj_cat.debug()
01172 << "Not caching uncompressed texture\n";
01173 }
01174 record = NULL;
01175 }
01176 }
01177 }
01178 }
01179 }
01180 }
01181
01182
01183
01184
01185
01186
01187
01188 void TexturePool::
01189 report_texture_unreadable(const Filename &filename) const {
01190 VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
01191 bool has_hash = (filename.get_fullpath().find('#') != string::npos);
01192 if (!has_hash && !vfs->exists(filename)) {
01193 if (filename.is_local()) {
01194
01195
01196
01197 gobj_cat.error()
01198 << "Unable to find texture \"" << filename << "\""
01199 << " on model-path " << get_model_path() <<"\n";
01200 } else {
01201
01202
01203 gobj_cat.error()
01204 << "Texture \"" << filename << "\" does not exist.\n";
01205 }
01206
01207 } else {
01208
01209 if (!has_hash) {
01210 gobj_cat.error()
01211 << "Texture \"" << filename << "\" exists but cannot be read.\n";
01212 } else {
01213
01214
01215 gobj_cat.error()
01216 << "Texture \"" << filename << "\" cannot be read.\n";
01217 }
01218
01219
01220 MakeTextureFunc *func = get_texture_type(filename.get_extension());
01221 if (func == (MakeTextureFunc *)NULL) {
01222 gobj_cat.error()
01223 << "Texture extension \"" << filename.get_extension()
01224 << "\" is unknown. Supported texture types:\n";
01225 write_texture_types(gobj_cat.error(false), 2);
01226 }
01227 }
01228 }
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238 PT(Texture) TexturePool::
01239 pre_load(const Filename &orig_filename, const Filename &orig_alpha_filename,
01240 int primary_file_num_channels, int alpha_file_channel,
01241 bool read_mipmaps, const LoaderOptions &options) {
01242 PT(Texture) tex;
01243
01244 MutexHolder holder(_lock);
01245
01246 FilterRegistry::iterator fi;
01247 for (fi = _filter_registry.begin();
01248 fi != _filter_registry.end();
01249 ++fi) {
01250 tex = (*fi)->pre_load(orig_filename, orig_alpha_filename,
01251 primary_file_num_channels, alpha_file_channel,
01252 read_mipmaps, options);
01253 if (tex != (Texture *)NULL) {
01254 return tex;
01255 }
01256 }
01257
01258 return tex;
01259 }
01260
01261
01262
01263
01264
01265
01266 PT(Texture) TexturePool::
01267 post_load(Texture *tex) {
01268 PT(Texture) result = tex;
01269
01270 MutexHolder holder(_lock);
01271
01272 FilterRegistry::iterator fi;
01273 for (fi = _filter_registry.begin();
01274 fi != _filter_registry.end();
01275 ++fi) {
01276 result = (*fi)->post_load(result);
01277 }
01278
01279 return result;
01280 }
01281
01282
01283
01284
01285
01286
01287
01288
01289 void TexturePool::
01290 load_filters() {
01291 ConfigVariableList texture_filter
01292 ("texture-filter",
01293 PRC_DESC("Names one or more external libraries that should be loaded for the "
01294 "purposes of performing texture filtering. This variable may be repeated several "
01295 "times. As in load-display, the actual library filename is derived by "
01296 "prefixing 'lib' to the specified name."));
01297
01298 int num_aux = texture_filter.get_num_unique_values();
01299 for (int i = 0; i < num_aux; i++) {
01300 string name = texture_filter.get_unique_value(i);
01301
01302 Filename dlname = Filename::dso_filename("lib" + name + ".so");
01303 gobj_cat->info()
01304 << "loading texture filter: " << dlname.to_os_specific() << endl;
01305 void *tmp = load_dso(get_plugin_path().get_value(), dlname);
01306 if (tmp == (void *)NULL) {
01307 gobj_cat.info()
01308 << "Unable to load: " << load_dso_error() << endl;
01309 }
01310 }
01311 }