Main Page   Class Hierarchy   Alphabetical List   Compound List   File List   Compound Members   File Members  

panda/src/gobj/texture.cxx

Go to the documentation of this file.
00001 // Filename: texture.cxx
00002 // Created by:  mike (09Jan97)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) 2001, Disney Enterprises, Inc.  All rights reserved
00008 //
00009 // All use of this software is subject to the terms of the Panda 3d
00010 // Software license.  You should have received a copy of this license
00011 // along with this source code; you will also find a current copy of
00012 // the license at http://www.panda3d.org/license.txt .
00013 //
00014 // To contact the maintainers of this program write to
00015 // panda3d@yahoogroups.com .
00016 //
00017 ////////////////////////////////////////////////////////////////////
00018 
00019 #include "pandabase.h"
00020 #include "texture.h"
00021 #include "config_gobj.h"
00022 #include "texturePool.h"
00023 #include "textureContext.h"
00024 #include "datagram.h"
00025 #include "datagramIterator.h"
00026 #include "bamReader.h"
00027 #include "bamWriter.h"
00028 #include "string_utils.h"
00029 
00030 #include <stddef.h>
00031 
00032 
00033 TypeHandle Texture::_type_handle;
00034 
00035 ////////////////////////////////////////////////////////////////////
00036 //     Function: up_to_power_2
00037 //  Description: Returns the smallest power of 2 greater than or equal
00038 //               to value.
00039 ////////////////////////////////////////////////////////////////////
00040 static int
00041 up_to_power_2(int value) {
00042   int x = 1;
00043   while (x < value) {
00044     x = (x << 1);
00045   }
00046   return x;
00047 }
00048 
00049 ////////////////////////////////////////////////////////////////////
00050 //     Function: down_to_power_2
00051 //  Description: Returns the largest power of 2 less than or equal
00052 //               to value.
00053 ////////////////////////////////////////////////////////////////////
00054 static int
00055 down_to_power_2(int value) {
00056   int x = 1;
00057   while ((x << 1) <= value) {
00058     x = (x << 1);
00059   }
00060   return x;
00061 }
00062 
00063 ////////////////////////////////////////////////////////////////////
00064 //     Function: consider_rescale
00065 //  Description: Scales the PNMImage according to the whims of the
00066 //               Configrc file.
00067 ////////////////////////////////////////////////////////////////////
00068 static void
00069 consider_rescale(PNMImage &pnmimage, const string &name) {
00070   int new_x_size = pnmimage.get_x_size();
00071   int new_y_size = pnmimage.get_y_size();
00072 
00073   if (textures_down_power_2) {
00074     new_x_size = down_to_power_2(new_x_size);
00075     new_y_size = down_to_power_2(new_y_size);
00076   } else if (textures_up_power_2) {
00077     new_x_size = up_to_power_2(new_x_size);
00078     new_y_size = up_to_power_2(new_y_size);
00079   }
00080 
00081   if (textures_down_square) {
00082     new_x_size = new_y_size = min(new_x_size, new_y_size);
00083   } else if (textures_up_square) {
00084     new_x_size = new_y_size = max(new_x_size, new_y_size);
00085   }
00086 
00087   if (max_texture_dimension > 0) {
00088     new_x_size = min(new_x_size, max_texture_dimension);
00089     new_y_size = min(new_y_size, max_texture_dimension);
00090   }
00091 
00092   if (pnmimage.get_x_size() != new_x_size ||
00093       pnmimage.get_y_size() != new_y_size) {
00094     gobj_cat.info()
00095       << "Automatically rescaling " << name << " from "
00096       << pnmimage.get_x_size() << " by " << pnmimage.get_y_size() << " to "
00097       << new_x_size << " by " << new_y_size << "\n";
00098 
00099     PNMImage scaled(new_x_size, new_y_size, pnmimage.get_num_channels(),
00100                     pnmimage.get_maxval(), pnmimage.get_type());
00101     scaled.quick_filter_from(pnmimage);
00102     pnmimage = scaled;
00103   }
00104 }
00105 
00106 ////////////////////////////////////////////////////////////////////
00107 //     Function: consider_downgrade
00108 //  Description: Reduces the number of channels in the texture, if
00109 //               necessary, according to num_channels.
00110 ////////////////////////////////////////////////////////////////////
00111 static void
00112 consider_downgrade(PNMImage &pnmimage, int num_channels, 
00113                    const string &name) {
00114   if (num_channels != 0 && num_channels < pnmimage.get_num_channels()) {
00115     // One special case: we can't reduce from 3 to 2 components, since
00116     // that would require adding an alpha channel.
00117     if (pnmimage.get_num_channels() == 3 && num_channels == 2) {
00118       return;
00119     }
00120 
00121     gobj_cat.info()
00122       << "Downgrading " << name << " from " << pnmimage.get_num_channels()
00123       << " components to " << num_channels << ".\n";
00124     pnmimage.set_num_channels(num_channels);
00125   }
00126 }
00127 
00128 ////////////////////////////////////////////////////////////////////
00129 //     Function: Constructor
00130 //       Access: Published
00131 //  Description:
00132 ////////////////////////////////////////////////////////////////////
00133 Texture::
00134 Texture() : ImageBuffer() {
00135   _magfilter = FT_nearest;
00136   _minfilter = FT_nearest;
00137   _wrapu = WM_repeat;
00138   _wrapv = WM_repeat;
00139   _anisotropic_degree = 1;
00140   _keep_ram_image = false;
00141   _pbuffer = new PixelBuffer;
00142   // _has_requested_size = false;
00143   _all_dirty_flags = 0;
00144   memset(&_border_color,0,sizeof(Colorf));
00145 }
00146 
00147 
00148 ////////////////////////////////////////////////////////////////////
00149 //     Function: Constructor
00150 //       Access: Published
00151 //  Description:
00152 ////////////////////////////////////////////////////////////////////
00153 Texture::
00154 Texture(int xsize, int ysize, int components, int component_width, PixelBuffer::Type type, 
00155         PixelBuffer::Format format, bool bAllocateRAM) : ImageBuffer() {
00156   _magfilter = FT_nearest;
00157   _minfilter = FT_nearest;
00158   _wrapu = WM_repeat;
00159   _wrapv = WM_repeat;
00160   _anisotropic_degree = 1;
00161   _keep_ram_image = bAllocateRAM;
00162   _pbuffer = new PixelBuffer(xsize,ysize,components,component_width,type,format,bAllocateRAM);
00163   // _has_requested_size = false;
00164   _all_dirty_flags = 0;
00165   memset(&_border_color,0,sizeof(Colorf));
00166 }
00167 
00168 ////////////////////////////////////////////////////////////////////
00169 //     Function: Destructor
00170 //       Access: Published
00171 //  Description:
00172 ////////////////////////////////////////////////////////////////////
00173 Texture::
00174 ~Texture() {
00175   unprepare();
00176 }
00177 
00178 ////////////////////////////////////////////////////////////////////
00179 //     Function: read
00180 //       Access: Published
00181 //  Description: Reads the texture from the indicated filename.  If
00182 //               num_channels is not 0, it specifies the number of
00183 //               components to downgrade the image to if it is greater
00184 //               than this number.
00185 ////////////////////////////////////////////////////////////////////
00186 bool Texture::
00187 read(const Filename &fullpath, int primary_file_num_channels) {
00188   PNMImage image;
00189 
00190   if (!image.read(fullpath)) {
00191     gobj_cat.error()
00192       << "Texture::read() - couldn't read: " << fullpath << endl;
00193     return false;
00194   }
00195 
00196   if (!has_name()) {
00197     set_name(fullpath.get_basename_wo_extension());
00198   }
00199   if (!has_filename()) {
00200     set_filename(fullpath);
00201     clear_alpha_filename();
00202   }
00203 
00204   set_fullpath(fullpath);
00205   clear_alpha_fullpath();
00206 
00207   // Check to see if we need to scale it.
00208   consider_rescale(image, get_name());
00209   consider_downgrade(image, primary_file_num_channels, get_name());
00210 
00211   _primary_file_num_channels = image.get_num_channels();
00212   _alpha_file_channel = 0;
00213 
00214   return load(image);
00215 }
00216 
00217 ////////////////////////////////////////////////////////////////////
00218 //     Function: read
00219 //       Access: Published
00220 //  Description: Combine a 3-component image with a grayscale image
00221 //               to get a 4-component image
00222 ////////////////////////////////////////////////////////////////////
00223 bool Texture::
00224 read(const Filename &fullpath, const Filename &alpha_fullpath,
00225      int primary_file_num_channels, int alpha_file_channel) {
00226   PNMImage image;
00227   if (!image.read(fullpath)) {
00228     gobj_cat.error()
00229       << "Texture::read() - couldn't read: " << fullpath << endl;
00230     return false;
00231   }
00232 
00233   PNMImage alpha_image;
00234   if (!alpha_image.read(alpha_fullpath)) {
00235     gobj_cat.error()
00236       << "Texture::read() - couldn't read: " << alpha_fullpath << endl;
00237     return false;
00238   }
00239 
00240   if (!has_name()) {
00241     set_name(fullpath.get_basename_wo_extension());
00242   }
00243   if (!has_filename()) {
00244     set_filename(fullpath);
00245     set_alpha_filename(alpha_fullpath);
00246   }
00247 
00248   set_fullpath(fullpath);
00249   set_alpha_fullpath(alpha_fullpath);
00250 
00251   consider_rescale(image, get_name());
00252 
00253   // The grayscale (alpha channel) image must be the same size as the
00254   // main image.
00255   if (image.get_x_size() != alpha_image.get_x_size() ||
00256       image.get_y_size() != alpha_image.get_y_size()) {
00257     gobj_cat.info()
00258       << "Automatically rescaling " << alpha_fullpath.get_basename()
00259       << " from " << alpha_image.get_x_size() << " by " 
00260       << alpha_image.get_y_size() << " to " << image.get_x_size()
00261       << " by " << image.get_y_size() << "\n";
00262 
00263     PNMImage scaled(image.get_x_size(), image.get_y_size(),
00264                     alpha_image.get_num_channels(),
00265                     alpha_image.get_maxval(), alpha_image.get_type());
00266     scaled.quick_filter_from(alpha_image);
00267     alpha_image = scaled;
00268   }
00269 
00270   consider_downgrade(image, primary_file_num_channels, get_name());
00271 
00272   _primary_file_num_channels = image.get_num_channels();
00273 
00274   // Make the original image a 4-component image by taking the
00275   // grayscale value from the second image.
00276   image.add_alpha();
00277 
00278   if (alpha_file_channel == 4 || 
00279       (alpha_file_channel == 2 && alpha_image.get_num_channels() == 2)) {
00280     // Use the alpha channel.
00281     for (int x = 0; x < image.get_x_size(); x++) {
00282       for (int y = 0; y < image.get_y_size(); y++) {
00283         image.set_alpha(x, y, alpha_image.get_alpha(x, y));
00284       }
00285     }
00286     _alpha_file_channel = alpha_image.get_num_channels();
00287 
00288   } else if (alpha_file_channel >= 1 && alpha_file_channel <= 3 &&
00289              alpha_image.get_num_channels() >= 3) {
00290     // Use the appropriate red, green, or blue channel.
00291     for (int x = 0; x < image.get_x_size(); x++) {
00292       for (int y = 0; y < image.get_y_size(); y++) {
00293         image.set_alpha(x, y, alpha_image.get_channel_val(x, y, alpha_file_channel - 1));
00294       }
00295     }
00296     _alpha_file_channel = alpha_file_channel;
00297 
00298   } else {
00299     // Use the grayscale channel.
00300     for (int x = 0; x < image.get_x_size(); x++) {
00301       for (int y = 0; y < image.get_y_size(); y++) {
00302         image.set_alpha(x, y, alpha_image.get_gray(x, y));
00303       }
00304     }
00305     _alpha_file_channel = 0;
00306   }
00307 
00308   return load(image);
00309 }
00310 
00311 ////////////////////////////////////////////////////////////////////
00312 //     Function: write
00313 //       Access: Published
00314 //  Description: Writes the texture to the indicated filename.
00315 ////////////////////////////////////////////////////////////////////
00316 bool Texture::
00317 write(const Filename &name) const {
00318   nassertr(has_ram_image(), false);
00319   PNMImage pnmimage;
00320   if (!_pbuffer->store(pnmimage)) {
00321     return false;
00322   }
00323 
00324   if (!pnmimage.write(name)) {
00325     gobj_cat.error()
00326       << "Texture::write() - couldn't write: " << name << endl;
00327     return false;
00328   }
00329   return true;
00330 }
00331 
00332 ////////////////////////////////////////////////////////////////////
00333 //     Function: set_wrapu
00334 //       Access: Published
00335 //  Description:
00336 ////////////////////////////////////////////////////////////////////
00337 void Texture::
00338 set_wrapu(Texture::WrapMode wrap) {
00339   if (_wrapu != wrap) {
00340     mark_dirty(DF_wrap);
00341     _wrapu = wrap;
00342   }
00343 }
00344 
00345 ////////////////////////////////////////////////////////////////////
00346 //     Function: set_wrapv
00347 //       Access: Published
00348 //  Description:
00349 ////////////////////////////////////////////////////////////////////
00350 void Texture::
00351 set_wrapv(Texture::WrapMode wrap) {
00352   if (_wrapv != wrap) {
00353     mark_dirty(DF_wrap); 
00354     _wrapv = wrap;
00355   }
00356 }
00357 
00358 ////////////////////////////////////////////////////////////////////
00359 //     Function: set_minfilter
00360 //       Access: Published
00361 //  Description:
00362 ////////////////////////////////////////////////////////////////////
00363 void Texture::
00364 set_minfilter(Texture::FilterType filter) {
00365   if (_minfilter != filter) {
00366     if (is_mipmap(_minfilter) != is_mipmap(filter)) {
00367       mark_dirty(DF_filter | DF_mipmap);
00368     } else {
00369       mark_dirty(DF_filter);
00370     }
00371     _minfilter = filter;
00372   }
00373 }
00374 
00375 ////////////////////////////////////////////////////////////////////
00376 //     Function: set_magfilter
00377 //       Access: Published
00378 //  Description:
00379 ////////////////////////////////////////////////////////////////////
00380 void Texture::
00381 set_magfilter(Texture::FilterType filter) {
00382   if (_magfilter != filter) {
00383     mark_dirty(DF_filter);
00384     _magfilter = filter;
00385   }
00386 }
00387 
00388 ////////////////////////////////////////////////////////////////////
00389 //     Function: set_anisotropic_degree
00390 //       Access: Published
00391 //  Description: Specifies the level of anisotropic filtering to apply
00392 //               to the texture.  Normally, this is 1, to indicate
00393 //               anisotropic filtering is disabled.  This may be set
00394 //               to a number higher than one to enable anisotropic
00395 //               filtering, if the rendering backend supports this.
00396 ////////////////////////////////////////////////////////////////////
00397 void Texture::
00398 set_anisotropic_degree(int anisotropic_degree) {
00399   if (_anisotropic_degree != anisotropic_degree) {
00400     mark_dirty(DF_filter);
00401     _anisotropic_degree = anisotropic_degree;
00402   }
00403 }
00404 
00405 ////////////////////////////////////////////////////////////////////
00406 //     Function: set_border_color
00407 //       Access: Published
00408 //  Description:
00409 ////////////////////////////////////////////////////////////////////
00410 void Texture::
00411 set_border_color(const Colorf &color) {
00412    memcpy(&_border_color,&color,sizeof(Colorf));
00413 }
00414 
00415 ////////////////////////////////////////////////////////////////////
00416 //     Function: load
00417 //       Access: Public
00418 //  Description: Creates the texture from the already-read PNMImage.
00419 ////////////////////////////////////////////////////////////////////
00420 bool Texture::
00421 load(const PNMImage &pnmimage) {
00422   if (!_pbuffer->load(pnmimage))
00423     return false;
00424 
00425   mark_dirty(DF_image);
00426 
00427   return true;
00428 }
00429 
00430 ////////////////////////////////////////////////////////////////////
00431 //     Function: store
00432 //       Access: Public
00433 //  Description: Saves the texture to the indicated PNMImage, but does
00434 //               not write it to disk.
00435 ////////////////////////////////////////////////////////////////////
00436 bool Texture::
00437 store(PNMImage &pnmimage) const {
00438   return _pbuffer->store( pnmimage );
00439 }
00440 
00441 ////////////////////////////////////////////////////////////////////
00442 //     Function: Texture::is_mipmap
00443 //       Access: Public, Static
00444 //  Description: Returns true if the indicated filter type requires
00445 //               the use of mipmaps, or false if it does not.
00446 ////////////////////////////////////////////////////////////////////
00447 bool Texture::
00448 is_mipmap(FilterType type) {
00449   switch (type) {
00450   case FT_nearest_mipmap_nearest:
00451   case FT_linear_mipmap_nearest:
00452   case FT_nearest_mipmap_linear:
00453   case FT_linear_mipmap_linear:
00454     return true;
00455     
00456   default:
00457     return false;
00458   }
00459 }
00460 
00461 ////////////////////////////////////////////////////////////////////
00462 //     Function: prepare
00463 //       Access: Public
00464 //  Description: Creates a context for the texture on the particular
00465 //               GSG, if it does not already exist.  Returns the new
00466 //               (or old) TextureContext.
00467 ////////////////////////////////////////////////////////////////////
00468 TextureContext *Texture::
00469 prepare(GraphicsStateGuardianBase *gsg) {
00470   Contexts::const_iterator ci;
00471   ci = _contexts.find(gsg);
00472   if (ci != _contexts.end()) {
00473     return (*ci).second;
00474   }
00475 
00476   TextureContext *tc = gsg->prepare_texture(this);
00477   _contexts[gsg] = tc;
00478 
00479   // Now that we have a new TextureContext with zero dirty flags, our
00480   // intersection of all dirty flags must be zero.  This doesn't mean
00481   // that some other contexts aren't still dirty, but at least one
00482   // context isn't.
00483   _all_dirty_flags = 0;
00484 
00485   if (!keep_texture_ram && !_keep_ram_image) {
00486     // Once we have prepared the texture, we can generally safely
00487     // remove the pixels from main RAM.  The GSG is now responsible
00488     // for remembering what it looks like.
00489 
00490     if (gobj_cat.is_debug()) {
00491       gobj_cat.debug()
00492         << "Dumping RAM for texture " << get_name() << "\n";
00493     }
00494     _pbuffer->_image.clear();
00495   }
00496 
00497   return tc;
00498 }
00499 
00500 ////////////////////////////////////////////////////////////////////
00501 //     Function: Texture::unprepare
00502 //       Access: Public
00503 //  Description: Frees the context allocated on all GSG's for which
00504 //               the texture has been declared.
00505 ////////////////////////////////////////////////////////////////////
00506 void Texture::
00507 unprepare() {
00508   // We have to traverse a copy of the _contexts list, because the GSG
00509   // will call clear_gsg() in response to each release_texture(), and
00510   // we don't want to be modifying the _contexts list while we're
00511   // traversing it.
00512   Contexts temp = _contexts;
00513 
00514   Contexts::const_iterator ci;
00515   for (ci = temp.begin(); ci != temp.end(); ++ci) {
00516     GraphicsStateGuardianBase *gsg = (*ci).first;
00517     TextureContext *tc = (*ci).second;
00518     gsg->release_texture(tc);
00519   }
00520 
00521   // Now that we've called release_texture() on every known GSG, the
00522   // _contexts list should have completely emptied itself.
00523   nassertv(_contexts.empty());
00524 }
00525 
00526 ////////////////////////////////////////////////////////////////////
00527 //     Function: Texture::unprepare
00528 //       Access: Public
00529 //  Description: Frees the texture context only on the indicated GSG,
00530 //               if it exists there.
00531 ////////////////////////////////////////////////////////////////////
00532 void Texture::
00533 unprepare(GraphicsStateGuardianBase *gsg) {
00534   Contexts::iterator ci;
00535   ci = _contexts.find(gsg);
00536   if (ci != _contexts.end()) {
00537     TextureContext *tc = (*ci).second;
00538     gsg->release_texture(tc);
00539   }
00540 }
00541 
00542 ////////////////////////////////////////////////////////////////////
00543 //     Function: Texture::clear_gsg
00544 //       Access: Public
00545 //  Description: Removes the indicated GSG from the Texture's known
00546 //               GSG's, without actually releasing the texture on that
00547 //               GSG.  This is intended to be called only from
00548 //               GSG::release_texture(); it should never be called by
00549 //               user code.
00550 ////////////////////////////////////////////////////////////////////
00551 void Texture::
00552 clear_gsg(GraphicsStateGuardianBase *gsg) {
00553   Contexts::iterator ci;
00554   ci = _contexts.find(gsg);
00555   if (ci != _contexts.end()) {
00556     _contexts.erase(ci);
00557   } else {
00558     // If this assertion fails, clear_gsg() was called on a GSG which
00559     // the texture didn't know about.
00560     nassertv(false);
00561   }
00562 }
00563 
00564 ////////////////////////////////////////////////////////////////////
00565 //     Function: Texture::get_ram_image
00566 //       Access: Public
00567 //  Description: Returns the PixelBuffer associated with the texture.
00568 //               If the PixelBuffer does not currently have an
00569 //               associated RAM image, and the texture was generated
00570 //               by loading an image from a disk file (the most common
00571 //               case), this forces the reload of the same texture.
00572 //               This can happen if keep_texture_ram is configured to
00573 //               false, and we have previously prepared this texture
00574 //               with a GSG.
00575 //
00576 //               Note that it is not correct to call has_ram_image()
00577 //               first to test whether this function will fail.  A
00578 //               false return value from has_ram_image() indicates
00579 //               only that get_ram_image() may need to reload the
00580 //               texture from disk, which it will do automatically.
00581 //
00582 //               On the other hand, it is possible that the texture
00583 //               cannot be found on disk or is otherwise unavailable.
00584 //               If that happens, this function returns NULL.  There
00585 //               is no way to predict whether get_ram_image() will
00586 //               return NULL without calling it first.
00587 ////////////////////////////////////////////////////////////////////
00588 PixelBuffer *Texture::
00589 get_ram_image() {
00590   if (!has_ram_image() && has_filename()) {
00591     // Now we have to reload the texture image.
00592     gobj_cat.info()
00593       << "Reloading texture " << get_name() << "\n";
00594     
00595     if (has_alpha_fullpath()) {
00596       read(get_fullpath(), get_alpha_fullpath());
00597     } else {
00598       read(get_fullpath());
00599     }
00600   }
00601 
00602   if (has_ram_image()) {
00603     return _pbuffer;
00604   } else {
00605     return (PixelBuffer *)NULL;
00606   }
00607 }
00608 
00609 void Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr) {
00610   gsg->copy_texture(prepare(gsg), dr);
00611 }
00612 
00613 void Texture::copy(GraphicsStateGuardianBase *gsg, const DisplayRegion *dr,
00614                    const RenderBuffer &rb) {
00615   gsg->copy_texture(prepare(gsg), dr, rb);
00616 }
00617 
00618 ////////////////////////////////////////////////////////////////////
00619 //     Function: Texture::mark_dirty
00620 //       Access: Public
00621 //  Description: Sets the indicated dirty bits on for all texture
00622 //               contexts that share this Texture.  Does not change
00623 //               the bits that are not on.  This presumably will
00624 //               inform the GSG that the texture properties have
00625 //               changed.  See also TextureContext::mark_dirty().
00626 //
00627 //               Normally, this does not need to be called directly;
00628 //               changing the properties on the texture will
00629 //               automatically call this.  However, if you fiddle with
00630 //               the texture image directly, for instance by meddling
00631 //               with the _pbuffer member, you may need to explicitly
00632 //               call mark_dirty(Texture::DF_image).
00633 ////////////////////////////////////////////////////////////////////
00634 void Texture::
00635 mark_dirty(int flags_to_set) {
00636   if ((_all_dirty_flags & flags_to_set) == flags_to_set) {
00637     // If all the texture contexts already share these bits, no need
00638     // to do anything else.
00639     return;
00640   }
00641 
00642   // Otherwise, iterate through the contexts and mark them all dirty.
00643   Contexts::iterator ci;
00644   for (ci = _contexts.begin(); ci != _contexts.end(); ++ci) {
00645     (*ci).second->mark_dirty(flags_to_set);
00646   }
00647 
00648   _all_dirty_flags |= flags_to_set;
00649 }
00650 
00651 ////////////////////////////////////////////////////////////////////
00652 //     Function: Texture::string_wrap_mode
00653 //       Access: Public
00654 //  Description: Returns the WrapMode value associated with the given
00655 //               string representation, or WM_invalid if the string
00656 //               does not match any known WrapMode value.
00657 ////////////////////////////////////////////////////////////////////
00658 Texture::WrapMode Texture::
00659 string_wrap_mode(const string &string) {
00660   if (cmp_nocase_uh(string, "repeat") == 0) {
00661     return WM_repeat;
00662   } else if (cmp_nocase_uh(string, "clamp") == 0) {
00663     return WM_clamp;
00664   } else if (cmp_nocase_uh(string, "mirror") == 0) {
00665     return WM_clamp;
00666   } else if (cmp_nocase_uh(string, "mirror_once") == 0) {
00667     return WM_clamp;
00668   } else if (cmp_nocase_uh(string, "border_color") == 0) {
00669     return WM_border_color;
00670   } else {
00671     return WM_invalid;
00672   }
00673 }
00674 
00675 ////////////////////////////////////////////////////////////////////
00676 //     Function: Texture::string_filter_type
00677 //       Access: Public
00678 //  Description: Returns the FilterType value associated with the given
00679 //               string representation, or FT_invalid if the string
00680 //               does not match any known FilterType value.
00681 ////////////////////////////////////////////////////////////////////
00682 Texture::FilterType Texture::
00683 string_filter_type(const string &string) {
00684   if (cmp_nocase_uh(string, "nearest") == 0) {
00685     return FT_nearest;
00686   } else if (cmp_nocase_uh(string, "linear") == 0) {
00687     return FT_linear;
00688   } else if (cmp_nocase_uh(string, "nearest_mipmap_nearest") == 0) {
00689     return FT_nearest_mipmap_nearest;
00690   } else if (cmp_nocase_uh(string, "linear_mipmap_nearest") == 0) {
00691     return FT_linear_mipmap_nearest;
00692   } else if (cmp_nocase_uh(string, "nearest_mipmap_linear") == 0) {
00693     return FT_nearest_mipmap_linear;
00694   } else if (cmp_nocase_uh(string, "linear_mipmap_linear") == 0) {
00695     return FT_linear_mipmap_linear;
00696   } else if (cmp_nocase_uh(string, "mipmap") == 0) {
00697     return FT_linear_mipmap_linear;
00698 
00699   } else {
00700     return FT_invalid;
00701   }
00702 }
00703 
00704 ////////////////////////////////////////////////////////////////////
00705 //     Function: Texture::register_with_read_factory
00706 //       Access: Public, Static
00707 //  Description: Factory method to generate a Texture object
00708 ////////////////////////////////////////////////////////////////////
00709 void Texture::
00710 register_with_read_factory() {
00711   BamReader::get_factory()->register_factory(get_class_type(), make_Texture);
00712 }
00713 
00714 ////////////////////////////////////////////////////////////////////
00715 //     Function: Texture::make_Texture
00716 //       Access: Protected
00717 //  Description: Factory method to generate a Texture object
00718 ////////////////////////////////////////////////////////////////////
00719 TypedWritable* Texture::
00720 make_Texture(const FactoryParams &params) {
00721   //The process of making a texture is slightly
00722   //different than making other Writable objects.
00723   //That is because all creation of Textures should
00724   //be done through calls to TexturePool, which ensures
00725   //that any loads of the same Texture, refer to the
00726   //same memory
00727   DatagramIterator scan;
00728   BamReader *manager;
00729 
00730   parse_params(params, scan, manager);
00731 
00732   // Get the properties written by ImageBuffer::write_datagram().
00733   string name = scan.get_string();
00734   Filename filename = scan.get_string();
00735   Filename alpha_filename = scan.get_string();
00736 
00737   int primary_file_num_channels = 0;  
00738   int alpha_file_channel = 0;  
00739 
00740   if (manager->get_file_minor_ver() == 2) {
00741     // We temporarily had a version that stored the number of channels
00742     // here.
00743     primary_file_num_channels = scan.get_uint8();
00744 
00745   } else if (manager->get_file_minor_ver() >= 3) {
00746     primary_file_num_channels = scan.get_uint8();
00747     alpha_file_channel = scan.get_uint8();
00748   }
00749 
00750   PT(Texture) me;
00751 
00752   if (filename.empty()) {
00753     // This texture has no filename; since we don't have an image to
00754     // load, we can't actually create the texture.
00755     gobj_cat.info()
00756       << "Cannot create texture '" << name << "' with no filename.\n";
00757 
00758   } else {
00759     // This texture does have a filename, so try to load it from disk.
00760     if (alpha_filename.empty()) {
00761       me = TexturePool::load_texture(filename, primary_file_num_channels);
00762     } else {
00763       me = TexturePool::load_texture(filename, alpha_filename, 
00764                                      primary_file_num_channels, alpha_file_channel);
00765     }
00766   }
00767 
00768   if (me == (Texture *)NULL) {
00769     // Oops, we couldn't load the texture; we'll just return NULL.
00770     // But we do need a dummy texture to read in and ignore all of the
00771     // attributes.
00772     PT(Texture) dummy = new Texture;
00773     dummy->fillin(scan, manager);
00774 
00775   } else {
00776     me->set_name(name);
00777     me->fillin(scan, manager);
00778   }
00779   return me;
00780 }
00781 
00782 ////////////////////////////////////////////////////////////////////
00783 //     Function: Texture::fillin
00784 //       Access: Protected
00785 //  Description: Function that reads out of the datagram (or asks
00786 //               manager to read) all of the data that is needed to
00787 //               re-create this object and stores it in the appropiate
00788 //               place
00789 ////////////////////////////////////////////////////////////////////
00790 void Texture::
00791 fillin(DatagramIterator &scan, BamReader *manager) {
00792   //We don't want to call ImageBuffer::fillin, like we
00793   //would normally, since due to needing to know the name
00794   //of the Texture before creating it, we have already read
00795   //that name in.  This is something of a problem as it forces
00796   //Texture to know how the parent write_datagram works.  And
00797   //makes the assumption that the only data being written is
00798   //the name
00799 
00800   _wrapu = (enum WrapMode) scan.get_uint8();
00801   _wrapv = (enum WrapMode) scan.get_uint8();
00802   _minfilter = (enum FilterType) scan.get_uint8();
00803   _magfilter = (enum FilterType) scan.get_uint8();
00804   _anisotropic_degree = scan.get_int16();
00805 
00806   bool has_pbuffer = scan.get_bool();
00807   if (has_pbuffer) {
00808     PixelBuffer::Format format = (PixelBuffer::Format)scan.get_uint8();
00809     int num_channels = -1;
00810     num_channels = scan.get_uint8();
00811 
00812     if (_pbuffer != (PixelBuffer *)NULL) {
00813       if (num_channels == _pbuffer->get_num_components()) {
00814         // Only reset the format if the number of components hasn't
00815         // changed.
00816         _pbuffer->set_format(format);
00817       }
00818     }
00819   }
00820 }
00821 
00822 ////////////////////////////////////////////////////////////////////
00823 //     Function: Texture::write_datagram
00824 //       Access: Public
00825 //  Description: Function to write the important information in
00826 //               the particular object to a Datagram
00827 ////////////////////////////////////////////////////////////////////
00828 void Texture::
00829 write_datagram(BamWriter *manager, Datagram &me) {
00830   // We also need to write out the pixel buffer's format, even though
00831   // that's not stored as part of the texture structure.
00832   bool has_pbuffer = (_pbuffer != (PixelBuffer *)NULL);
00833 
00834   // These properties are read in again by make_Texture(), above.
00835   ImageBuffer::write_datagram(manager, me);
00836 
00837   // These properties are read in again by fillin(), above.
00838   me.add_uint8(_wrapu);
00839   me.add_uint8(_wrapv);
00840   me.add_uint8(_minfilter);
00841   me.add_uint8(_magfilter);
00842   me.add_int16(_anisotropic_degree);
00843 
00844   me.add_bool(has_pbuffer);
00845   if (has_pbuffer) {
00846     me.add_uint8(_pbuffer->get_format());
00847     me.add_uint8(_pbuffer->get_num_components());
00848   }
00849 }
00850 

Generated on Fri May 2 00:39:43 2003 for Panda by doxygen1.3