ソースを参照

load cube maps etc. from egg files

David Rose 20 年 前
コミット
5fde282d94

+ 27 - 0
panda/src/doc/eggSyntax.txt

@@ -171,6 +171,7 @@ appear before they are referenced.
   <Scalar> wrap { repeat-definition }
   <Scalar> wrapu { repeat-definition }
   <Scalar> wrapv { repeat-definition }
+  <Scalar> wrapw { repeat-definition }
 
     This defines the behavior of the texture image outside of the
     normal (u,v) range 0.0 - 1.0.  It is "REPEAT" to repeat the
@@ -178,6 +179,32 @@ appear before they are referenced.
     specified independently for each axis via "wrapu" and "wrapv", or
     it may be specified for both simultaneously via "wrap".
 
+    Although less often used, for 3-d textures wrapw may also be
+    specified, and it behaves similarly to wrapu and wrapv.
+
+  <Scalar> type { texture-type }
+
+    This may be one of the following attributes:
+
+      1D
+      2D
+      3D
+      CUBE_MAP
+
+    The default is "2D", which specifies a normal, 2-d texture.  If
+    any of the other types is specified instead, a texture image of
+    the corresponding type is loaded.
+
+    If 3D or CUBE_MAP is specified, then a series of texture images
+    must be loaded to make up the complete texture; in this case, the
+    texture filename is expected to include a sequence of one or more
+    hash mark ("#") characters, which will be filled in with the
+    sequence number.  The first image in the sequence must be numbered
+    0, and there must be no gaps in the sequence.  In this case, a
+    separate alpha-file designation is ignored; the alpha channel, if
+    present, must be included in the same image with the color
+    channel(s).
+
   <Scalar> minfilter { filter-type }
   <Scalar> magfilter { filter-type }
   <Scalar> magfilteralpha { filter-type }

+ 22 - 7
panda/src/egg/eggGroupNode.cxx

@@ -33,6 +33,7 @@
 #include "pt_EggMaterial.h"
 #include "config_egg.h"
 
+#include "hashFilename.h"
 #include "dSearchPath.h"
 #include "deg_2_rad.h"
 #include "dcast.h"
@@ -421,6 +422,7 @@ has_absolute_pathnames() const {
 ////////////////////////////////////////////////////////////////////
 void EggGroupNode::
 resolve_filenames(const DSearchPath &searchpath) {
+  cerr << "resolve_filenames " << get_name() << "\n";
   Children::iterator ci;
   for (ci = _children.begin();
        ci != _children.end();
@@ -428,14 +430,27 @@ resolve_filenames(const DSearchPath &searchpath) {
     EggNode *child = *ci;
     if (child->is_of_type(EggTexture::get_class_type())) {
       EggTexture *tex = DCAST(EggTexture, child);
-      Filename tex_filename = tex->get_filename();
-      tex_filename.resolve_filename(searchpath);
-      tex->set_filename(tex_filename);
+      if (tex->has_hash_filename()) {
+	HashFilename tex_filename = tex->get_filename();
+	tex_filename.resolve_filename(searchpath);
+	tex->set_filename(tex_filename);
+	
+	if (tex->has_alpha_filename()) {
+	  HashFilename alpha_filename = tex->get_alpha_filename();
+	  alpha_filename.resolve_filename(searchpath);
+	  tex->set_alpha_filename(alpha_filename);
+	}
 
-      if (tex->has_alpha_filename()) {
-        Filename alpha_filename = tex->get_alpha_filename();
-        alpha_filename.resolve_filename(searchpath);
-        tex->set_alpha_filename(alpha_filename);
+      } else {
+	Filename tex_filename = tex->get_filename();
+	tex_filename.resolve_filename(searchpath);
+	tex->set_filename(tex_filename);
+	
+	if (tex->has_alpha_filename()) {
+	  Filename alpha_filename = tex->get_alpha_filename();
+	  alpha_filename.resolve_filename(searchpath);
+	  tex->set_alpha_filename(alpha_filename);
+	}
       }
 
     } else if (child->is_of_type(EggFilenameNode::get_class_type())) {

+ 70 - 1
panda/src/egg/eggTexture.I

@@ -17,6 +17,41 @@
 ////////////////////////////////////////////////////////////////////
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::has_hash_filename
+//       Access: Published
+//  Description: Returns true if the texture filename is expected to
+//               contain a hash mark standing in for a sequence
+//               number, or false if the filename is a literal
+//               filename.  This will be true only if TextureType is
+//               TT_3d_texture or TT_cube_map.
+////////////////////////////////////////////////////////////////////
+INLINE bool EggTexture::
+has_hash_filename() const {
+  return (_texture_type == TT_3d_texture ||
+	  _texture_type == TT_cube_map);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::set_texture_type
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggTexture::
+set_texture_type(TextureType texture_type) {
+  _texture_type = texture_type;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::get_texture_type
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggTexture::TextureType EggTexture::
+get_texture_type() const {
+  return _texture_type;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggTexture::set_format
 //       Access: Published
@@ -116,7 +151,7 @@ get_wrap_v() const {
 //     Function: EggTexture::determine_wrap_v
 //       Access: Published
 //  Description: Determines the appropriate wrap in the V direction.
-//               This is different from get_wrap_v() in that if the U
+//               This is different from get_wrap_v() in that if the V
 //               wrap is unspecified, it returns the overall wrap
 //               value.
 ////////////////////////////////////////////////////////////////////
@@ -125,6 +160,40 @@ determine_wrap_v() const {
   return (_wrap_v == WM_unspecified) ? get_wrap_mode() : get_wrap_v();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::set_wrap_w
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggTexture::
+set_wrap_w(WrapMode mode) {
+  _wrap_w = mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::get_wrap_w
+//       Access: Published
+//  Description: Returns the amount specified for W wrap.  This may be
+//               unspecified, even if there is an overall wrap value.
+////////////////////////////////////////////////////////////////////
+INLINE EggTexture::WrapMode EggTexture::
+get_wrap_w() const {
+  return _wrap_w;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::determine_wrap_w
+//       Access: Published
+//  Description: Determines the appropriate wrap in the W direction.
+//               This is different from get_wrap_w() in that if the W
+//               wrap is unspecified, it returns the overall wrap
+//               value.
+////////////////////////////////////////////////////////////////////
+INLINE EggTexture::WrapMode EggTexture::
+determine_wrap_w() const {
+  return (_wrap_w == WM_unspecified) ? get_wrap_mode() : get_wrap_w();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggTexture::set_minfilter
 //       Access: Published

+ 84 - 1
panda/src/egg/eggTexture.cxx

@@ -35,10 +35,12 @@ EggTexture::
 EggTexture(const string &tref_name, const string &filename)
   : EggFilenameNode(tref_name, filename)
 {
+  _texture_type = TT_unspecified;
   _format = F_unspecified;
   _wrap_mode = WM_unspecified;
   _wrap_u = WM_unspecified;
   _wrap_v = WM_unspecified;
+  _wrap_w = WM_unspecified;
   _minfilter = FT_unspecified;
   _magfilter = FT_unspecified;
   _anisotropic_degree = 0;
@@ -75,10 +77,12 @@ operator = (const EggTexture &copy) {
   EggRenderMode::operator = (copy);
   EggTransform::operator = (copy);
 
+  _texture_type = copy._texture_type;
   _format = copy._format;
   _wrap_mode = copy._wrap_mode;
   _wrap_u = copy._wrap_u;
   _wrap_v = copy._wrap_v;
+  _wrap_w = copy._wrap_w;
   _minfilter = copy._minfilter;
   _magfilter = copy._magfilter;
   _anisotropic_degree = copy._anisotropic_degree;
@@ -136,6 +140,11 @@ write(ostream &out, int indent_level) const {
       << get_alpha_file_channel() << " }\n";
   }
 
+  if (get_texture_type() != TT_unspecified) {
+    indent(out, indent_level + 2)
+      << "<Scalar> texture_type { " << get_texture_type() << " }\n";
+  }
+
   if (get_format() != F_unspecified) {
     indent(out, indent_level + 2)
       << "<Scalar> format { " << get_format() << " }\n";
@@ -156,6 +165,11 @@ write(ostream &out, int indent_level) const {
       << "<Scalar> wrapv { " << get_wrap_v() << " }\n";
   }
 
+  if (get_wrap_w() != WM_unspecified) {
+    indent(out, indent_level + 2)
+      << "<Scalar> wrapw { " << get_wrap_w() << " }\n";
+  }
+
   if (get_minfilter() != FT_unspecified) {
     indent(out, indent_level + 2)
       << "<Scalar> minfilter { " << get_minfilter() << " }\n";
@@ -331,10 +345,12 @@ is_equivalent_to(const EggTexture &other, int eq) const {
 
   if (eq & E_attributes) {
     //cout << "compared by attributes" << endl;
-    if (_format != other._format ||
+    if (_texture_type != other._texture_type ||
+	_format != other._format ||
         _wrap_mode != other._wrap_mode ||
         _wrap_u != other._wrap_u ||
         _wrap_v != other._wrap_v ||
+        _wrap_w != other._wrap_w ||
         _minfilter != other._minfilter ||
         _magfilter != other._magfilter ||
         _env_type != other._env_type) {
@@ -407,6 +423,9 @@ sorts_less_than(const EggTexture &other, int eq) const {
   }
 
   if (eq & E_attributes) {
+    if (_texture_type != other._texture_type) {
+      return (int)_texture_type < (int)other._texture_type;
+    }
     if (_format != other._format) {
       return (int)_format < (int)other._format;
     }
@@ -419,6 +438,9 @@ sorts_less_than(const EggTexture &other, int eq) const {
     if (_wrap_v != other._wrap_v) {
       return (int)_wrap_v < (int)other._wrap_v;
     }
+    if (_wrap_w != other._wrap_w) {
+      return (int)_wrap_w < (int)other._wrap_w;
+    }
     if (_minfilter != other._minfilter) {
       return (int)_minfilter < (int)other._minfilter;
     }
@@ -560,6 +582,40 @@ multitexture_over(EggTexture *other) {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::string_texture_type
+//       Access: Published, Static
+//  Description: Returns the Texture_ype value associated with the given
+//               string representation, or TT_unspecified if the string
+//               does not match any known TextureType value.
+////////////////////////////////////////////////////////////////////
+EggTexture::TextureType EggTexture::
+string_texture_type(const string &string) {
+  if (cmp_nocase_uh(string, "1d") == 0 ||
+      cmp_nocase_uh(string, "1dtexture") == 0 ||
+      cmp_nocase_uh(string, "1d_texture") == 0) {
+    return TT_1d_texture;
+
+  } else if (cmp_nocase_uh(string, "2d") == 0 ||
+	     cmp_nocase_uh(string, "2dtexture") == 0 ||
+	     cmp_nocase_uh(string, "2d_texture") == 0) {
+    return TT_2d_texture;
+
+  } else if (cmp_nocase_uh(string, "3d") == 0 ||
+	     cmp_nocase_uh(string, "3dtexture") == 0 ||
+	     cmp_nocase_uh(string, "3d_texture") == 0) {
+    return TT_2d_texture;
+
+  } else if (cmp_nocase_uh(string, "cube") == 0 ||
+	     cmp_nocase_uh(string, "cubemap") == 0 ||
+	     cmp_nocase_uh(string, "cube_map") == 0) {
+    return TT_cube_map;
+
+  } else {
+    return TT_unspecified;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggTexture::string_format
 //       Access: Published, Static
@@ -912,6 +968,33 @@ r_min_multitexture_sort(int sort, EggTexture::MultiTextures &cycle_detector) {
 }
 
 
+////////////////////////////////////////////////////////////////////
+//     Function: TextureType output operator
+//  Description:
+////////////////////////////////////////////////////////////////////
+ostream &operator << (ostream &out, EggTexture::TextureType texture_type) {
+  switch (texture_type) {
+  case EggTexture::TT_unspecified:
+    return out << "unspecified";
+
+  case EggTexture::TT_1d_texture:
+    return out << "1d";
+
+  case EggTexture::TT_2d_texture:
+    return out << "2d";
+
+  case EggTexture::TT_3d_texture:
+    return out << "3d";
+
+  case EggTexture::TT_cube_map:
+    return out << "cube-map";
+  }
+
+  nassertr(false, out);
+  return out << "(**invalid**)";
+}
+
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Format output operator
 //  Description:

+ 17 - 1
panda/src/egg/eggTexture.h

@@ -58,6 +58,10 @@ PUBLISHED:
 
   bool has_alpha_channel(int num_components) const;
 
+  enum TextureType {
+    TT_unspecified, TT_1d_texture,
+    TT_2d_texture, TT_3d_texture, TT_cube_map
+  };
   enum Format {
     F_unspecified,
     F_rgba, F_rgbm, F_rgba12, F_rgba8, F_rgba4, F_rgba5,
@@ -146,6 +150,11 @@ PUBLISHED:
     TG_point_sprite,
   };
 
+  INLINE bool has_hash_filename() const;
+
+  INLINE void set_texture_type(TextureType texture_type);
+  INLINE TextureType get_texture_type() const;
+
   INLINE void set_format(Format format);
   INLINE Format get_format() const;
 
@@ -160,6 +169,10 @@ PUBLISHED:
   INLINE WrapMode get_wrap_v() const;
   INLINE WrapMode determine_wrap_v() const;
 
+  INLINE void set_wrap_w(WrapMode mode);
+  INLINE WrapMode get_wrap_w() const;
+  INLINE WrapMode determine_wrap_w() const;
+
   INLINE void set_minfilter(FilterType type);
   INLINE FilterType get_minfilter() const;
 
@@ -234,6 +247,7 @@ PUBLISHED:
   bool multitexture_over(EggTexture *other);
   INLINE int get_multitexture_sort() const;
 
+  static TextureType string_texture_type(const string &string);
   static Format string_format(const string &string);
   static WrapMode string_wrap_mode(const string &string);
   static FilterType string_filter_type(const string &string);
@@ -265,8 +279,9 @@ private:
     F_has_alpha_scale        = 0x0200,
   };
 
+  TextureType _texture_type;
   Format _format;
-  WrapMode _wrap_mode, _wrap_u, _wrap_v;
+  WrapMode _wrap_mode, _wrap_u, _wrap_v, _wrap_w;
   FilterType _minfilter, _magfilter;
   int _anisotropic_degree;
   EnvType _env_type;
@@ -344,6 +359,7 @@ INLINE ostream &operator << (ostream &out, const EggTexture &n) {
   return out << n.get_filename();
 }
 
+EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::TextureType texture_type);
 EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::Format format);
 EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::WrapMode mode);
 EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::FilterType type);

+ 9 - 1
panda/src/egg/parser.yxx

@@ -344,7 +344,15 @@ texture_body:
   double value = $<_number>5;
   string strval = $<_string>5;
 
-  if (cmp_nocase_uh(name, "format") == 0) {
+  if (cmp_nocase_uh(name, "type") == 0) {
+    EggTexture::TextureType tt = EggTexture::string_texture_type(strval);
+    if (tt == EggTexture::TT_unspecified) {
+      eggyywarning("Unknown texture texture_type " + strval);
+    } else {
+      texture->set_texture_type(tt);
+    }
+
+  } else if (cmp_nocase_uh(name, "format") == 0) {
     EggTexture::Format f = EggTexture::string_format(strval);
     if (f == EggTexture::F_unspecified) {
       eggyywarning("Unknown texture format " + strval);

+ 48 - 9
panda/src/egg2pg/eggLoader.cxx

@@ -852,16 +852,31 @@ load_texture(TextureDef &def, const EggTexture *egg_tex) {
     break;
   }
 
-  Texture *tex;
-  if (egg_tex->has_alpha_filename() && wanted_alpha) {
-    tex = TexturePool::load_texture(egg_tex->get_fullpath(),
-                                    egg_tex->get_alpha_fullpath(),
-                                    wanted_channels,
-                                    egg_tex->get_alpha_file_channel());
-  } else {
-    tex = TexturePool::load_texture(egg_tex->get_fullpath(),
-                                    wanted_channels);
+  PT(Texture) tex;
+  switch (egg_tex->get_texture_type()) {
+  case EggTexture::TT_unspecified:
+  case EggTexture::TT_1d_texture:
+  case EggTexture::TT_2d_texture:
+    if (egg_tex->has_alpha_filename() && wanted_alpha) {
+      tex = TexturePool::load_texture(egg_tex->get_fullpath(),
+				      egg_tex->get_alpha_fullpath(),
+				      wanted_channels,
+				      egg_tex->get_alpha_file_channel());
+    } else {
+      tex = TexturePool::load_texture(egg_tex->get_fullpath(),
+				      wanted_channels);
+    }
+    break;
+
+  case EggTexture::TT_3d_texture:
+    tex = TexturePool::load_3d_texture(HashFilename(egg_tex->get_fullpath()));
+    break;
+
+  case EggTexture::TT_cube_map:
+    tex = TexturePool::load_cube_map(HashFilename(egg_tex->get_fullpath()));
+    break;
   }
+
   if (tex == (Texture *)NULL) {
     return false;
   }
@@ -941,6 +956,30 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
       << (int)egg_tex->determine_wrap_v() << "\n";
   }
 
+  switch (egg_tex->determine_wrap_w()) {
+  case EggTexture::WM_repeat:
+    tex->set_wrap_w(Texture::WM_repeat);
+    break;
+
+  case EggTexture::WM_clamp:
+    if (egg_ignore_clamp) {
+      egg2pg_cat.warning()
+        << "Ignoring clamp request\n";
+      tex->set_wrap_w(Texture::WM_repeat);
+    } else {
+      tex->set_wrap_w(Texture::WM_clamp);
+    }
+    break;
+
+  case EggTexture::WM_unspecified:
+    break;
+
+  default:
+    egg2pg_cat.warning()
+      << "Unexpected texture wrap flag: "
+      << (int)egg_tex->determine_wrap_w() << "\n";
+  }
+
   switch (egg_tex->get_minfilter()) {
   case EggTexture::FT_nearest:
     tex->set_minfilter(Texture::FT_nearest);

+ 3 - 3
panda/src/putil/animInterface.cxx

@@ -90,7 +90,7 @@ AnimInterface::
 void AnimInterface::
 play(double from, double to) {
   if (from >= to) {
-    pose(from);
+    pose((int)from);
     return;
   }
 
@@ -122,7 +122,7 @@ play(double from, double to) {
 void AnimInterface::
 loop(bool restart, double from, double to) {
   if (from >= to) {
-    pose(from);
+    pose((int)from);
     return;
   }
 
@@ -157,7 +157,7 @@ loop(bool restart, double from, double to) {
 void AnimInterface::
 pingpong(bool restart, double from, double to) {
   if (from >= to) {
-    pose(from);
+    pose((int)from);
     return;
   }
 

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

@@ -42,6 +42,18 @@ HashFilename(const HashFilename &copy) :
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::Copy Constructor
+//       Access: Published
+//  Description: 
+////////////////////////////////////////////////////////////////////
+INLINE HashFilename::
+HashFilename(const Filename &copy) :
+  Filename(copy)
+{
+  locate_hash();
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: HashFilename::Copy Assignment Operator
 //       Access: Published

+ 86 - 1
panda/src/putil/hashFilename.cxx

@@ -46,7 +46,7 @@ get_filename_index(int index) const {
 //     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.
+//               of the hash sequence to the end of the filename.
 ////////////////////////////////////////////////////////////////////
 void HashFilename::
 set_hash_to_end(const string &s) {
@@ -57,6 +57,91 @@ set_hash_to_end(const string &s) {
   locate_hash();
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::resolve_filename
+//       Access: Published
+//  Description: Searches the given search path for the filename.  If
+//               it is found, updates the filename to the full
+//               pathname found and returns true; otherwise, returns
+//               false.
+////////////////////////////////////////////////////////////////////
+bool HashFilename::
+resolve_filename(const DSearchPath &searchpath,
+                 const string &default_extension) {
+  if (!has_hash()) {
+    return Filename::resolve_filename(searchpath, default_extension);
+  }
+
+  Filename file0 = get_filename_index(0);
+  if (!file0.resolve_filename(searchpath, default_extension)) {
+    return false;
+  }
+
+  int change = file0.length() - length();
+
+  if (file0.length() < _hash_start || _hash_end + change < 0 ||
+      file0.substr(_hash_end + change) != substr(_hash_end)) {
+    // Hmm, somehow the suffix part of the filename--everything after
+    // the hash sequence--was changed by the above resolve operation.
+    // Abandon ship.
+    return false;
+  }
+
+  // Replace the prefix part of the filename--everything before the
+  // hash sequence.
+  _filename = file0.substr(0, _hash_start + change) + substr(_hash_start);
+  _hash_start += change;
+  _hash_end += change;
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: HashFilename::find_on_searchpath
+//       Access: Published
+//  Description: Performs the reverse of the resolve_filename()
+//               operation: assuming that the current filename is
+//               fully-specified pathname (i.e. beginning with '/'),
+//               look on the indicated search path for a directory
+//               under which the file can be found.  When found,
+//               adjust the Filename to be relative to the indicated
+//               directory name.
+//
+//               Returns the index of the directory on the searchpath
+//               at which the file was found, or -1 if it was not
+//               found.
+////////////////////////////////////////////////////////////////////
+int HashFilename::
+find_on_searchpath(const DSearchPath &searchpath) {
+  if (!has_hash()) {
+    return Filename::find_on_searchpath(searchpath);
+  }
+
+  Filename file0 = get_filename_index(0);
+  int index = file0.find_on_searchpath(searchpath);
+  if (index == -1) {
+    return -1;
+  }
+
+  int change = file0.length() - length();
+
+  if (file0.length() < _hash_start || _hash_end + change < 0 ||
+      file0.substr(_hash_end + change) != substr(_hash_end)) {
+    // Hmm, somehow the suffix part of the filename--everything after
+    // the hash sequence--was changed by the above resolve operation.
+    // Abandon ship.
+    return false;
+  }
+
+  // Replace the prefix part of the filename--everything before the
+  // hash sequence.
+  _filename = file0.substr(0, _hash_start + change) + substr(_hash_start);
+  _hash_start += change;
+  _hash_end += change;
+
+  return index;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: HashFilename::locate_hash
 //       Access: Private

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

@@ -38,6 +38,7 @@ class EXPCL_PANDA HashFilename : public Filename {
 PUBLISHED:
   INLINE HashFilename(const string &filename_pattern = string());
   INLINE HashFilename(const HashFilename &copy);
+  INLINE HashFilename(const Filename &copy);
   INLINE void operator = (const HashFilename &copy);
   INLINE void operator = (const Filename &copy);
   INLINE ~HashFilename();
@@ -47,6 +48,10 @@ PUBLISHED:
 
   INLINE string get_hash_to_end() const;
   void set_hash_to_end(const string &s);
+
+  bool resolve_filename(const DSearchPath &searchpath,
+                        const string &default_extension = string());
+  int find_on_searchpath(const DSearchPath &searchpath);
   
 private:
   void locate_hash();