Panda3D
texturePool.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file texturePool.cxx
10  * @author drose
11  * @date 2000-04-26
12  * @author fperazzi, PandaSE
13  * @date 2010-04-29
14  */
15 
16 #include "texturePool.h"
17 #include "config_gobj.h"
18 #include "config_putil.h"
19 #include "config_express.h"
20 #include "string_utils.h"
21 #include "virtualFileSystem.h"
22 #include "bamCache.h"
23 #include "bamCacheRecord.h"
24 #include "pnmFileTypeRegistry.h"
25 #include "texturePoolFilter.h"
26 #include "configVariableList.h"
27 #include "load_dso.h"
28 #include "mutexHolder.h"
29 #include "dcast.h"
30 
31 using std::istream;
32 using std::ostream;
33 using std::string;
34 
35 TexturePool *TexturePool::_global_ptr;
36 
37 /**
38  * Lists the contents of the texture pool to the indicated output stream. For
39  * debugging.
40  */
41 void TexturePool::
42 write(ostream &out) {
43  get_global_ptr()->ns_list_contents(out);
44 }
45 
46 /**
47  * Records a factory function that makes a Texture object of the appropriate
48  * type for one or more particular filename extensions. The string extensions
49  * may be a string that contains space-separated list of extensions, case-
50  * insensitive.
51  */
52 void TexturePool::
53 register_texture_type(MakeTextureFunc *func, const string &extensions) {
54  MutexHolder holder(_lock);
55 
56  vector_string words;
57  extract_words(downcase(extensions), words);
58 
59  vector_string::const_iterator wi;
60  for (wi = words.begin(); wi != words.end(); ++wi) {
61  _type_registry[*wi] = func;
62  }
63 }
64 
65 /**
66  * Records a TexturePoolFilter object that may operate on texture images as
67  * they are loaded from disk.
68  */
69 void TexturePool::
71  MutexHolder holder(_lock);
72 
73  gobj_cat.info()
74  << "Registering Texture filter " << *filter << "\n";
75  _filter_registry.push_back(filter);
76 }
77 
78 /**
79  * Returns the factory function to construct a new texture of the type
80  * appropriate for the indicated filename extension, if any, or NULL if the
81  * extension is not one of the extensions for a texture file.
82  */
83 TexturePool::MakeTextureFunc *TexturePool::
84 get_texture_type(const string &extension) const {
85  MutexHolder holder(_lock);
86 
87  string c = downcase(extension);
88  TypeRegistry::const_iterator ti;
89  ti = _type_registry.find(c);
90  if (ti != _type_registry.end()) {
91  return (*ti).second;
92  }
93 
94  // Check the PNM type registry.
96  PNMFileType *type = pnm_reg->get_type_from_extension(c);
97  if (type != nullptr || c == "txo" || c == "dds" || c == "ktx") {
98  // This is a known image type; create an ordinary Texture.
99  ((TexturePool *)this)->_type_registry[c] = Texture::make_texture;
100  return Texture::make_texture;
101  }
102 
103  // This is an unknown texture type.
104  return nullptr;
105 }
106 
107 /**
108  * Outputs a list of the available texture types to the indicated output
109  * stream. This is mostly the list of available image types, with maybe a few
110  * additional ones for video textures.
111  */
112 void TexturePool::
113 write_texture_types(ostream &out, int indent_level) const {
114  MutexHolder holder(_lock);
115 
116  // These are supported out of the box.
117  indent(out, indent_level) << "Texture Object .txo\n";
118  indent(out, indent_level) << "DirectDraw Surface .dds\n";
119  indent(out, indent_level) << "Khronos Texture .ktx\n";
120 
122  pnm_reg->write(out, indent_level);
123 
124  // Also output any of the additional texture types, that aren't strictly
125  // images (these are typically video textures).
126  TypeRegistry::const_iterator ti;
127  for (ti = _type_registry.begin(); ti != _type_registry.end(); ++ti) {
128  string extension = (*ti).first;
129  MakeTextureFunc *func = (*ti).second;
130 
131  if (pnm_reg->get_type_from_extension(extension) == nullptr) {
132  PT(Texture) tex = func();
133  string name = tex->get_type().get_name();
134  indent(out, indent_level) << name;
135  indent(out, std::max(30 - (int)name.length(), 0))
136  << " ." << extension << "\n";
137  }
138  }
139 }
140 
141 /**
142  * Initializes and/or returns the global pointer to the one TexturePool object
143  * in the system.
144  */
147  if (_global_ptr == nullptr) {
148  _global_ptr = new TexturePool;
149 
150  // We have to call this here, not in the constructor, so that the
151  // _global_ptr is safely assigned by the time the filters begin to load.
152  _global_ptr->load_filters();
153  }
154  return _global_ptr;
155 }
156 
157 /**
158  * The constructor is not intended to be called directly; there's only
159  * supposed to be one TexturePool in the universe and it constructs itself.
160  */
161 TexturePool::
162 TexturePool() {
163  ConfigVariableFilename fake_texture_image
164  ("fake-texture-image", "",
165  PRC_DESC("Set this to enable a speedy-load mode in which you don't care "
166  "what the world looks like, you just want it to load in minimal "
167  "time. This causes all texture loads via the TexturePool to use "
168  "the same texture file, which will presumably only be loaded "
169  "once."));
170  _fake_texture_image = fake_texture_image;
171 }
172 
173 /**
174  * The nonstatic implementation of has_texture().
175  */
176 bool TexturePool::
177 ns_has_texture(const Filename &orig_filename) {
178  MutexHolder holder(_lock);
179 
180  LookupKey key;
181  resolve_filename(key._fullpath, orig_filename, false, LoaderOptions());
182 
183  Textures::const_iterator ti;
184  ti = _textures.find(key);
185  if (ti != _textures.end()) {
186  // This texture was previously loaded.
187  return true;
188  }
189 
190  // It might still have been loaded with non-standard settings.
191  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
192  if (ti->first._fullpath == key._fullpath) {
193  return true;
194  }
195  }
196 
197  return false;
198 }
199 
200 /**
201  * The nonstatic implementation of load_texture().
202  */
203 Texture *TexturePool::
204 ns_load_texture(const Filename &orig_filename, int primary_file_num_channels,
205  bool read_mipmaps, const LoaderOptions &options) {
206  LookupKey key;
207  key._primary_file_num_channels = primary_file_num_channels;
208  {
209  MutexHolder holder(_lock);
210  resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
211 
212  Textures::const_iterator ti;
213  ti = _textures.find(key);
214  if (ti != _textures.end()) {
215  // This texture was previously loaded.
216  Texture *tex = (*ti).second;
217  nassertr(!tex->get_fullpath().empty(), tex);
218  return tex;
219  }
220  }
221 
222  // The texture was not found in the pool.
223  PT(Texture) tex;
224  PT(BamCacheRecord) record;
225  bool store_record = false;
226 
227  // Can one of our texture filters supply the texture?
228  tex = pre_load(orig_filename, Filename(), primary_file_num_channels, 0,
229  read_mipmaps, options);
230 
232  bool compressed_cache_record = false;
233  try_load_cache(tex, cache, key._fullpath, record, compressed_cache_record,
234  options);
235 
236  if (tex == nullptr) {
237  // The texture was neither in the pool, nor found in the on-disk cache; it
238  // needs to be loaded from its source image(s).
239  gobj_cat.info()
240  << "Loading texture " << key._fullpath << "\n";
241 
242  string ext = downcase(key._fullpath.get_extension());
243  if (ext == "txo" || ext == "bam") {
244  // Assume this is a txo file, which might conceivably contain a movie
245  // file or some other subclass of Texture. In that case, use
246  // make_from_txo() to load it instead of read().
248 
249  key._fullpath.set_binary();
250  PT(VirtualFile) file = vfs->get_file(key._fullpath);
251  if (file == nullptr) {
252  // No such file.
253  gobj_cat.error()
254  << "Could not find " << key._fullpath << "\n";
255  return nullptr;
256  }
257 
258  if (gobj_cat.is_debug()) {
259  gobj_cat.debug()
260  << "Reading texture object " << key._fullpath << "\n";
261  }
262 
263  istream *in = file->open_read_file(true);
264  tex = Texture::make_from_txo(*in, key._fullpath);
265  vfs->close_read_file(in);
266 
267  if (tex == nullptr) {
268  return nullptr;
269  }
270  tex->set_fullpath(key._fullpath);
271  tex->clear_alpha_fullpath();
272  tex->set_keep_ram_image(false);
273 
274  } else {
275  // Read it the conventional way.
276  tex = ns_make_texture(ext);
277  if (!tex->read(key._fullpath, Filename(), primary_file_num_channels, 0,
278  0, 0, false, read_mipmaps, record, options)) {
279  // This texture was not found or could not be read.
280  report_texture_unreadable(key._fullpath);
281  return nullptr;
282  }
283  }
284 
285  if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) {
287  }
288 
289  store_record = (record != nullptr);
290  }
291 
292  if (cache->get_cache_compressed_textures() && tex->has_compression()) {
293 #ifndef HAVE_SQUISH
294  bool needs_driver_compression = true;
295 #else
296  bool needs_driver_compression = driver_compress_textures;
297 #endif // HAVE_SQUISH
298  if (needs_driver_compression) {
299  // We don't want to save the uncompressed version; we'll save the
300  // compressed version when it becomes available.
301  store_record = false;
302  if (!compressed_cache_record) {
303  tex->set_post_load_store_cache(true);
304  }
305  }
306 
307  } else if (!cache->get_cache_textures()) {
308  // We don't want to save this texture.
309  store_record = false;
310  }
311 
312  // Set the original filename, before we searched along the path.
313  nassertr(tex != nullptr, nullptr);
314  tex->set_filename(orig_filename);
315  tex->set_fullpath(key._fullpath);
316  tex->_texture_pool_key = key._fullpath;
317 
318  {
319  MutexHolder holder(_lock);
320 
321  // Now look again--someone may have just loaded this texture in another
322  // thread.
323  Textures::const_iterator ti;
324  ti = _textures.find(key);
325  if (ti != _textures.end()) {
326  // This texture was previously loaded.
327  Texture *tex = (*ti).second;
328  nassertr(!tex->get_fullpath().empty(), tex);
329  return tex;
330  }
331 
332  _textures[std::move(key)] = tex;
333  }
334 
335  if (store_record && tex->is_cacheable()) {
336  // Store the on-disk cache record for next time.
337  record->set_data(tex);
338  cache->store(record);
339  }
340 
341  if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) {
342  // And now drop the RAM until we need it.
343  tex->clear_ram_image();
344  }
345 
346  nassertr(!tex->get_fullpath().empty(), tex);
347 
348  // Finally, apply any post-loading texture filters.
349  tex = post_load(tex);
350 
351  return tex;
352 }
353 
354 /**
355  * The nonstatic implementation of load_texture().
356  */
357 Texture *TexturePool::
358 ns_load_texture(const Filename &orig_filename,
359  const Filename &orig_alpha_filename,
360  int primary_file_num_channels,
361  int alpha_file_channel,
362  bool read_mipmaps, const LoaderOptions &options) {
363  if (!_fake_texture_image.empty()) {
364  return ns_load_texture(_fake_texture_image, primary_file_num_channels,
365  read_mipmaps, options);
366  }
367 
368  LookupKey key;
369  key._primary_file_num_channels = primary_file_num_channels;
370  key._alpha_file_channel = alpha_file_channel;
371  {
372  MutexHolder holder(_lock);
373  resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
374  resolve_filename(key._alpha_fullpath, orig_alpha_filename, read_mipmaps, options);
375 
376  Textures::const_iterator ti;
377  ti = _textures.find(key);
378  if (ti != _textures.end()) {
379  // This texture was previously loaded.
380  Texture *tex = (*ti).second;
381  nassertr(!tex->get_fullpath().empty(), tex);
382  return tex;
383  }
384  }
385 
386  PT(Texture) tex;
387  PT(BamCacheRecord) record;
388  bool store_record = false;
389 
390  // Can one of our texture filters supply the texture?
391  tex = pre_load(orig_filename, orig_alpha_filename, primary_file_num_channels,
392  alpha_file_channel, read_mipmaps, options);
393 
395  bool compressed_cache_record = false;
396  try_load_cache(tex, cache, key._fullpath, record, compressed_cache_record,
397  options);
398 
399  if (tex == nullptr) {
400  // The texture was neither in the pool, nor found in the on-disk cache; it
401  // needs to be loaded from its source image(s).
402  gobj_cat.info()
403  << "Loading texture " << key._fullpath << " and alpha component "
404  << key._alpha_fullpath << std::endl;
405  tex = ns_make_texture(key._fullpath.get_extension());
406  if (!tex->read(key._fullpath, key._alpha_fullpath, primary_file_num_channels,
407  alpha_file_channel, 0, 0, false, read_mipmaps, nullptr,
408  options)) {
409  // This texture was not found or could not be read.
410  report_texture_unreadable(key._fullpath);
411  return nullptr;
412  }
413 
414  if (options.get_texture_flags() & LoaderOptions::TF_preload_simple) {
416  }
417 
418  store_record = (record != nullptr);
419  }
420 
421  if (cache->get_cache_compressed_textures() && tex->has_compression()) {
422 #ifndef HAVE_SQUISH
423  bool needs_driver_compression = true;
424 #else
425  bool needs_driver_compression = driver_compress_textures;
426 #endif // HAVE_SQUISH
427  if (needs_driver_compression) {
428  // We don't want to save the uncompressed version; we'll save the
429  // compressed version when it becomes available.
430  store_record = false;
431  if (!compressed_cache_record) {
432  tex->set_post_load_store_cache(true);
433  }
434  }
435 
436  } else if (!cache->get_cache_textures()) {
437  // We don't want to save this texture.
438  store_record = false;
439  }
440 
441  // Set the original filenames, before we searched along the path.
442  nassertr(tex != nullptr, nullptr);
443  tex->set_filename(orig_filename);
444  tex->set_fullpath(key._fullpath);
445  tex->set_alpha_filename(orig_alpha_filename);
446  tex->set_alpha_fullpath(key._alpha_fullpath);
447  tex->_texture_pool_key = key._fullpath;
448 
449  {
450  MutexHolder holder(_lock);
451 
452  // Now look again.
453  Textures::const_iterator ti;
454  ti = _textures.find(key);
455  if (ti != _textures.end()) {
456  // This texture was previously loaded.
457  Texture *tex = (*ti).second;
458  nassertr(!tex->get_fullpath().empty(), tex);
459  return tex;
460  }
461 
462  _textures[std::move(key)] = tex;
463  }
464 
465  if (store_record && tex->is_cacheable()) {
466  // Store the on-disk cache record for next time.
467  record->set_data(tex);
468  cache->store(record);
469  }
470 
471  if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) {
472  // And now drop the RAM until we need it.
473  tex->clear_ram_image();
474  }
475 
476  nassertr(!tex->get_fullpath().empty(), tex);
477 
478  // Finally, apply any post-loading texture filters.
479  tex = post_load(tex);
480 
481  return tex;
482 }
483 
484 /**
485  * The nonstatic implementation of load_3d_texture().
486  */
487 Texture *TexturePool::
488 ns_load_3d_texture(const Filename &filename_pattern,
489  bool read_mipmaps, const LoaderOptions &options) {
490  Filename orig_filename(filename_pattern);
491  orig_filename.set_pattern(true);
492 
493  LookupKey key;
494  key._texture_type = Texture::TT_3d_texture;
495  {
496  MutexHolder holder(_lock);
497  resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
498 
499  Textures::const_iterator ti;
500  ti = _textures.find(key);
501  if (ti != _textures.end()) {
502  // This texture was previously loaded.
503  Texture *tex = (*ti).second;
504  nassertr(!tex->get_fullpath().empty(), tex);
505  return tex;
506  }
507  }
508 
509  PT(Texture) tex;
510  PT(BamCacheRecord) record;
511  bool store_record = false;
512 
514  bool compressed_cache_record = false;
515  try_load_cache(tex, cache, key._fullpath, record, compressed_cache_record,
516  options);
517 
518  if (tex == nullptr ||
519  tex->get_texture_type() != Texture::TT_3d_texture) {
520  // The texture was neither in the pool, nor found in the on-disk cache; it
521  // needs to be loaded from its source image(s).
522  gobj_cat.info()
523  << "Loading 3-d texture " << key._fullpath << "\n";
524  tex = ns_make_texture(key._fullpath.get_extension());
525  tex->setup_3d_texture();
526  if (!tex->read(key._fullpath, 0, 0, true, read_mipmaps, options)) {
527  // This texture was not found or could not be read.
528  report_texture_unreadable(key._fullpath);
529  return nullptr;
530  }
531  store_record = (record != nullptr);
532  }
533 
534  if (cache->get_cache_compressed_textures() && tex->has_compression()) {
535 #ifndef HAVE_SQUISH
536  bool needs_driver_compression = true;
537 #else
538  bool needs_driver_compression = driver_compress_textures;
539 #endif // HAVE_SQUISH
540  if (needs_driver_compression) {
541  // We don't want to save the uncompressed version; we'll save the
542  // compressed version when it becomes available.
543  store_record = false;
544  if (!compressed_cache_record) {
545  tex->set_post_load_store_cache(true);
546  }
547  }
548 
549  } else if (!cache->get_cache_textures()) {
550  // We don't want to save this texture.
551  store_record = false;
552  }
553 
554  // Set the original filename, before we searched along the path.
555  nassertr(tex != nullptr, nullptr);
556  tex->set_filename(filename_pattern);
557  tex->set_fullpath(key._fullpath);
558  tex->_texture_pool_key = key._fullpath;
559 
560  {
561  MutexHolder holder(_lock);
562 
563  // Now look again.
564  Textures::const_iterator ti;
565  ti = _textures.find(key);
566  if (ti != _textures.end()) {
567  // This texture was previously loaded.
568  Texture *tex = (*ti).second;
569  nassertr(!tex->get_fullpath().empty(), tex);
570  return tex;
571  }
572 
573  _textures[std::move(key)] = tex;
574  }
575 
576  if (store_record && tex->is_cacheable()) {
577  // Store the on-disk cache record for next time.
578  record->set_data(tex);
579  cache->store(record);
580  }
581 
582  nassertr(!tex->get_fullpath().empty(), tex);
583  return tex;
584 }
585 
586 /**
587  * The nonstatic implementation of load_2d_texture_array().
588  */
589 Texture *TexturePool::
590 ns_load_2d_texture_array(const Filename &filename_pattern,
591  bool read_mipmaps, const LoaderOptions &options) {
592  Filename orig_filename(filename_pattern);
593  orig_filename.set_pattern(true);
594 
595  LookupKey key;
596  key._texture_type = Texture::TT_2d_texture_array;
597  {
598  MutexHolder holder(_lock);
599  resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
600 
601  Textures::const_iterator ti;
602  ti = _textures.find(key);
603  if (ti != _textures.end()) {
604  // This texture was previously loaded.
605  Texture *tex = (*ti).second;
606  nassertr(!tex->get_fullpath().empty(), tex);
607  return tex;
608  }
609  }
610 
611  PT(Texture) tex;
612  PT(BamCacheRecord) record;
613  bool store_record = false;
614 
616  bool compressed_cache_record = false;
617  try_load_cache(tex, cache, key._fullpath, record, compressed_cache_record,
618  options);
619 
620  if (tex == nullptr ||
621  tex->get_texture_type() != Texture::TT_2d_texture_array) {
622  // The texture was neither in the pool, nor found in the on-disk cache; it
623  // needs to be loaded from its source image(s).
624  gobj_cat.info()
625  << "Loading 2-d texture array " << key._fullpath << "\n";
626  tex = ns_make_texture(key._fullpath.get_extension());
627  tex->setup_2d_texture_array();
628  if (!tex->read(key._fullpath, 0, 0, true, read_mipmaps, options)) {
629  // This texture was not found or could not be read.
630  report_texture_unreadable(key._fullpath);
631  return nullptr;
632  }
633  store_record = (record != nullptr);
634  }
635 
636  if (cache->get_cache_compressed_textures() && tex->has_compression()) {
637 #ifndef HAVE_SQUISH
638  bool needs_driver_compression = true;
639 #else
640  bool needs_driver_compression = driver_compress_textures;
641 #endif // HAVE_SQUISH
642  if (needs_driver_compression) {
643  // We don't want to save the uncompressed version; we'll save the
644  // compressed version when it becomes available.
645  store_record = false;
646  if (!compressed_cache_record) {
647  tex->set_post_load_store_cache(true);
648  }
649  }
650 
651  } else if (!cache->get_cache_textures()) {
652  // We don't want to save this texture.
653  store_record = false;
654  }
655 
656  // Set the original filename, before we searched along the path.
657  nassertr(tex != nullptr, nullptr);
658  tex->set_filename(filename_pattern);
659  tex->set_fullpath(key._fullpath);
660  tex->_texture_pool_key = key._fullpath;
661 
662  {
663  MutexHolder holder(_lock);
664 
665  // Now look again.
666  Textures::const_iterator ti;
667  ti = _textures.find(key);
668  if (ti != _textures.end()) {
669  // This texture was previously loaded.
670  Texture *tex = (*ti).second;
671  nassertr(!tex->get_fullpath().empty(), tex);
672  return tex;
673  }
674 
675  _textures[std::move(key)] = tex;
676  }
677 
678  if (store_record && tex->is_cacheable()) {
679  // Store the on-disk cache record for next time.
680  record->set_data(tex);
681  cache->store(record);
682  }
683 
684  nassertr(!tex->get_fullpath().empty(), tex);
685  return tex;
686 }
687 
688 /**
689  * The nonstatic implementation of load_cube_map().
690  */
691 Texture *TexturePool::
692 ns_load_cube_map(const Filename &filename_pattern, bool read_mipmaps,
693  const LoaderOptions &options) {
694  Filename orig_filename(filename_pattern);
695  orig_filename.set_pattern(true);
696 
697  LookupKey key;
698  key._texture_type = Texture::TT_cube_map;
699  {
700  MutexHolder holder(_lock);
701  resolve_filename(key._fullpath, orig_filename, read_mipmaps, options);
702 
703  Textures::const_iterator ti;
704  ti = _textures.find(key);
705  if (ti != _textures.end()) {
706  // This texture was previously loaded.
707  Texture *tex = (*ti).second;
708  nassertr(!tex->get_fullpath().empty(), tex);
709  return tex;
710  }
711  }
712 
713  PT(Texture) tex;
714  PT(BamCacheRecord) record;
715  bool store_record = false;
716 
718  bool compressed_cache_record = false;
719  try_load_cache(tex, cache, key._fullpath, record, compressed_cache_record,
720  options);
721 
722  if (tex == nullptr ||
723  tex->get_texture_type() != Texture::TT_cube_map) {
724  // The texture was neither in the pool, nor found in the on-disk cache; it
725  // needs to be loaded from its source image(s).
726  gobj_cat.info()
727  << "Loading cube map texture " << key._fullpath << "\n";
728  tex = ns_make_texture(key._fullpath.get_extension());
729  tex->setup_cube_map();
730  if (!tex->read(key._fullpath, 0, 0, true, read_mipmaps, options)) {
731  // This texture was not found or could not be read.
732  report_texture_unreadable(key._fullpath);
733  return nullptr;
734  }
735  store_record = (record != nullptr);
736  }
737 
738  if (cache->get_cache_compressed_textures() && tex->has_compression()) {
739 #ifndef HAVE_SQUISH
740  bool needs_driver_compression = true;
741 #else
742  bool needs_driver_compression = driver_compress_textures;
743 #endif // HAVE_SQUISH
744  if (needs_driver_compression) {
745  // We don't want to save the uncompressed version; we'll save the
746  // compressed version when it becomes available.
747  store_record = false;
748  if (!compressed_cache_record) {
749  tex->set_post_load_store_cache(true);
750  }
751  }
752 
753  } else if (!cache->get_cache_textures()) {
754  // We don't want to save this texture.
755  store_record = false;
756  }
757 
758  // Set the original filename, before we searched along the path.
759  nassertr(tex != nullptr, nullptr);
760  tex->set_filename(filename_pattern);
761  tex->set_fullpath(key._fullpath);
762  tex->_texture_pool_key = key._fullpath;
763 
764  {
765  MutexHolder holder(_lock);
766 
767  // Now look again.
768  Textures::const_iterator ti;
769  ti = _textures.find(key);
770  if (ti != _textures.end()) {
771  // This texture was previously loaded.
772  Texture *tex = (*ti).second;
773  nassertr(!tex->get_fullpath().empty(), tex);
774  return tex;
775  }
776 
777  _textures[std::move(key)] = tex;
778  }
779 
780  if (store_record && tex->is_cacheable()) {
781  // Store the on-disk cache record for next time.
782  record->set_data(tex);
783  cache->store(record);
784  }
785 
786  nassertr(!tex->get_fullpath().empty(), tex);
787  return tex;
788 }
789 
790 /**
791  * The nonstatic implementation of get_normalization_cube_map().
792  */
793 Texture *TexturePool::
794 ns_get_normalization_cube_map(int size) {
795  MutexHolder holder(_lock);
796 
797  if (_normalization_cube_map == nullptr) {
798  _normalization_cube_map = new Texture("normalization_cube_map");
799  }
800  if (_normalization_cube_map->get_x_size() < size ||
801  _normalization_cube_map->get_texture_type() != Texture::TT_cube_map) {
802  _normalization_cube_map->generate_normalization_cube_map(size);
803  }
804 
805  return _normalization_cube_map;
806 }
807 
808 /**
809  * The nonstatic implementation of get_alpha_scale_map().
810  */
811 Texture *TexturePool::
812 ns_get_alpha_scale_map() {
813  MutexHolder holder(_lock);
814 
815  if (_alpha_scale_map == nullptr) {
816  _alpha_scale_map = new Texture("alpha_scale_map");
817  _alpha_scale_map->generate_alpha_scale_map();
818  }
819 
820  return _alpha_scale_map;
821 }
822 
823 /**
824  * The nonstatic implementation of add_texture().
825  */
826 void TexturePool::
827 ns_add_texture(Texture *tex) {
828  PT(Texture) keep = tex;
829  MutexHolder holder(_lock);
830 
831  if (!tex->_texture_pool_key.empty()) {
832  ns_release_texture(tex);
833  }
834 
835  Texture::CDReader tex_cdata(tex->_cycler);
836  if (tex_cdata->_fullpath.empty()) {
837  gobj_cat.error() << "Attempt to call add_texture() on an unnamed texture.\n";
838  return;
839  }
840 
841  LookupKey key;
842  key._fullpath = tex_cdata->_fullpath;
843  key._alpha_fullpath = tex_cdata->_alpha_fullpath;
844  key._alpha_file_channel = tex_cdata->_alpha_file_channel;
845  key._texture_type = tex_cdata->_texture_type;
846 
847  // We blow away whatever texture was there previously, if any.
848  tex->_texture_pool_key = key._fullpath;
849  _textures[key] = tex;
850 }
851 
852 /**
853  * The nonstatic implementation of release_texture().
854  */
855 void TexturePool::
856 ns_release_texture(Texture *tex) {
857  MutexHolder holder(_lock);
858 
859  Textures::iterator ti;
860  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
861  if (tex == (*ti).second) {
862  _textures.erase(ti);
863  tex->_texture_pool_key = string();
864  break;
865  }
866  }
867 
868  // Blow away the cache of resolved relative filenames.
869  _relpath_lookup.clear();
870 }
871 
872 /**
873  * The nonstatic implementation of release_all_textures().
874  */
875 void TexturePool::
876 ns_release_all_textures() {
877  MutexHolder holder(_lock);
878 
879  Textures::iterator ti;
880  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
881  Texture *tex = (*ti).second;
882  tex->_texture_pool_key = string();
883  }
884 
885  _textures.clear();
886  _normalization_cube_map = nullptr;
887 
888  // Blow away the cache of resolved relative filenames.
889  _relpath_lookup.clear();
890 }
891 
892 /**
893  * The nonstatic implementation of garbage_collect().
894  */
895 int TexturePool::
896 ns_garbage_collect() {
897  MutexHolder holder(_lock);
898 
899  int num_released = 0;
900  Textures new_set;
901 
902  Textures::iterator ti;
903  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
904  Texture *tex = (*ti).second;
905  if (tex->get_ref_count() == 1) {
906  if (gobj_cat.is_debug()) {
907  gobj_cat.debug()
908  << "Releasing " << (*ti).first._fullpath << "\n";
909  }
910  ++num_released;
911  tex->_texture_pool_key = string();
912  } else {
913  new_set.insert(new_set.end(), *ti);
914  }
915  }
916 
917  _textures.swap(new_set);
918 
919  if (_normalization_cube_map != nullptr &&
920  _normalization_cube_map->get_ref_count() == 1) {
921  if (gobj_cat.is_debug()) {
922  gobj_cat.debug()
923  << "Releasing normalization cube map\n";
924  }
925  ++num_released;
926  _normalization_cube_map = nullptr;
927  }
928 
929  return num_released;
930 }
931 
932 /**
933  * The nonstatic implementation of list_contents().
934  */
935 void TexturePool::
936 ns_list_contents(ostream &out) const {
937  MutexHolder holder(_lock);
938 
939  int total_size;
940  int total_ram_size;
941  Textures::const_iterator ti;
942 
943  out << "texture pool contents:\n";
944 
945  total_size = 0;
946  total_ram_size = 0;
947  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
948  Texture *tex = (*ti).second;
949  out << (*ti).first._fullpath << "\n";
950  out << " (count = " << tex->get_ref_count()
951  << ", ram = " << tex->get_ram_image_size()
952  << ", size = " << tex->get_ram_page_size()
953  << ", w = " << tex->get_x_size()
954  << ", h = " << tex->get_y_size()
955  << ")\n";
956  nassertv(tex->_texture_pool_key == (*ti).first._fullpath);
957  total_ram_size += tex->get_ram_image_size();
958  total_size += tex->get_ram_page_size();
959  }
960 
961  out << "total number of textures: " << _textures.size() << "\n";
962  out << "texture pool ram : " << total_ram_size << "\n";
963  out << "texture pool size: " << total_size << "\n";
964  out << "texture pool size - texture pool ram: " << total_size - total_ram_size << "\n";
965 }
966 
967 /**
968  * The nonstatic implementation of find_texture().
969  */
970 Texture *TexturePool::
971 ns_find_texture(const string &name) const {
972  MutexHolder holder(_lock);
973  GlobPattern glob(name);
974 
975  Textures::const_iterator ti;
976  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
977  Texture *tex = (*ti).second;
978  if (glob.matches(tex->get_name())) {
979  return tex;
980  }
981  }
982 
983  return nullptr;
984 }
985 
986 /**
987  * The nonstatic implementation of find_all_textures().
988  */
989 TextureCollection TexturePool::
990 ns_find_all_textures(const string &name) const {
991  MutexHolder holder(_lock);
992  TextureCollection result;
993  GlobPattern glob(name);
994 
995  Textures::const_iterator ti;
996  for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
997  Texture *tex = (*ti).second;
998  if (glob.matches(tex->get_name())) {
999  result.add_texture(tex);
1000  }
1001  }
1002 
1003  return result;
1004 }
1005 
1006 /**
1007  * Creates a new Texture object of the appropriate type for the indicated
1008  * filename extension, according to the types that have been registered via
1009  * register_texture_type().
1010  */
1011 PT(Texture) TexturePool::
1012 ns_make_texture(const string &extension) const {
1013  MakeTextureFunc *func = get_texture_type(extension);
1014  if (func != nullptr) {
1015  return func();
1016  }
1017 
1018  // We don't know what kind of file type this is; return an ordinary Texture
1019  // in case it's an image file with no extension.
1020  return new Texture;
1021 }
1022 
1023 /**
1024  * Searches for the indicated filename along the model path. If the filename
1025  * was previously searched for, doesn't search again, as an optimization.
1026  * Assumes _lock is held.
1027  */
1028 void TexturePool::
1029 resolve_filename(Filename &new_filename, const Filename &orig_filename,
1030  bool read_mipmaps, const LoaderOptions &options) {
1031  if (!_fake_texture_image.empty()) {
1032  new_filename = _fake_texture_image;
1033  return;
1034  }
1035 
1036  RelpathLookup::iterator rpi = _relpath_lookup.find(orig_filename);
1037  if (rpi != _relpath_lookup.end()) {
1038  new_filename = (*rpi).second;
1039  return;
1040  }
1041 
1042  new_filename = orig_filename;
1043  if (read_mipmaps || (options.get_texture_flags() & LoaderOptions::TF_multiview)) {
1044  new_filename.set_pattern(true);
1045  }
1046 
1048  vfs->resolve_filename(new_filename, get_model_path());
1049 
1050  _relpath_lookup[orig_filename] = new_filename;
1051 }
1052 
1053 /**
1054  * Attempts to load the texture from the cache record.
1055  */
1056 void TexturePool::
1057 try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename,
1058  PT(BamCacheRecord) &record, bool &compressed_cache_record,
1059  const LoaderOptions &options) {
1060  if (tex == nullptr) {
1061  // The texture was not supplied by a texture filter. See if it can be
1062  // found in the on-disk cache, if it is active.
1063  if ((cache->get_cache_textures() || cache->get_cache_compressed_textures()) && !textures_header_only) {
1064 
1065  // Call ns_make_texture() on the file extension and create a dummy
1066  // texture object we can call ensure_loaded_type() on. We don't need to
1067  // keep this object around after this call, since we'll be creating a
1068  // new one below. I know this is a bit hacky.
1069  string ext = downcase(filename.get_extension());
1070  PT(Texture) dummy = ns_make_texture(ext);
1071  dummy->ensure_loader_type(filename);
1072  dummy.clear();
1073 
1074  record = cache->lookup(filename, "txo");
1075  if (record != nullptr) {
1076  if (record->has_data()) {
1077  tex = DCAST(Texture, record->get_data());
1078  compressed_cache_record = (tex->get_ram_image_compression() != Texture::CM_off);
1079  int x_size = tex->get_orig_file_x_size();
1080  int y_size = tex->get_orig_file_y_size();
1081  tex->adjust_this_size(x_size, y_size, filename.get_basename(), true);
1082 
1083  if (!cache->get_cache_textures() && !compressed_cache_record) {
1084  // We're not supposed to be caching uncompressed textures.
1085  if (gobj_cat.is_debug()) {
1086  gobj_cat.debug()
1087  << "Not caching uncompressed texture " << *tex << "\n";
1088  }
1089  tex = nullptr;
1090  record = nullptr;
1091 
1092  } else if (x_size != tex->get_x_size() ||
1093  y_size != tex->get_y_size()) {
1094  // The cached texture no longer matches our expected size (the
1095  // resizing config variables must have changed). We'll have to
1096  // reload the texture from its original file so we can rebuild the
1097  // cache.
1098  if (gobj_cat.is_debug()) {
1099  gobj_cat.debug()
1100  << "Cached texture " << *tex << " has size "
1101  << tex->get_x_size() << " x " << tex->get_y_size()
1102  << " instead of " << x_size << " x " << y_size
1103  << "; dropping cache.\n";
1104  }
1105  tex = nullptr;
1106 
1107  } else if (!tex->has_compression() && tex->get_ram_image_compression() != Texture::CM_off) {
1108  // This texture shouldn't be compressed, but it is. Go reload it.
1109  if (gobj_cat.is_debug()) {
1110  gobj_cat.debug()
1111  << "Cached texture " << *tex
1112  << " is compressed in cache; dropping cache.\n";
1113  }
1114  tex = nullptr;
1115 
1116  } else {
1117  gobj_cat.info()
1118  << "Texture " << filename << " found in disk cache.\n";
1119  if ((options.get_texture_flags() & LoaderOptions::TF_preload_simple) &&
1120  !tex->has_simple_ram_image()) {
1122  }
1123  if (!(options.get_texture_flags() & LoaderOptions::TF_preload)) {
1124  // But drop the RAM until we need it.
1125  tex->clear_ram_image();
1126 
1127  } else {
1128  bool was_compressed = (tex->get_ram_image_compression() != Texture::CM_off);
1129  if (tex->consider_auto_process_ram_image(tex->uses_mipmaps(), true)) {
1130  bool is_compressed = (tex->get_ram_image_compression() != Texture::CM_off);
1131  if (!was_compressed && is_compressed &&
1132  cache->get_cache_compressed_textures()) {
1133  // We've re-compressed the image after loading it from the
1134  // cache. To keep the cache current, rewrite it to the
1135  // cache now, in its newly compressed form.
1136  record->set_data(tex);
1137  cache->store(record);
1138  compressed_cache_record = true;
1139  }
1140  }
1141  }
1142  tex->set_keep_ram_image(false);
1143  }
1144  } else {
1145  if (!cache->get_cache_textures()) {
1146  // This texture has no actual record, and therefore no compressed
1147  // record (yet). And we're not supposed to be caching
1148  // uncompressed textures.
1149  if (gobj_cat.is_debug()) {
1150  gobj_cat.debug()
1151  << "Not caching uncompressed texture\n";
1152  }
1153  record = nullptr;
1154  }
1155  }
1156  }
1157  }
1158  }
1159 }
1160 
1161 /**
1162  * Prints a suitable error message when a texture could not be loaded.
1163  */
1164 void TexturePool::
1165 report_texture_unreadable(const Filename &filename) const {
1167  bool has_hash = (filename.get_fullpath().find('#') != string::npos);
1168  if (!has_hash && !vfs->exists(filename)) {
1169  if (filename.is_local()) {
1170  // The file doesn't exist, and it wasn't fully-qualified--therefore, it
1171  // wasn't found along either search path.
1172  gobj_cat.error()
1173  << "Unable to find texture \"" << filename << "\""
1174  << " on model-path " << get_model_path() <<"\n";
1175  } else {
1176  // A fully-specified filename is not searched along the path, so don't
1177  // mislead the user with the error message.
1178  gobj_cat.error()
1179  << "Texture \"" << filename << "\" does not exist.\n";
1180  }
1181 
1182  } else {
1183  // The file exists, but it couldn't be read for some reason.
1184  if (!has_hash) {
1185  gobj_cat.error()
1186  << "Texture \"" << filename << "\" exists but cannot be read.\n";
1187  } else {
1188  // If the filename contains a hash, we'll be noncommittal about whether
1189  // it exists or not.
1190  gobj_cat.error()
1191  << "Texture \"" << filename << "\" cannot be read.\n";
1192  }
1193 
1194  // Maybe the filename extension is unknown.
1195  MakeTextureFunc *func = get_texture_type(filename.get_extension());
1196  if (func == nullptr) {
1197  gobj_cat.error()
1198  << "Texture extension \"" << filename.get_extension()
1199  << "\" is unknown. Supported texture types:\n";
1200  write_texture_types(gobj_cat.error(false), 2);
1201  }
1202  }
1203 }
1204 
1205 /**
1206  * Invokes pre_load() on all registered filters until one returns non-NULL;
1207  * returns NULL if there are no registered filters or if all registered
1208  * filters returned NULL.
1209  */
1210 PT(Texture) TexturePool::
1211 pre_load(const Filename &orig_filename, const Filename &orig_alpha_filename,
1212  int primary_file_num_channels, int alpha_file_channel,
1213  bool read_mipmaps, const LoaderOptions &options) {
1214  PT(Texture) tex;
1215 
1216  MutexHolder holder(_lock);
1217 
1218  FilterRegistry::iterator fi;
1219  for (fi = _filter_registry.begin();
1220  fi != _filter_registry.end();
1221  ++fi) {
1222  tex = (*fi)->pre_load(orig_filename, orig_alpha_filename,
1223  primary_file_num_channels, alpha_file_channel,
1224  read_mipmaps, options);
1225  if (tex != nullptr) {
1226  return tex;
1227  }
1228  }
1229 
1230  return tex;
1231 }
1232 
1233 /**
1234  * Invokes post_load() on all registered filters.
1235  */
1236 PT(Texture) TexturePool::
1237 post_load(Texture *tex) {
1238  PT(Texture) result = tex;
1239 
1240  MutexHolder holder(_lock);
1241 
1242  FilterRegistry::iterator fi;
1243  for (fi = _filter_registry.begin();
1244  fi != _filter_registry.end();
1245  ++fi) {
1246  result = (*fi)->post_load(result);
1247  }
1248 
1249  return result;
1250 }
1251 
1252 
1253 /**
1254  * Loads up all of the dll's named by the texture-filter Config.prc variable.
1255  */
1256 void TexturePool::
1257 load_filters() {
1258  ConfigVariableList texture_filter
1259  ("texture-filter",
1260  PRC_DESC("Names one or more external libraries that should be loaded for the "
1261  "purposes of performing texture filtering. This variable may be repeated several "
1262  "times. As in load-display, the actual library filename is derived by "
1263  "prefixing 'lib' to the specified name."));
1264 
1265  int num_aux = texture_filter.get_num_unique_values();
1266  for (int i = 0; i < num_aux; i++) {
1267  string name = texture_filter.get_unique_value(i);
1268 
1269  Filename dlname = Filename::dso_filename("lib" + name + ".so");
1270  gobj_cat->info()
1271  << "loading texture filter: " << dlname.to_os_specific() << std::endl;
1272  void *tmp = load_dso(get_plugin_path().get_value(), dlname);
1273  if (tmp == nullptr) {
1274  gobj_cat.info()
1275  << "Unable to load: " << load_dso_error() << std::endl;
1276  }
1277  }
1278 }
Texture(const std::string &name=std::string())
Constructs an empty texture.
Definition: texture.cxx:374
get_orig_file_y_size
Returns the Y size of the original disk image that this Texture was loaded from (if it came from a di...
Definition: texture.h:565
get_y_size
Returns the height of the texture image in texels.
Definition: texture.h:338
set_fullpath
Sets the full pathname to the file that contains the image's contents, as found along the search path...
Definition: texture.h:324
get_ref_count
Returns the current reference count.
set_data
Stores a new data object on the record.
set_filename
Sets the name of the file that contains the image's contents.
Definition: texture.h:312
void register_texture_type(MakeTextureFunc *func, const std::string &extensions)
Records a factory function that makes a Texture object of the appropriate type for one or more partic...
Definition: texturePool.cxx:53
get_fullpath
Returns the fullpath that has been set.
Definition: texture.h:324
void write_texture_types(std::ostream &out, int indent_level) const
Outputs a list of the available texture types to the indicated output stream.
MakeTextureFunc * get_texture_type(const std::string &extension) const
Returns the factory function to construct a new texture of the type appropriate for the indicated fil...
Definition: texturePool.cxx:84
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_cache_textures
Returns whether texture files (e.g.
Definition: bamCache.h:90
Specifies parameters that may be passed to the loader.
Definition: loaderOptions.h:23
This class maintains a cache of Bam and/or Txo objects generated from model files and texture images ...
Definition: bamCache.h:42
This is a convenience class to specialize ConfigVariable as a Filename type.
void write(std::ostream &out, int indent_level=0) const
Writes a list of supported image file types to the indicated output stream, one per line.
A hierarchy of directories and files that appears to be one continuous file system,...
Represents a texture object, which is typically a single 2-d image but may also represent a 1-d or 3-...
Definition: texture.h:71
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_texture_type
Returns the overall interpretation of the texture.
Definition: texture.h:357
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
string downcase(const string &s)
Returns the input string with all uppercase letters converted to lowercase.
bool resolve_filename(Filename &filename, const DSearchPath &searchpath, const std::string &default_extension=std::string()) const
Searches the given search path for the filename.
A lightweight C++ object whose constructor calls acquire() and whose destructor calls release() on a ...
Definition: mutexHolder.h:25
set_alpha_fullpath
Sets the full pathname to the file that contains the image's alpha channel contents,...
Definition: texture.h:330
void add_texture(Texture *texture)
Adds a new Texture to the collection.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
The abstract base class for a file or directory within the VirtualFileSystem.
Definition: virtualFile.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class is similar to ConfigVariable, but it reports its value as a list of strings.
void register_filter(TexturePoolFilter *filter)
Records a TexturePoolFilter object that may operate on texture images as they are loaded from disk.
Definition: texturePool.cxx:70
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
void setup_cube_map()
Sets the texture as an empty cube map texture with no dimensions.
Definition: texture.I:155
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_ram_page_size
Returns the number of bytes used by the in-memory image per page, or 0 if there is no in-memory image...
Definition: texture.h:439
This template class calls PipelineCycler::read_unlocked(), and then provides a transparent read-only ...
bool adjust_this_size(int &x_size, int &y_size, const std::string &name, bool for_padding) const
Works like adjust_size, but also considers the texture class.
Definition: texture.I:2154
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
bool store(BamCacheRecord *record)
Flushes a cache entry to disk.
Definition: bamCache.cxx:188
PT(Texture) TexturePool
Creates a new Texture object of the appropriate type for the indicated filename extension,...
bool uses_mipmaps() const
Returns true if the minfilter settings on this texture indicate the use of mipmapping,...
Definition: texture.I:1127
bool exists(const Filename &filename) const
Convenience function; returns true if the named file exists.
int extract_words(const string &str, vector_string &words)
Divides the string into a number of words according to whitespace.
void setup_3d_texture(int z_size=1)
Sets the texture as an empty 3-d texture with no dimensions (though if you know the depth ahead of ti...
Definition: texture.I:110
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
set_keep_ram_image
Sets the flag that indicates whether this Texture is eligible to have its main RAM copy of the textur...
Definition: texture.h:464
An instance of this class is written to the front of a Bam or Txo file to make the file a cached inst...
void setup_2d_texture_array(int z_size=1)
Sets the texture as an empty 2-d texture array with no dimensions (though if you know the depth ahead...
Definition: texture.I:133
std::ostream & indent(std::ostream &out, int indent_level)
A handy function for doing text formatting.
Definition: indent.cxx:20
std::string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:338
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
get_data
Returns a pointer to the data stored in the record, or NULL if there is no data.
get_orig_file_x_size
Returns the X size of the original disk image that this Texture was loaded from (if it came from a di...
Definition: texture.h:564
Manages a list of Texture objects, as returned by TexturePool::find_all_textures().
PointerTo< VirtualFile > get_file(const Filename &filename, bool status_only=false) const
Looks up the file by the indicated name in the file system.
set_post_load_store_cache
Sets the post_load_store_cache flag.
Definition: texture.h:586
bool is_local() const
Returns true if the filename is local, e.g.
Definition: filename.I:549
static TexturePool * get_global_ptr()
Initializes and/or returns the global pointer to the one TexturePool object in the system.
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
void clear_ram_image()
Discards the current system-RAM image.
Definition: texture.I:1439
std::string get_basename() const
Returns the basename part of the filename.
Definition: filename.I:367
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This class maintains the set of all known PNMFileTypes in the universe.
void generate_simple_ram_image()
Computes the "simple" ram image by loading the main RAM image, if it is not already available,...
Definition: texture.cxx:1318
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
set_alpha_filename
Sets the name of the file that contains the image's alpha channel contents.
Definition: texture.h:318
bool read(const Filename &fullpath, const LoaderOptions &options=LoaderOptions())
Reads the named filename into the texture.
Definition: texture.cxx:551
This is an abstract base class, a placeholder for any number of different classes that may wish to im...
get_cache_compressed_textures
Returns whether compressed texture files will be stored in the cache, as compressed txo files.
Definition: bamCache.h:92
has_simple_ram_image
Returns true if the Texture has a "simple" image available in main RAM.
Definition: texture.h:509
This is the preferred interface for loading textures from image files.
Definition: texturePool.h:37
is_cacheable
Returns true if there is enough information in this Texture object to write it to the bam cache succe...
Definition: texture.h:465
clear_alpha_fullpath
Removes the alpha fullpath, if it was previously set.
Definition: texture.h:330
bool has_compression() const
Returns true if the texture indicates it wants to be compressed, either with CM_on or higher,...
Definition: texture.I:1102
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
get_x_size
Returns the width of the texture image in texels.
Definition: texture.h:334
static BamCache * get_global_ptr()
Returns a pointer to the global BamCache object, which is used automatically by the ModelPool and Tex...
Definition: bamCache.I:223
void set_pattern(bool pattern)
Sets the flag indicating whether this is a filename pattern.
Definition: filename.I:503
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
get_ram_image_compression
Returns the compression mode in which the ram image is already stored pre- compressed.
Definition: texture.h:463
get_ram_image_size
Returns the total number of bytes used by the in-memory image, across all pages and views,...
Definition: texture.h:437
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.