Browse Source

regularize TexturePool interfaces for cube maps etc.

David Rose 20 years ago
parent
commit
f169ddb221

+ 9 - 38
panda/src/gobj/texture.cxx

@@ -29,6 +29,7 @@
 #include "preparedGraphicsObjects.h"
 #include "pnmImage.h"
 #include "virtualFileSystem.h"
+#include "hashFilename.h"
 
 #include <stddef.h>
 
@@ -393,24 +394,13 @@ write(const Filename &name, int z) const {
 //               corresponding number of digits.
 ////////////////////////////////////////////////////////////////////
 bool Texture::
-read_pages(const Filename &fullpath_template, int z_size) {
-  string fp = fullpath_template.get_fullpath();
-  size_t hash = fp.rfind('#');
-  if (hash == string::npos) {
+read_pages(const HashFilename &fullpath_template, int z_size) {
+  if (!fullpath_template.has_hash()) {
     gobj_cat.error()
       << "Template " << fullpath_template << " contains no hash marks.\n";
     return false;
   }
 
-  // Count the number of hash marks.
-  size_t num_hash = 1;
-  while (hash >= num_hash && fp[hash - num_hash] == '#') {
-    num_hash++;
-  }
-
-  string prefix = fp.substr(0, hash - num_hash + 1);
-  string suffix = fp.substr(hash + 1);
-
   clear_ram_image();
 
   if (z_size == 0) {
@@ -432,29 +422,23 @@ read_pages(const Filename &fullpath_template, int z_size) {
   if (z_size != 0) {
     set_z_size(z_size);
     for (int z = 0; z < z_size; z++) {
-      ostringstream strm;
-      strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
-      if (!read(strm.str(), z)) {
+      if (!read(fullpath_template.get_filename_index(z), z)) {
         return false;
       }
     }
   } else {
     set_z_size(0);
     int z = 0;
-    ostringstream strm;
-    strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
-    Filename file(strm.str());
     VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
 
+    Filename file = fullpath_template.get_filename_index(z);
     while (vfs->exists(file)) {
       if (!read(file, z)) {
         return false;
       }
       ++z;
 
-      ostringstream strm;
-      strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
-      file = Filename(strm.str());
+      file = fullpath_template.get_filename_index(z);
     }
   }
 
@@ -475,28 +459,15 @@ read_pages(const Filename &fullpath_template, int z_size) {
 //               corresponding number of digits.
 ////////////////////////////////////////////////////////////////////
 bool Texture::
-write_pages(const Filename &fullpath_template) {
-  string fp = fullpath_template.get_fullpath();
-  size_t hash = fp.rfind('#');
-  if (hash == string::npos) {
+write_pages(const HashFilename &fullpath_template) {
+  if (!fullpath_template.has_hash()) {
     gobj_cat.error()
       << "Template " << fullpath_template << " contains no hash marks.\n";
     return false;
   }
 
-  // Count the number of hash marks.
-  size_t num_hash = 1;
-  while (hash >= num_hash && fp[hash - num_hash] == '#') {
-    num_hash++;
-  }
-
-  string prefix = fp.substr(0, hash - num_hash + 1);
-  string suffix = fp.substr(hash + 1);
-
   for (int z = 0; z < _z_size; z++) {
-    ostringstream strm;
-    strm << prefix << setw(num_hash) << setfill('0') << z << suffix;
-    if (!write(strm.str(), z)) {
+    if (!write(fullpath_template.get_filename_index(z), z)) {
       return false;
     }
   }

+ 4 - 2
panda/src/gobj/texture.h

@@ -21,6 +21,7 @@
 
 #include "pandabase.h"
 
+#include "filename.h"
 #include "typedWritableReferenceCount.h"
 #include "namable.h"
 #include "graphicsStateGuardianBase.h"
@@ -30,6 +31,7 @@ class PNMImage;
 class TextureContext;
 class FactoryParams;
 class PreparedGraphicsObjects;
+class HashFilename;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : Texture
@@ -159,8 +161,8 @@ PUBLISHED:
             int primary_file_num_channels = 0, int alpha_file_channel = 0);
   bool write(const Filename &fullpath, int z = 0) const;
 
-  bool read_pages(const Filename &fullpath_template, int z_size = 0);
-  bool write_pages(const Filename &fullpath_template);
+  bool read_pages(const HashFilename &fullpath_template, int z_size = 0);
+  bool write_pages(const HashFilename &fullpath_template);
 
   bool load(const PNMImage &pnmimage, int z = 0);
   bool store(PNMImage &pnmimage, int z = 0) const;

+ 44 - 0
panda/src/gobj/texturePool.I

@@ -74,6 +74,50 @@ load_texture(const string &filename, const string &alpha_filename,
                                     alpha_file_channel);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePool::load_3d_texture
+//       Access: Published, Static
+//  Description: Loads a 3-D texture that is specified with a series
+//               of n pages, all numbered in sequence, and beginning
+//               with index 0.  The filename should include a sequence
+//               of one or more hash characters ("#") which will be
+//               filled in with the index number of each level.
+////////////////////////////////////////////////////////////////////
+INLINE Texture *TexturePool::
+load_3d_texture(const string &filename_template) {
+  return get_ptr()->ns_load_3d_texture(filename_template);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePool::load_cube_map
+//       Access: Published, Static
+//  Description: Loads a cube map texture that is specified with a
+//               series of 6 pages, numbered 0 through 5.  The
+//               filename should include a sequence of one or more
+//               hash characters ("#") which will be filled in with
+//               the index number of each pagee.
+////////////////////////////////////////////////////////////////////
+INLINE Texture *TexturePool::
+load_cube_map(const string &filename_template) {
+  return get_ptr()->ns_load_cube_map(filename_template);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePool::get_normalization_cube_map
+//       Access: Published, Static
+//  Description: Returns a standard Texture object that has been
+//               created with
+//               Texture::generate_normalization_cube_map().  This
+//               Texture may be shared by any application code
+//               requiring a normalization cube map.  It will be at
+//               least as large as the specified size, though it may
+//               be larger.
+////////////////////////////////////////////////////////////////////
+INLINE Texture *TexturePool::
+get_normalization_cube_map(int size) {
+  return get_ptr()->ns_get_normalization_cube_map(size);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TexturePool::add_texture
 //       Access: Published, Static

+ 155 - 7
panda/src/gobj/texturePool.cxx

@@ -93,11 +93,8 @@ ns_load_texture(const Filename &orig_filename, int primary_file_num_channels) {
     << "Loading texture " << filename << "\n";
   PT(Texture) tex = new Texture;
   if (!tex->read(filename, 0, primary_file_num_channels)) {
-    // This texture was not found.
-    gobj_cat.error()
-      << "Unable to read texture \"" << filename << "\""
-      << " on texture_path " << texture_path
-      << " or model_path " << model_path <<"\n";
+    // This texture was not found or could not be read.
+    report_texture_unreadable(filename);
     return NULL;
   }
 
@@ -145,8 +142,8 @@ ns_load_texture(const Filename &orig_filename,
   PT(Texture) tex = new Texture;
   if (!tex->read(filename, alpha_filename, 0, primary_file_num_channels,
                  alpha_file_channel)) {
-    // This texture was not found.
-    gobj_cat.error() << "Unable to read texture " << filename << "\n";
+    // This texture was not found or could not be read.
+    report_texture_unreadable(filename);
     return NULL;
   }
 
@@ -158,6 +155,114 @@ ns_load_texture(const Filename &orig_filename,
   return tex;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePool::ns_load_3d_texture
+//       Access: Private
+//  Description: The nonstatic implementation of load_3d_texture().
+////////////////////////////////////////////////////////////////////
+Texture *TexturePool::
+ns_load_3d_texture(const HashFilename &filename_template) {
+  // Look up filename 0 on the model path.
+  Filename filename = filename_template.get_filename_index(0);
+  if (!_fake_texture_image.empty()) {
+    filename = _fake_texture_image;
+  }
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->resolve_filename(filename, get_texture_path()) ||
+    vfs->resolve_filename(filename, get_model_path());
+
+  // Then, replace everything before the hash code with the directory
+  // we've found.
+  string hash = filename_template.get_hash_to_end();
+  HashFilename hash_filename(filename.substr(0, filename.length() - hash.length()) + hash);
+
+  Textures::const_iterator ti;
+  ti = _textures.find(hash_filename);
+  if (ti != _textures.end()) {
+    // This texture was previously loaded.
+    return (*ti).second;
+  }
+
+  gobj_cat.info()
+    << "Loading 3-d texture " << hash_filename << "\n";
+  PT(Texture) tex = new Texture;
+  tex->setup_3d_texture();
+  if (!tex->read_pages(hash_filename)) {
+    // This texture was not found or could not be read.
+    report_texture_unreadable(filename);
+  }
+
+  // Set the original filename, before we searched along the path.
+  tex->set_filename(filename_template);
+
+  _textures[hash_filename] = tex;
+  return tex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePool::ns_load_cube_map
+//       Access: Private
+//  Description: The nonstatic implementation of load_cube_map().
+////////////////////////////////////////////////////////////////////
+Texture *TexturePool::
+ns_load_cube_map(const HashFilename &filename_template) {
+  // Look up filename 0 on the model path.
+  Filename filename = filename_template.get_filename_index(0);
+  if (!_fake_texture_image.empty()) {
+    filename = _fake_texture_image;
+  }
+
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  vfs->resolve_filename(filename, get_texture_path()) ||
+    vfs->resolve_filename(filename, get_model_path());
+
+  // Then, replace everything before the hash code with the directory
+  // we've found.
+  string hash = filename_template.get_hash_to_end();
+  HashFilename hash_filename(filename.substr(0, filename.length() - hash.length()) + hash);
+
+  Textures::const_iterator ti;
+  ti = _textures.find(hash_filename);
+  if (ti != _textures.end()) {
+    // This texture was previously loaded.
+    return (*ti).second;
+  }
+
+  gobj_cat.info()
+    << "Loading cube map texture " << hash_filename << "\n";
+  PT(Texture) tex = new Texture;
+  tex->setup_cube_map();
+  if (!tex->read_pages(hash_filename)) {
+    // This texture was not found or could not be read.
+    report_texture_unreadable(filename);
+  }
+
+  // Set the original filename, before we searched along the path.
+  tex->set_filename(filename_template);
+
+  _textures[hash_filename] = tex;
+  return tex;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePool::ns_get_normalization_cube_map
+//       Access: Private
+//  Description: The nonstatic implementation of get_normalization_cube_map().
+////////////////////////////////////////////////////////////////////
+Texture *TexturePool::
+ns_get_normalization_cube_map(int size) {
+  if (_normalization_cube_map == (Texture *)NULL) {
+    _normalization_cube_map = new Texture("normalization_cube_map");
+  }
+  if (_normalization_cube_map->get_x_size() < size ||
+      _normalization_cube_map->get_texture_type() != Texture::TT_cube_map) {
+    _normalization_cube_map->generate_normalization_cube_map(size);
+  }
+
+  return _normalization_cube_map;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TexturePool::ns_add_texture
 //       Access: Private
@@ -197,6 +302,7 @@ ns_release_texture(Texture *tex) {
 void TexturePool::
 ns_release_all_textures() {
   _textures.clear();
+  _normalization_cube_map = NULL;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -224,6 +330,16 @@ ns_garbage_collect() {
   }
 
   _textures.swap(new_set);
+
+  if (_normalization_cube_map->get_ref_count() == 1) {
+    if (gobj_cat.is_debug()) {
+      gobj_cat.debug()
+	<< "Releasing normalization cube map\n";
+    }
+    ++num_released;
+    _normalization_cube_map = NULL;
+  }
+
   return num_released;
 }
 
@@ -244,6 +360,38 @@ ns_list_contents(ostream &out) const {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: TexturePool::report_texture_unreadable
+//       Access: Private
+//  Description: Prints a suitable error message when a texture could
+//               not be loaded.
+////////////////////////////////////////////////////////////////////
+void TexturePool::
+report_texture_unreadable(const Filename &filename) const {
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+  if (!vfs->exists(filename)) {
+    if (filename.is_local()) {
+      // The file doesn't exist, and it wasn't
+      // fully-qualified--therefore, it wasn't found along either
+      // search path.
+      gobj_cat.error()
+	<< "Unable to find texture \"" << filename << "\""
+	<< " on texture_path " << texture_path
+	<< " or model_path " << model_path <<"\n";
+    } else {
+      // A fully-specified filename is not searched along the path, so
+      // don't mislead the user with the error message.
+      gobj_cat.error()
+	<< "Texture \"" << filename << "\" does not exist.\n";
+    }
+
+  } else {
+    // The file exists, but it couldn't be read for some reason.
+    gobj_cat.error()
+      << "Texture \"" << filename << "\" exists but cannot be read.\n";
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: TexturePool::get_ptr
 //       Access: Private, Static

+ 14 - 1
panda/src/gobj/texturePool.h

@@ -23,6 +23,7 @@
 #include "texture.h"
 #include "filename.h"
 #include "config_gobj.h"
+#include "hashFilename.h"
 
 #include "pmap.h"
 
@@ -47,6 +48,11 @@ PUBLISHED:
                                       const string &alpha_filename, 
                                       int primary_file_num_channels = 0,
                                       int alpha_file_channel = 0);
+  INLINE static Texture *load_3d_texture(const string &filename_template);
+  INLINE static Texture *load_cube_map(const string &filename_template);
+
+  INLINE static Texture *get_normalization_cube_map(int size);
+
   INLINE static void add_texture(Texture *texture);
   INLINE static void release_texture(Texture *texture);
   INLINE static void release_all_textures();
@@ -60,7 +66,6 @@ PUBLISHED:
   INLINE static bool has_fake_texture_image();
   INLINE static const string &get_fake_texture_image();
   
-  // static void output(ostream &out);
   static void write(ostream &out);
 
 private:
@@ -72,18 +77,26 @@ private:
                            const Filename &orig_alpha_filename, 
                            int primary_file_num_channels,
                            int alpha_file_channel);
+  Texture *ns_load_3d_texture(const HashFilename &filename_template);
+  Texture *ns_load_cube_map(const HashFilename &filename_template);
+  Texture *ns_get_normalization_cube_map(int size);
+
   void ns_add_texture(Texture *texture);
   void ns_release_texture(Texture *texture);
   void ns_release_all_textures();
   int ns_garbage_collect();
   void ns_list_contents(ostream &out) const;
 
+  void report_texture_unreadable(const Filename &filename) const;
+
   static TexturePool *get_ptr();
 
   static TexturePool *_global_ptr;
   typedef phash_map<string,  PT(Texture), string_hash> Textures;
   Textures _textures;
   string _fake_texture_image;
+
+  PT(Texture) _normalization_cube_map;
 };
 
 #include "texturePool.I"

+ 4 - 5
panda/src/particlesystem/spriteParticleRenderer.cxx

@@ -74,6 +74,7 @@ SpriteParticleRenderer(Texture *tex) :
 SpriteParticleRenderer::
 SpriteParticleRenderer(const SpriteParticleRenderer& copy) :
   BaseParticleRenderer(copy), 
+  _anims(copy._anims),
   _color(copy._color),
   _height(copy._height),
   _width(copy._width),
@@ -95,7 +96,6 @@ SpriteParticleRenderer(const SpriteParticleRenderer& copy) :
   _blend_method(copy._blend_method),
   _color_interpolation_manager(copy._color_interpolation_manager),
   _pool_size(0),
-  _anims(copy._anims),
   _birth_list(copy._birth_list) {
   init_geoms();
 }
@@ -299,7 +299,6 @@ add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) {
     GeomNode *gnode = NULL;
     const Geom *geom;
     const GeomPrimitive *primitive;
-    bool got_texcoord;
 
     for (int i = 0; i < np_col.get_num_paths(); ++i) {
       // Get the node from which we'll extract the geometry information.
@@ -309,7 +308,7 @@ add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) {
       nassertv(gnode->get_num_geoms() > 0);
       geom = gnode->get_geom(0);
       
-      got_texcoord = false;
+      bool got_texcoord = false;
       TexCoordf min_uv(0.0f, 0.0f);
       TexCoordf max_uv(0.0f, 0.0f);
       
@@ -389,7 +388,7 @@ add_from_node(const NodePath &node_path, bool size_from_texels, bool resize) {
         float height = max(max_xyz[1] - min_xyz[1],
                            max_xyz[2] - min_xyz[2]);
       
-        if (size_from_texels && got_texcoord) {
+        if (size_from_texels) {
           // If size_from_texels is true, we get the particle size from the
           // number of texels in the source image.
           float y_texels = _anims[0]->get_frame(0)->get_y_size() * fabs(_anims[0]->get_ur(0)[1] - _anims[0]->get_ll(0)[1]);
@@ -601,7 +600,7 @@ render(pvector< PT(PhysicsObject) >& po_vector, int ttl_particles) {
   for (i = 0; i < anim_count; ++i) {
     for (j = 0; j < _anim_size[i]; ++j) {
       // Set the particle per frame counts to 0.
-      memset(_ttl_count[i],NULL,_anim_size[i]*sizeof(int));
+      memset(_ttl_count[i], 0, _anim_size[i]*sizeof(int));
       
       _sprite_writer[i][j].vertex = GeomVertexWriter(_vdata[i][j], InternalName::get_vertex());
       _sprite_writer[i][j].color = GeomVertexWriter(_vdata[i][j], InternalName::get_color());

+ 5 - 1
panda/src/putil/Sources.pp

@@ -31,6 +31,7 @@
     firstOfPairCompare.I firstOfPairCompare.h \
     firstOfPairLess.I firstOfPairLess.h \
     globalPointerRegistry.I globalPointerRegistry.h \
+    hashFilename.I hashFilename.h \
     indirectCompareNames.I indirectCompareNames.h \
     indirectCompareSort.I indirectCompareSort.h \
     indirectCompareTo.I indirectCompareTo.h \
@@ -70,7 +71,9 @@
     datagramInputFile.cxx datagramOutputFile.cxx \
     factoryBase.cxx \
     factoryParam.cxx factoryParams.cxx \
-    globalPointerRegistry.cxx ioPtaDatagramFloat.cxx \
+    globalPointerRegistry.cxx \
+    hashFilename.cxx \
+    ioPtaDatagramFloat.cxx \
     ioPtaDatagramInt.cxx ioPtaDatagramShort.cxx \
     keyboardButton.cxx lineStream.cxx lineStreamBuf.cxx \
     load_prc_file.cxx \
@@ -113,6 +116,7 @@
     firstOfPairCompare.I firstOfPairCompare.h \
     firstOfPairLess.I firstOfPairLess.h \
     globalPointerRegistry.I globalPointerRegistry.h \
+    hashFilename.I hashFilename.h \
     indirectCompareNames.I indirectCompareNames.h \
     indirectCompareSort.I indirectCompareSort.h \
     indirectCompareTo.I indirectCompareTo.h \

+ 100 - 0
panda/src/putil/hashFilename.I

@@ -0,0 +1,100 @@
+// Filename: hashFilename.I
+// Created by:  drose (02Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE HashFilename::
+HashFilename(const string &filename_pattern) :
+  Filename(filename_pattern)
+{
+  locate_hash();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE HashFilename::
+HashFilename(const HashFilename &copy) :
+  Filename(copy),
+  _hash_start(copy._hash_start),
+  _hash_end(copy._hash_end)
+{
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HashFilename::
+operator = (const HashFilename &copy) {
+  Filename::operator = (copy);
+  _hash_start = copy._hash_start;
+  _hash_end = copy._hash_end;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::Copy Assignment Operator
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE void HashFilename::
+operator = (const Filename &copy) {
+  Filename::operator = (copy);
+  locate_hash();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::Destructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE HashFilename::
+~HashFilename() {
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::has_hash
+//       Access: Published
+//  Description: Returns true if the filename pattern did include a
+//               sequence of hash marks, false otherwise.  If this is
+//               true, then get_filename_index() will return a
+//               different filename each time.
+////////////////////////////////////////////////////////////////////
+INLINE bool HashFilename::
+has_hash() const {
+  return (_hash_start != _hash_end);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::get_hash_to_end
+//       Access: Published
+//  Description: Returns the part of the filename beginning at the
+//               hash sequence (if any), and continuing to the end of
+//               the filename.
+////////////////////////////////////////////////////////////////////
+INLINE string HashFilename::
+get_hash_to_end() const {
+  return _filename.substr(_hash_start);
+}

+ 80 - 0
panda/src/putil/hashFilename.cxx

@@ -0,0 +1,80 @@
+// Filename: hashFilename.cxx
+// Created by:  drose (02Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "hashFilename.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::get_filename_index
+//       Access: Published
+//  Description: Returns a Filename, derived from the HashFilename,
+//               with the sequence of hash marks (if any) replaced by
+//               the indicated index number.  If the HashFilename does
+//               not contain a sequence of hash marks, this quietly
+//               returns the original filename.
+////////////////////////////////////////////////////////////////////
+Filename HashFilename::
+get_filename_index(int index) const {
+  Filename file(*this);
+
+  if (_hash_end != _hash_start) {
+    ostringstream strm;
+    strm << _filename.substr(0, _hash_start) 
+	 << setw(_hash_end - _hash_start) << setfill('0') << index
+	 << _filename.substr(_hash_end);
+    file.set_fullpath(strm.str());
+  }
+
+  return file;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::set_hash_to_end
+//       Access: Published
+//  Description: Replaces the part of the filename from the beginning
+//               of the has sequence to the end of the filename.
+////////////////////////////////////////////////////////////////////
+void HashFilename::
+set_hash_to_end(const string &s) {
+  _filename.replace(_hash_start, string::npos, s);
+
+  locate_basename();
+  locate_extension();
+  locate_hash();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::locate_hash
+//       Access: Private
+//  Description: Identifies the part of the filename that contains the
+//               sequence of hash marks, if any.
+////////////////////////////////////////////////////////////////////
+void HashFilename::
+locate_hash() {
+  _hash_end = _filename.rfind('#');
+  if (_hash_end == string::npos) {
+    _hash_end = string::npos;
+    _hash_start = string::npos;
+
+  } else {
+    _hash_start = _hash_end;
+    ++_hash_end;
+    while (_hash_start > 0 && _filename[_hash_start - 1] == '#') {
+      --_hash_start;
+    }
+  }
+}

+ 60 - 0
panda/src/putil/hashFilename.h

@@ -0,0 +1,60 @@
+// Filename: hashFilename.h
+// Created by:  drose (02Aug05)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
+//
+// All use of this software is subject to the terms of the Panda 3d
+// Software license.  You should have received a copy of this license
+// along with this source code; you will also find a current copy of
+// the license at http://etc.cmu.edu/panda3d/docs/license/ .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef HASHFILENAME_H
+#define HASHFILENAME_H
+
+#include "pandabase.h"
+#include "filename.h"
+
+////////////////////////////////////////////////////////////////////
+//       Class : HashFilename
+// Description : This is a specialization on Filename that is intended
+//               to be used to record a filename pattern for reading a
+//               numeric sequence of filenames in a directory; for
+//               instance, as used by TexturePool::load_cube_map().
+//
+//               The Filename may include a string of one or more hash
+//               marks ('#') in the basename or extension part.  These
+//               will be filled in with digits when
+//               get_filename_index() is called.
+////////////////////////////////////////////////////////////////////
+class EXPCL_PANDA HashFilename : public Filename {
+PUBLISHED:
+  INLINE HashFilename(const string &filename_pattern = string());
+  INLINE HashFilename(const HashFilename &copy);
+  INLINE void operator = (const HashFilename &copy);
+  INLINE void operator = (const Filename &copy);
+  INLINE ~HashFilename();
+
+  INLINE bool has_hash() const;
+  Filename get_filename_index(int index) const;
+
+  INLINE string get_hash_to_end() const;
+  void set_hash_to_end(const string &s);
+  
+private:
+  void locate_hash();
+
+  size_t _hash_start;
+  size_t _hash_end;
+};
+
+#include "hashFilename.I"
+
+#endif

+ 1 - 0
panda/src/putil/putil_composite1.cxx

@@ -17,6 +17,7 @@
 #include "factoryParam.cxx"
 #include "factoryParams.cxx"
 #include "globalPointerRegistry.cxx"
+#include "hashFilename.cxx"
 #include "ioPtaDatagramFloat.cxx"
 #include "ioPtaDatagramInt.cxx"
 #include "ioPtaDatagramShort.cxx"