Browse Source

compressed textures, texture objects

David Rose 20 years ago
parent
commit
e6369ba5a4
42 changed files with 2305 additions and 255 deletions
  1. 57 0
      panda/src/display/graphicsEngine.cxx
  2. 2 0
      panda/src/display/graphicsEngine.h
  3. 23 0
      panda/src/display/graphicsStateGuardian.I
  4. 20 0
      panda/src/display/graphicsStateGuardian.cxx
  5. 9 0
      panda/src/display/graphicsStateGuardian.h
  6. 15 1
      panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx
  7. 15 1
      panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx
  8. 20 0
      panda/src/egg/eggTexture.I
  9. 72 1
      panda/src/egg/eggTexture.cxx
  10. 10 0
      panda/src/egg/eggTexture.h
  11. 8 0
      panda/src/egg/parser.yxx
  12. 63 15
      panda/src/egg2pg/eggLoader.cxx
  13. 1 0
      panda/src/egg2pg/eggLoader.h
  14. 15 9
      panda/src/framework/windowFramework.cxx
  15. 719 92
      panda/src/glstuff/glGraphicsStateGuardian_src.cxx
  16. 17 7
      panda/src/glstuff/glGraphicsStateGuardian_src.h
  17. 1 0
      panda/src/gobj/Sources.pp
  18. 7 51
      panda/src/gobj/config_gobj.cxx
  19. 1 11
      panda/src/gobj/config_gobj.h
  20. 102 13
      panda/src/gobj/texture.I
  21. 601 41
      panda/src/gobj/texture.cxx
  22. 49 2
      panda/src/gobj/texture.h
  23. 4 1
      panda/src/gobj/videoTexture.cxx
  24. 2 2
      panda/src/grutil/openCVTexture.cxx
  25. 24 0
      panda/src/pgraph/bamFile.cxx
  26. 3 0
      panda/src/pgraph/bamFile.h
  27. 7 2
      panda/src/putil/Sources.pp
  28. 2 1
      panda/src/putil/bam.h
  29. 66 0
      panda/src/putil/bamTextureMode.cxx
  30. 36 0
      panda/src/putil/bamTextureMode.h
  31. 26 0
      panda/src/putil/bamWriter.I
  32. 1 0
      panda/src/putil/bamWriter.cxx
  33. 5 0
      panda/src/putil/bamWriter.h
  34. 5 0
      panda/src/putil/config_util.cxx
  35. 3 1
      panda/src/putil/config_util.h
  36. 2 2
      panda/src/putil/load_prc_file.h
  37. 1 0
      panda/src/putil/putil_composite1.cxx
  38. 4 0
      panda/src/text/dynamicTextPage.cxx
  39. 15 0
      pandatool/src/bam/bamInfo.cxx
  40. 1 0
      pandatool/src/bam/bamInfo.h
  41. 242 2
      pandatool/src/bam/eggToBam.cxx
  42. 29 0
      pandatool/src/bam/eggToBam.h

+ 57 - 0
panda/src/display/graphicsEngine.cxx

@@ -744,6 +744,63 @@ flip_frame() {
     do_flip_frame();
   }
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsEngine::extract_texture_data
+//       Access: Published
+//  Description: Asks the indicated GraphicsStateGuardian to retrieve
+//               the texture memory image of the indicated texture and
+//               store it in the texture's ram_image field.  The image
+//               can then be written to disk via Texture::write(), or
+//               otherwise manipulated on the CPU.
+//
+//               This is useful for retrieving the contents of a
+//               texture that has been somehow generated on the
+//               graphics card, instead of having been loaded the
+//               normal way via Texture::read() or Texture::load().
+//               It is particularly useful for getting the data
+//               associated with a compressed texture image.
+//
+//               Since this requires a round-trip to the draw thread,
+//               it may require waiting for the current thread to
+//               finish rendering if it is called in a multithreaded
+//               environment.  However, you can call this several
+//               consecutive times on different textures for little
+//               additional cost.
+//
+//               If the texture has not yet been loaded to the GSG in
+//               question, it will be loaded immediately.
+//
+//               The return value is true if the operation is
+//               successful, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool GraphicsEngine::
+extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg) {
+  MutexHolder holder(_lock);
+
+  string draw_name = gsg->get_threading_model().get_draw_name();
+  if (draw_name.empty()) {
+    // A single-threaded environment.  No problem.
+    return gsg->extract_texture_data(tex);
+
+  } else {
+    // A multi-threaded environment.  We have to wait until the draw
+    // thread has finished its current task.
+    WindowRenderer *wr = get_window_renderer(draw_name, 0);
+    RenderThread *thread = (RenderThread *)wr;
+    MutexHolder holder2(thread->_cv_mutex);
+      
+    while (thread->_thread_state != TS_wait) {
+      thread->_cv_done.wait();
+    }
+
+    // OK, now the draw thread is idle.  That's really good enough for
+    // our purposes; we don't *actually* need to make the draw thread
+    // do the work--it's sufficient that it's not doing anything else
+    // while we access the GSG.
+    return gsg->extract_texture_data(tex);
+  }
+}
  
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsEngine::add_callback

+ 2 - 0
panda/src/display/graphicsEngine.h

@@ -97,6 +97,8 @@ PUBLISHED:
   void sync_frame();
   void flip_frame();
 
+  bool extract_texture_data(Texture *tex, GraphicsStateGuardian *gsg);
+
 public:
   enum ThreadState {
     TS_wait,

+ 23 - 0
panda/src/display/graphicsStateGuardian.I

@@ -356,6 +356,29 @@ get_supports_tex_non_pow2() const {
   return _supports_tex_non_pow2;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_compressed_texture
+//       Access: Published
+//  Description: Returns true if this GSG can compress textures as it
+//               loads them into texture memory, and/or accept
+//               pre-compressed textures for storing.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_supports_compressed_texture() const {
+  return _supports_compressed_texture;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::get_supports_compressed_texture_format
+//       Access: Published
+//  Description: Returns true if this GSG can accept textures
+//               pre-compressed in the indicated format.
+////////////////////////////////////////////////////////////////////
+INLINE bool GraphicsStateGuardian::
+get_supports_compressed_texture_format(Texture::CompressionMode compression) const {
+  return _compressed_texture_formats.get_bit(compression);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::get_max_lights
 //       Access: Published

+ 20 - 0
panda/src/display/graphicsStateGuardian.cxx

@@ -136,6 +136,9 @@ GraphicsStateGuardian(const FrameBufferProperties &properties,
   _supports_3d_texture = false;
   _supports_cube_map = false;
   _supports_tex_non_pow2 = false;
+  _supports_compressed_texture = false;
+  _compressed_texture_formats.clear();
+  _compressed_texture_formats.set_bit(Texture::CM_off);
   
   // Assume no limits on number of lights or clip planes.
   _max_lights = -1;
@@ -415,6 +418,23 @@ void GraphicsStateGuardian::
 release_texture(TextureContext *) {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: GraphicsStateGuardian::extract_texture_data
+//       Access: Public, Virtual
+//  Description: This method should only be called by the
+//               GraphicsEngine.  Do not call it directly; call
+//               GraphicsEngine::extract_texture_data() instead.
+//
+//               This method will be called in the draw thread to
+//               download the texture memory's image into its
+//               ram_image value.  It returns true on success, false
+//               otherwise.
+////////////////////////////////////////////////////////////////////
+bool GraphicsStateGuardian::
+extract_texture_data(Texture *) {
+  return false;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: GraphicsStateGuardian::prepare_geom
 //       Access: Public, Virtual

+ 9 - 0
panda/src/display/graphicsStateGuardian.h

@@ -43,6 +43,8 @@
 #include "pvector.h"
 #include "attribSlots.h"
 #include "shaderContext.h"
+#include "bitMask.h"
+#include "texture.h"
 
 class DrawableRegion;
 class GraphicsEngine;
@@ -99,6 +101,9 @@ PUBLISHED:
   INLINE bool get_supports_cube_map() const;
   INLINE bool get_supports_tex_non_pow2() const;
 
+  INLINE bool get_supports_compressed_texture() const;
+  INLINE bool get_supports_compressed_texture_format(Texture::CompressionMode compression) const;
+
   INLINE int get_max_lights() const;
   INLINE int get_max_clip_planes() const;
 
@@ -132,6 +137,7 @@ public:
 
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual void release_texture(TextureContext *tc);
+  virtual bool extract_texture_data(Texture *tex);
 
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual void release_geom(GeomContext *gc);
@@ -347,6 +353,9 @@ protected:
   bool _supports_cube_map;
   bool _supports_tex_non_pow2;
 
+  bool _supports_compressed_texture;
+  BitMask32 _compressed_texture_formats;
+  
   int _max_lights;
   int _max_clip_planes;
 

+ 15 - 1
panda/src/dxgsg8/dxGraphicsStateGuardian8.cxx

@@ -148,6 +148,13 @@ DXGraphicsStateGuardian8::
 TextureContext *DXGraphicsStateGuardian8::
 prepare_texture(Texture *tex) {
   DXTextureContext8 *dtc = new DXTextureContext8(tex);
+
+  if (!get_supports_compressed_texture_format(tex->get_ram_image_compression())) {
+    dxgsg8_cat.error()
+      << *dtc->_texture << " is stored in an unsupported compressed format.\n";
+    return NULL;
+  }
+  
   if (!dtc->create_texture(*_screen)) {
     delete dtc;
     return NULL;
@@ -189,7 +196,14 @@ apply_texture(int i, TextureContext *tc) {
     // error or oversight.
     if ((dirty & Texture::DF_image) == 0) {
       dxgsg8_cat.warning()
-        << "Texture " << *dtc->_texture << " has changed mipmap state.\n";
+        << *dtc->_texture << " has changed mipmap state.\n";
+    }
+
+    if (!get_supports_compressed_texture_format(tc->_texture->get_ram_image_compression())) {
+      dxgsg8_cat.error()
+        << *dtc->_texture << " is stored in an unsupported compressed format.\n";
+      _d3d_device->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
+      return;
     }
 
     if (!dtc->create_texture(*_screen)) {

+ 15 - 1
panda/src/dxgsg9/dxGraphicsStateGuardian9.cxx

@@ -191,6 +191,13 @@ DXGraphicsStateGuardian9::
 TextureContext *DXGraphicsStateGuardian9::
 prepare_texture(Texture *tex) {
   DXTextureContext9 *dtc = new DXTextureContext9(tex);
+
+  if (!get_supports_compressed_texture_format(tex->get_ram_image_compression())) {
+    dxgsg9_cat.error()
+      << *dtc->_texture << " is stored in an unsupported compressed format.\n";
+    return NULL;
+  }
+  
   if (!dtc->create_texture(*_screen)) {
     delete dtc;
     return NULL;
@@ -237,7 +244,14 @@ apply_texture(int i, TextureContext *tc) {
     // error or oversight.
     if ((dirty & Texture::DF_image) == 0) {
       dxgsg9_cat.warning()
-        << "Texture " << *dtc->_texture << " has changed mipmap state.\n";
+        << *dtc->_texture << " has changed mipmap state.\n";
+    }
+
+    if (!get_supports_compressed_texture_format(tc->_texture->get_ram_image_compression())) {
+      dxgsg9_cat.error()
+        << *dtc->_texture << " is stored in an unsupported compressed format.\n";
+      _d3d_device->SetTextureStageState(i, D3DTSS_COLOROP, D3DTOP_DISABLE);
+      return;
     }
 
     if (!dtc->create_texture(*_screen)) {

+ 20 - 0
panda/src/egg/eggTexture.I

@@ -63,6 +63,26 @@ get_format() const {
   return _format;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::set_compression_mode
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE void EggTexture::
+set_compression_mode(CompressionMode mode) {
+  _compression_mode = mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::get_compression_mode
+//       Access: Published
+//  Description:
+////////////////////////////////////////////////////////////////////
+INLINE EggTexture::CompressionMode EggTexture::
+get_compression_mode() const {
+  return _compression_mode;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggTexture::set_wrap_mode
 //       Access: Published

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

@@ -37,6 +37,7 @@ EggTexture(const string &tref_name, const string &filename)
 {
   _texture_type = TT_unspecified;
   _format = F_unspecified;
+  _compression_mode = CM_default;
   _wrap_mode = WM_unspecified;
   _wrap_u = WM_unspecified;
   _wrap_v = WM_unspecified;
@@ -80,6 +81,7 @@ operator = (const EggTexture &copy) {
 
   _texture_type = copy._texture_type;
   _format = copy._format;
+  _compression_mode = copy._compression_mode;
   _wrap_mode = copy._wrap_mode;
   _wrap_u = copy._wrap_u;
   _wrap_v = copy._wrap_v;
@@ -152,6 +154,11 @@ write(ostream &out, int indent_level) const {
       << "<Scalar> format { " << get_format() << " }\n";
   }
 
+  if (get_compression_mode() != CM_default) {
+    indent(out, indent_level + 2)
+      << "<Scalar> compression { " << get_compression_mode() << " }\n";
+  }
+
   if (get_wrap_mode() != WM_unspecified) {
     indent(out, indent_level + 2)
       << "<Scalar> wrap { " << get_wrap_mode() << " }\n";
@@ -360,6 +367,7 @@ is_equivalent_to(const EggTexture &other, int eq) const {
     //cout << "compared by attributes" << endl;
     if (_texture_type != other._texture_type ||
         _format != other._format ||
+        _compression_mode != other._compression_mode ||
         _wrap_mode != other._wrap_mode ||
         _wrap_u != other._wrap_u ||
         _wrap_v != other._wrap_v ||
@@ -442,6 +450,9 @@ sorts_less_than(const EggTexture &other, int eq) const {
     if (_format != other._format) {
       return (int)_format < (int)other._format;
     }
+    if (_compression_mode != other._compression_mode) {
+      return (int)_compression_mode < (int)other._compression_mode;
+    }
     if (_wrap_mode != other._wrap_mode) {
       return (int)_wrap_mode < (int)other._wrap_mode;
     }
@@ -617,7 +628,7 @@ string_texture_type(const string &string) {
   } 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;
+    return TT_3d_texture;
 
   } else if (cmp_nocase_uh(string, "cube") == 0 ||
              cmp_nocase_uh(string, "cubemap") == 0 ||
@@ -680,6 +691,36 @@ string_format(const string &string) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggTexture::string_compression_mode
+//       Access: Published, Static
+//  Description: Returns the CompressionMode value associated with the given
+//               string representation, or CM_default if the string
+//               does not match any known CompressionMode value.
+////////////////////////////////////////////////////////////////////
+EggTexture::CompressionMode EggTexture::
+string_compression_mode(const string &string) {
+  if (cmp_nocase_uh(string, "off") == 0) {
+    return CM_off;
+  } else if (cmp_nocase_uh(string, "on") == 0) {
+    return CM_on;
+  } else if (cmp_nocase_uh(string, "fxt1") == 0) {
+    return CM_fxt1;
+  } else if (cmp_nocase_uh(string, "dxt1") == 0) {
+    return CM_dxt1;
+  } else if (cmp_nocase_uh(string, "dxt2") == 0) {
+    return CM_dxt2;
+  } else if (cmp_nocase_uh(string, "dxt3") == 0) {
+    return CM_dxt3;
+  } else if (cmp_nocase_uh(string, "dxt4") == 0) {
+    return CM_dxt4;
+  } else if (cmp_nocase_uh(string, "dxt5") == 0) {
+    return CM_dxt5;
+  } else {
+    return CM_default;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggTexture::string_wrap_mode
 //       Access: Published, Static
@@ -1067,6 +1108,36 @@ ostream &operator << (ostream &out, EggTexture::Format format) {
   return out << "(**invalid**)";
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: CompressionMode output operator
+//  Description:
+////////////////////////////////////////////////////////////////////
+ostream &operator << (ostream &out, EggTexture::CompressionMode mode) {
+  switch (mode) {
+  case EggTexture::CM_default:
+    return out << "default";
+  case EggTexture::CM_off:
+    return out << "off";
+  case EggTexture::CM_on:
+    return out << "on";
+  case EggTexture::CM_fxt1:
+    return out << "fxt1";
+  case EggTexture::CM_dxt1:
+    return out << "dxt1";
+  case EggTexture::CM_dxt2:
+    return out << "dxt2";
+  case EggTexture::CM_dxt3:
+    return out << "dxt3";
+  case EggTexture::CM_dxt4:
+    return out << "dxt4";
+  case EggTexture::CM_dxt5:
+    return out << "dxt5";
+  }
+
+  nassertr(false, out);
+  return out << "(**invalid**)";
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: WrapMode output operator
 //  Description:

+ 10 - 0
panda/src/egg/eggTexture.h

@@ -69,6 +69,10 @@ PUBLISHED:
     F_red, F_green, F_blue, F_alpha, F_luminance,
     F_luminance_alpha, F_luminance_alphamask
   };
+  enum CompressionMode {
+    CM_default, CM_off, CM_on,
+    CM_fxt1, CM_dxt1, CM_dxt2, CM_dxt3, CM_dxt4, CM_dxt5,
+  };
   enum WrapMode {
     WM_unspecified, WM_clamp, WM_repeat,
     WM_mirror, WM_mirror_once, WM_border_color
@@ -157,6 +161,9 @@ PUBLISHED:
   INLINE void set_format(Format format);
   INLINE Format get_format() const;
 
+  INLINE void set_compression_mode(CompressionMode mode);
+  INLINE CompressionMode get_compression_mode() const;
+
   INLINE void set_wrap_mode(WrapMode mode);
   INLINE WrapMode get_wrap_mode() const;
 
@@ -253,6 +260,7 @@ PUBLISHED:
 
   static TextureType string_texture_type(const string &string);
   static Format string_format(const string &string);
+  static CompressionMode string_compression_mode(const string &string);
   static WrapMode string_wrap_mode(const string &string);
   static FilterType string_filter_type(const string &string);
   static EnvType string_env_type(const string &string);
@@ -286,6 +294,7 @@ private:
 
   TextureType _texture_type;
   Format _format;
+  CompressionMode _compression_mode;
   WrapMode _wrap_mode, _wrap_u, _wrap_v, _wrap_w;
   FilterType _minfilter, _magfilter;
   int _anisotropic_degree;
@@ -367,6 +376,7 @@ INLINE ostream &operator << (ostream &out, const EggTexture &n) {
 
 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::CompressionMode mode);
 EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::WrapMode mode);
 EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::FilterType type);
 EXPCL_PANDAEGG ostream &operator << (ostream &out, EggTexture::EnvType type);

+ 8 - 0
panda/src/egg/parser.yxx

@@ -360,6 +360,14 @@ texture_body:
       texture->set_format(f);
     }
 
+  } else if (cmp_nocase_uh(name, "compression") == 0) {
+    EggTexture::CompressionMode w = EggTexture::string_compression_mode(strval);
+    if (w == EggTexture::CM_default) {
+      eggyywarning("Unknown texture compression mode " + strval);
+    } else {
+      texture->set_compression_mode(w);
+    }
+
   } else if (cmp_nocase_uh(name, "wrap") == 0) {
     EggTexture::WrapMode w = EggTexture::string_wrap_mode(strval);
     if (w == EggTexture::WM_unspecified) {

+ 63 - 15
panda/src/egg2pg/eggLoader.cxx

@@ -933,9 +933,23 @@ load_texture(TextureDef &def, const EggTexture *egg_tex) {
 ////////////////////////////////////////////////////////////////////
 void EggLoader::
 apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
-  tex->set_wrap_u(convert_wrap_mode(egg_tex->determine_wrap_u()));
-  tex->set_wrap_v(convert_wrap_mode(egg_tex->determine_wrap_v()));
-  tex->set_wrap_w(convert_wrap_mode(egg_tex->determine_wrap_w()));
+  if (egg_tex->get_compression_mode() != EggTexture::CM_default) {
+    tex->set_compression(convert_compression_mode(egg_tex->get_compression_mode()));
+  }
+
+  EggTexture::WrapMode wrap_u = egg_tex->determine_wrap_u();
+  EggTexture::WrapMode wrap_v = egg_tex->determine_wrap_v();
+  EggTexture::WrapMode wrap_w = egg_tex->determine_wrap_w();
+
+  if (wrap_u != EggTexture::WM_unspecified) {
+    tex->set_wrap_u(convert_wrap_mode(wrap_u));
+  }
+  if (wrap_v != EggTexture::WM_unspecified) {
+    tex->set_wrap_v(convert_wrap_mode(wrap_v));
+  }
+  if (wrap_w != EggTexture::WM_unspecified) {
+    tex->set_wrap_w(convert_wrap_mode(wrap_w));
+  }
 
   if (egg_tex->has_border_color()) {
     tex->set_border_color(egg_tex->get_border_color());
@@ -1013,12 +1027,7 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
     break;
 
   case EggTexture::FT_unspecified:
-    // Default is bilinear, unless egg_ignore_filters is specified.
-    if (egg_ignore_filters) {
-      tex->set_minfilter(Texture::FT_nearest);
-    } else {
-      tex->set_minfilter(Texture::FT_linear);
-    }
+    break;
   }
 
   switch (egg_tex->get_magfilter()) {
@@ -1041,12 +1050,7 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
     break;
 
   case EggTexture::FT_unspecified:
-    // Default is bilinear, unless egg_ignore_filters is specified.
-    if (egg_ignore_filters) {
-      tex->set_magfilter(Texture::FT_nearest);
-    } else {
-      tex->set_magfilter(Texture::FT_linear);
-    }
+    break;
   }
 
   if (egg_tex->has_anisotropic_degree()) {
@@ -1176,6 +1180,50 @@ apply_texture_attributes(Texture *tex, const EggTexture *egg_tex) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggLoader::convert_compression_mode
+//       Access: Private
+//  Description: Returns the Texture::CompressionMode enum
+//               corresponding to the EggTexture::CompressionMode.
+//               Returns CM_default if the compression mode is
+//               unspecified.
+////////////////////////////////////////////////////////////////////
+Texture::CompressionMode EggLoader::
+convert_compression_mode(EggTexture::CompressionMode compression_mode) const {
+  switch (compression_mode) {
+  case EggTexture::CM_off:
+    return Texture::CM_off;
+
+  case EggTexture::CM_on:
+    return Texture::CM_on;
+
+  case EggTexture::CM_fxt1:
+    return Texture::CM_fxt1;
+
+  case EggTexture::CM_dxt1:
+    return Texture::CM_dxt1;
+
+  case EggTexture::CM_dxt2:
+    return Texture::CM_dxt2;
+
+  case EggTexture::CM_dxt3:
+    return Texture::CM_dxt3;
+
+  case EggTexture::CM_dxt4:
+    return Texture::CM_dxt4;
+
+  case EggTexture::CM_dxt5:
+    return Texture::CM_dxt5;
+
+  case EggTexture::CM_default:
+    return Texture::CM_default;
+  }
+
+  egg2pg_cat.warning()
+    << "Unexpected texture compression flag: " << (int)compression_mode << "\n";
+  return Texture::CM_default;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: EggLoader::convert_wrap_mode
 //       Access: Private

+ 1 - 0
panda/src/egg2pg/eggLoader.h

@@ -122,6 +122,7 @@ private:
   void load_textures();
   bool load_texture(TextureDef &def, const EggTexture *egg_tex);
   void apply_texture_attributes(Texture *tex, const EggTexture *egg_tex);
+  Texture::CompressionMode convert_compression_mode(EggTexture::CompressionMode compression_mode) const;
   Texture::WrapMode convert_wrap_mode(EggTexture::WrapMode wrap_mode) const;
   PT(TextureStage) make_texture_stage(const EggTexture *egg_tex);
 

+ 15 - 9
panda/src/framework/windowFramework.cxx

@@ -593,12 +593,19 @@ load_model(const NodePath &parent, Filename filename) {
     LoaderFileType *model_type =
       reg->get_type_from_extension(extension);
     if (model_type == (LoaderFileType *)NULL) {
-      // The extension isn't a known model file type, is it a known
-      // texture extension?
-      TexturePool *texture_pool = TexturePool::get_global_ptr();
-      if (texture_pool->get_texture_type(extension) != NULL) {
-        // It is a known texture extension.
-        is_image = true;
+      // The extension isn't a known model file type; is it a known
+      // image file extension?
+      if (extension == "txo") {
+	// A texture object.  Not exactly an image, but certainly a
+	// texture.
+	is_image = true;
+	
+      } else {
+	TexturePool *texture_pool = TexturePool::get_global_ptr();
+	if (texture_pool->get_texture_type(extension) != NULL) {
+	  // It is a known image file extension.
+	  is_image = true;
+	}
       }
     }
   }
@@ -1093,7 +1100,7 @@ load_image_as_model(const Filename &filename) {
 
   int x_size = tex->get_x_size();
   int y_size = tex->get_y_size();
-  bool has_alpha = false;
+  bool has_alpha = true;
   LVecBase2f tex_scale(1.0f, 1.0f);
 
   if (tex->is_of_type(VideoTexture::get_class_type())) {
@@ -1103,7 +1110,7 @@ load_image_as_model(const Filename &filename) {
     y_size = vtex->get_video_height();
     tex_scale = vtex->get_tex_scale();
 
-  } else {
+  } else if (!tex->get_loaded_from_txo()) {
     // Get the size from the original image (the texture may have
     // scaled it to make a power of 2).
     PNMImageHeader header;
@@ -1127,7 +1134,6 @@ load_image_as_model(const Filename &filename) {
   float top = y_size / 2.0;
 
   PT(GeomNode) card_node = new GeomNode("card");
-  card_node->set_attrib(ColorAttrib::make_flat(Colorf(1.0f, 1.0f, 1.0f, 1.0f)));
   card_node->set_attrib(TextureAttrib::make(tex));
   if (has_alpha) {
     card_node->set_attrib(TransparencyAttrib::make(TransparencyAttrib::M_alpha));

File diff suppressed because it is too large
+ 719 - 92
panda/src/glstuff/glGraphicsStateGuardian_src.cxx


+ 17 - 7
panda/src/glstuff/glGraphicsStateGuardian_src.h

@@ -65,6 +65,9 @@ typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum target, GLintptr offset,
 typedef void (APIENTRYP PFNGLDRAWBUFFERSARBPROC) (GLsizei n, const GLenum *bufs);
 typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage);
 typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei n, const GLuint *buffers);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXIMAGE2DPROC) (GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLCOMPRESSEDTEXSUBIMAGE3DPROC) (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLenum format, GLsizei imageSize, const GLvoid *data);
+typedef void (APIENTRYP PFNGLGETCOMPRESSEDTEXIMAGEPROC) (GLenum target, GLint level, GLvoid *img);
 
 class CLP(GeomContext);
 
@@ -106,6 +109,7 @@ public:
 
   virtual TextureContext *prepare_texture(Texture *tex);
   virtual void release_texture(TextureContext *tc);
+  virtual bool extract_texture_data(Texture *tex);
 
   virtual GeomContext *prepare_geom(Geom *geom);
   virtual void release_geom(GeomContext *gc);
@@ -236,14 +240,14 @@ protected:
 
   static GLenum get_numeric_type(Geom::NumericType numeric_type);
   GLenum get_texture_target(Texture::TextureType texture_type) const;
-  GLenum get_texture_wrap_mode(Texture::WrapMode wm);
+  GLenum get_texture_wrap_mode(Texture::WrapMode wm) const;
+  static Texture::WrapMode get_panda_wrap_mode(GLenum wm);
   static GLenum get_texture_filter_type(Texture::FilterType ft, bool ignore_mipmaps);
+  static Texture::FilterType get_panda_filter_type(GLenum ft);
   static GLenum get_component_type(Texture::ComponentType component_type);
-  GLint get_external_image_format(Texture::Format format) const;
-  static GLint get_internal_image_format(Texture::Format format);
-  static int get_external_texture_bytes(int width, int height, int depth,
-                                        GLint external_format, 
-                                        GLenum component_type);
+  GLint get_external_image_format(Texture *tex) const;
+  GLint get_internal_image_format(Texture *tex) const;
+  static bool is_compressed_format(GLenum format);
   static GLint get_texture_apply_mode_type(TextureStage::Mode am);
   static GLint get_texture_combine_type(TextureStage::CombineMode cm);
   GLint get_texture_src_type(TextureStage::CombineSource cs,
@@ -268,7 +272,9 @@ protected:
                             GLenum target, GLint internal_format, 
                             int width, int height, int depth,
                             GLint external_format, GLenum component_type, 
-                            const unsigned char *image);
+                            const unsigned char *image,
+			    size_t image_size,
+			    Texture::CompressionMode image_compression);
 
   void do_point_size();
 
@@ -361,6 +367,10 @@ public:
   PFNGLTEXIMAGE3DPROC _glTexImage3D;
   PFNGLTEXSUBIMAGE3DPROC _glTexSubImage3D;
 
+  PFNGLCOMPRESSEDTEXIMAGE2DPROC _glCompressedTexImage2D;
+  PFNGLCOMPRESSEDTEXSUBIMAGE2DPROC _glCompressedTexSubImage2D;
+  PFNGLGETCOMPRESSEDTEXIMAGEPROC _glGetCompressedTexImage;
+
   bool _supports_bgr;
   bool _supports_rescale_normal;
 

+ 1 - 0
panda/src/gobj/Sources.pp

@@ -1,6 +1,7 @@
 #define OTHER_LIBS interrogatedb:c dconfig:c dtoolconfig:m \
                    dtoolutil:c dtoolbase:c dtool:m prc:c
 #define OSX_SYS_LIBS mx
+#define USE_PACKAGES zlib
 
 #begin lib_target
   #define TARGET gobj

+ 7 - 51
panda/src/gobj/config_gobj.cxx

@@ -79,6 +79,13 @@ ConfigVariableBool keep_texture_ram
           "texture image from disk; but it will consume memory somewhat "
           "wastefully."));
 
+ConfigVariableBool compressed_textures
+("compressed-textures", false,
+ PRC_DESC("Set this to true to compress textures as they are loaded into "
+	  "texture memory, if the driver supports this.  Specifically, this "
+	  "changes the meaning of set_compression(Texture::CM_default) to "
+	  "Texture::CM_on."));
+
 ConfigVariableBool vertex_buffers
 ("vertex-buffers", true,
  PRC_DESC("Set this true to allow the use of vertex buffers (or buffer "
@@ -167,11 +174,6 @@ ConfigVariableBool connect_triangle_strips
           "triangle strip may help performance by reducing the number "
           "of separate graphics calls that have to be made."));
 
-ConfigVariableEnum<BamTextureMode> bam_texture_mode
-("bam-texture-mode", BTM_relative,
- PRC_DESC("Set this to specify how textures should be written into Bam files."
-          "See the panda source or documentation for available options."));
-
 ConfigVariableEnum<AutoTextureScale> textures_power_2
 ("textures-power-2", ATS_down,
  PRC_DESC("Specify whether textures should automatically be constrained to "
@@ -300,52 +302,6 @@ ConfigureFn(config_gobj) {
   InternalName::register_with_read_factory();
 }
 
-ostream &
-operator << (ostream &out, BamTextureMode btm) {
-  switch (btm) {
-  case BTM_unchanged:
-    return out << "unchanged";
-   
-  case BTM_fullpath:
-    return out << "fullpath";
-    
-  case BTM_relative:
-    return out << "relative";
-    
-  case BTM_basename:
-    return out << "basename";
-
-  case BTM_rawdata:
-    return out << "rawdata";
-  }
-
-  return out << "**invalid BamTextureMode (" << (int)btm << ")**";
-}
-
-istream &
-operator >> (istream &in, BamTextureMode &btm) {
-  string word;
-  in >> word;
-
-  if (cmp_nocase(word, "unchanged") == 0) {
-    btm = BTM_unchanged;
-  } else if (cmp_nocase(word, "fullpath") == 0) {
-    btm = BTM_fullpath;
-  } else if (cmp_nocase(word, "relative") == 0) {
-    btm = BTM_relative;
-  } else if (cmp_nocase(word, "basename") == 0) {
-    btm = BTM_basename;
-  } else if (cmp_nocase(word, "rawdata") == 0) {
-    btm = BTM_rawdata;
-
-  } else {
-    gobj_cat->error() << "Invalid BamTextureMode value: " << word << "\n";
-    btm = BTM_relative;
-  }
-
-  return in;
-}
-
 ostream &
 operator << (ostream &out, AutoTextureScale ats) {
   switch (ats) {

+ 1 - 11
panda/src/gobj/config_gobj.h

@@ -28,16 +28,6 @@
 
 NotifyCategoryDecl(gobj, EXPCL_PANDA, EXPTP_PANDA);
 
-enum BamTextureMode {
-  BTM_unchanged,
-  BTM_fullpath,
-  BTM_relative,
-  BTM_basename,
-  BTM_rawdata
-};
-EXPCL_PANDA ostream &operator << (ostream &out, BamTextureMode btm);
-EXPCL_PANDA istream &operator >> (istream &in, BamTextureMode &btm);
-
 enum AutoTextureScale {
   ATS_none,
   ATS_down,
@@ -49,6 +39,7 @@ EXPCL_PANDA istream &operator >> (istream &in, AutoTextureScale &ats);
 // Configure variables for gobj package.
 extern EXPCL_PANDA ConfigVariableInt max_texture_dimension;
 extern EXPCL_PANDA ConfigVariableBool keep_texture_ram;
+extern EXPCL_PANDA ConfigVariableBool compressed_textures;
 extern EXPCL_PANDA ConfigVariableBool vertex_buffers;
 extern EXPCL_PANDA ConfigVariableBool vertex_arrays;
 extern EXPCL_PANDA ConfigVariableBool display_lists;
@@ -58,7 +49,6 @@ extern EXPCL_PANDA ConfigVariableBool matrix_palette;
 extern EXPCL_PANDA ConfigVariableBool display_list_animation;
 extern EXPCL_PANDA ConfigVariableBool connect_triangle_strips;
 
-extern EXPCL_PANDA ConfigVariableEnum<BamTextureMode> bam_texture_mode;
 extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> textures_power_2;
 extern EXPCL_PANDA ConfigVariableEnum<AutoTextureScale> textures_square;
 extern EXPCL_PANDA ConfigVariableBool textures_header_only;

+ 102 - 13
panda/src/gobj/texture.I

@@ -398,13 +398,29 @@ get_border_color() const {
   return _border_color;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_compression
+//       Access: Published
+//  Description: Returns the compression mode requested for this
+//               particular texture, or CM_off if the texture is not
+//               to be compressed.
+//
+//               If a value other than CM_off is returned, this is
+//               not a guarantee that the texture is actually
+//               successfully compressed on the GSG.  It may be that
+//               the GSG does not support the requested compression
+//               mode, in which case the texture may actually be
+//               stored uncompressed in texture memory.
+////////////////////////////////////////////////////////////////////
+INLINE Texture::CompressionMode Texture::
+get_compression() const {
+  return _compression;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_render_to_texture
 //       Access: Published
-//  Description: Returns the solid color of the texture's border.
-//               Some OpenGL implementations use a border for tiling
-//               textures; in Panda, it is only used for specifying
-//               the clamp color.
+//  Description: 
 ////////////////////////////////////////////////////////////////////
 INLINE bool Texture::
 get_render_to_texture() const {
@@ -446,7 +462,24 @@ might_have_ram_image() const {
 ////////////////////////////////////////////////////////////////////
 INLINE size_t Texture::
 get_ram_image_size() const {
-  return _image.size();
+  return _ram_image.size();
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_ram_page_size
+//       Access: Published
+//  Description: Returns the number of bytes used by the in-memory
+//               image per page, or 0 if there is no in-memory image.
+//
+//               For a non-compressed texture, this is the same as
+//               get_expected_ram_page_size().  For a compressed
+//               texture, this may be a smaller value.  (We do assume
+//               that all pages will be the same size on a compressed
+//               texture).
+////////////////////////////////////////////////////////////////////
+INLINE size_t Texture::
+get_ram_page_size() const {
+  return _ram_image_compression != CM_off ? _ram_page_size : get_expected_ram_page_size();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -474,6 +507,21 @@ get_expected_ram_page_size() const {
   return (size_t)(_x_size * _y_size * _num_components * _component_width);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_ram_image_compression
+//       Access: Published
+//  Description: Returns the compression mode in which the ram image
+//               is already stored pre-compressed.  If this is other
+//               than CM_off, you cannot rely on the contents of the
+//               ram image to be anything predicatable (it will not be
+//               an array of x by y pixels, and it probably won't have
+//               the same length as get_expected_ram_image_size()).
+////////////////////////////////////////////////////////////////////
+INLINE Texture::CompressionMode Texture::
+get_ram_image_compression() const {
+  return _ram_image_compression;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_keep_ram_image
 //       Access: Published
@@ -678,6 +726,30 @@ get_loaded_from_disk() const {
   return _loaded_from_disk;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::set_loaded_from_txo
+//       Access: Published
+//  Description: Sets the flag that indicates the texture has been
+//               loaded from a txo file.  You probably shouldn't be
+//               setting this directly; it is set automatically when a
+//               Texture is loaded.
+////////////////////////////////////////////////////////////////////
+INLINE void Texture::
+set_loaded_from_txo() {
+  _loaded_from_txo = true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_loaded_from_txo
+//       Access: Published
+//  Description: Returns the flag that indicates the texture has been
+//               loaded from a txo file.
+////////////////////////////////////////////////////////////////////
+INLINE bool Texture::
+get_loaded_from_txo() const {
+  return _loaded_from_txo;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_match_framebuffer_format
 //       Access: Public
@@ -725,7 +797,7 @@ set_match_framebuffer_format(bool flag) {
 ////////////////////////////////////////////////////////////////////
 INLINE void Texture::
 store_unscaled_byte(int &index, int value) {
-  _image[index++] = (uchar)value;
+  _ram_image[index++] = (uchar)value;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -744,8 +816,8 @@ store_unscaled_short(int &index, int value) {
     uchar uc[2];
   } v;
   v.us = (ushort)value;
-  _image[index++] = v.uc[0];
-  _image[index++] = v.uc[1];
+  _ram_image[index++] = v.uc[0];
+  _ram_image[index++] = v.uc[1];
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -786,8 +858,8 @@ store_scaled_short(int &index, int value, double scale) {
 ////////////////////////////////////////////////////////////////////
 INLINE double Texture::
 get_unsigned_byte(int &index) const {
-  nassertr(index >= 0 && index < (int)_image.size(), 0.0);
-  return (double)_image[index++] / 255.0;
+  nassertr(index >= 0 && index < (int)_ram_image.size(), 0.0);
+  return (double)_ram_image[index++] / 255.0;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -800,12 +872,29 @@ get_unsigned_byte(int &index) const {
 ////////////////////////////////////////////////////////////////////
 INLINE double Texture::
 get_unsigned_short(int &index) const {
-  nassertr(index >= 0 && index+1 < (int)_image.size(), 0.0);
+  nassertr(index >= 0 && index+1 < (int)_ram_image.size(), 0.0);
   union {
     ushort us;
     uchar uc[2];
   } v;
-  v.uc[0] = _image[index++];
-  v.uc[1] = _image[index++];
+  v.uc[0] = _ram_image[index++];
+  v.uc[1] = _ram_image[index++];
   return (double)v.us / 65535.0;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::is_txo_filename
+//       Access: Private, Static
+//  Description: Returns true if the indicated filename ends in .txo
+//               or .txo.pz, false otherwise.
+////////////////////////////////////////////////////////////////////
+INLINE bool Texture::
+is_txo_filename(const Filename &fullpath) {
+  string extension = fullpath.get_extension();
+#ifdef HAVE_ZLIB
+  if (extension == "pz") {
+    extension = Filename(fullpath.get_basename_wo_extension()).get_extension();
+  }
+#endif  // HAVE_ZLIB
+  return (extension == "txo");
+}

+ 601 - 41
panda/src/gobj/texture.cxx

@@ -29,6 +29,11 @@
 #include "preparedGraphicsObjects.h"
 #include "pnmImage.h"
 #include "virtualFileSystem.h"
+#include "datagramInputFile.h"
+#include "datagramOutputFile.h"
+#include "bam.h"
+#include "zStream.h"
+#include "indent.h"
 
 #include <stddef.h>
 
@@ -58,6 +63,9 @@ Texture(const string &name) :
   _keep_ram_image = true;
   _all_dirty_flags = 0;
   _border_color.set(0.0f, 0.0f, 0.0f, 1.0f);
+  _compression = CM_default;
+  _ram_image_compression = CM_off;
+  _ram_page_size = 0;
   _render_to_texture = false;
   _match_framebuffer_format = false;
 
@@ -69,6 +77,7 @@ Texture(const string &name) :
   set_component_type(T_unsigned_byte);
 
   _loaded_from_disk = false;
+  _loaded_from_txo = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -95,6 +104,7 @@ Texture(const Texture &copy) :
   _format(copy._format),
   _component_type(copy._component_type),
   _loaded_from_disk(copy._loaded_from_disk),
+  _loaded_from_txo(copy._loaded_from_txo),
   _wrap_u(copy._wrap_u),
   _wrap_v(copy._wrap_v),
   _wrap_w(copy._wrap_w),
@@ -103,12 +113,56 @@ Texture(const Texture &copy) :
   _anisotropic_degree(copy._anisotropic_degree),
   _keep_ram_image(copy._keep_ram_image),
   _border_color(copy._border_color),
+  _compression(copy._compression),
   _match_framebuffer_format(copy._match_framebuffer_format),
   _all_dirty_flags(0),
-  _image(copy._image)
+  _ram_image(copy._ram_image),
+  _ram_image_compression(copy._ram_image_compression),
+  _ram_page_size(copy._ram_page_size)
 {
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::Copy Assignment Operator
+//       Access: Protected
+//  Description: Use Texture::make_copy() to make a duplicate copy of
+//               an existing Texture.
+////////////////////////////////////////////////////////////////////
+void Texture::
+operator = (const Texture &copy) {
+  Namable::operator = (copy);
+  _filename = copy._filename;
+  _alpha_filename = copy._alpha_filename;
+  _fullpath = copy._fullpath;
+  _alpha_fullpath = copy._alpha_fullpath;
+  _primary_file_num_channels = copy._primary_file_num_channels;
+  _alpha_file_channel = copy._alpha_file_channel;
+  _x_size = copy._x_size;
+  _y_size = copy._y_size;
+  _z_size = copy._z_size;
+  _num_components = copy._num_components;
+  _component_width = copy._component_width;
+  _texture_type = copy._texture_type;
+  _format = copy._format;
+  _component_type = copy._component_type;
+  _loaded_from_disk = copy._loaded_from_disk;
+  _loaded_from_txo = copy._loaded_from_txo;
+  _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;
+  _keep_ram_image = copy._keep_ram_image;
+  _border_color = copy._border_color;
+  _compression = copy._compression;
+  _match_framebuffer_format = copy._match_framebuffer_format;
+  _all_dirty_flags = 0;
+  _ram_image = copy._ram_image;
+  _ram_image_compression = copy._ram_image_compression;
+  _ram_page_size = copy._ram_page_size;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::Destructor
 //       Access: Published, Virtual
@@ -170,6 +224,7 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
 
   clear_ram_image();
   _loaded_from_disk = false;
+  _loaded_from_txo = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -187,7 +242,7 @@ setup_texture(Texture::TextureType texture_type, int x_size, int y_size,
 void Texture::
 generate_normalization_cube_map(int size) {
   setup_cube_map(size, T_unsigned_byte, F_rgb);
-  PTA_uchar image = modify_ram_image();
+  PTA_uchar image = make_ram_image();
 
   float half_size = (float)size * 0.5f;
   float center = half_size - 0.5f;
@@ -284,9 +339,18 @@ generate_normalization_cube_map(int size) {
 //               than this number.
 //
 //               This also implicitly sets keep_ram_image to false.
+//
+//               If the filename has the extension .txo, this
+//               implicitly reads a texture object instead of a
+//               filename (which replaces all of the texture
+//               properties).
 ////////////////////////////////////////////////////////////////////
 bool Texture::
 read(const Filename &fullpath, int z, int primary_file_num_channels) {
+  if (is_txo_filename(fullpath)) {
+    return read_txo_file(fullpath);
+  }
+
   PNMImage image;
 
   if (textures_header_only) {
@@ -461,11 +525,24 @@ read(const Filename &fullpath, const Filename &alpha_fullpath,
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::write
 //       Access: Published
-//  Description: Writes the texture to the indicated filename.
+//  Description: Writes the texture to the indicated filename.  If the
+//               filename ends in the extension .txo, this implicitly
+//               writes a Panda texture object (.txo) instead of an
+//               image file.
 ////////////////////////////////////////////////////////////////////
 bool Texture::
 write(const Filename &name, int z) const {
+  if (!has_ram_image()) {
+    ((Texture *)this)->get_ram_image();
+  }
   nassertr(has_ram_image(), false);
+
+  if (is_txo_filename(name)) {
+    return write_txo_file(name);
+  }
+
+  nassertr(get_ram_image_compression() == CM_off, false);
+
   PNMImage pnmimage;
   if (!store(pnmimage, z)) {
     return false;
@@ -479,6 +556,114 @@ write(const Filename &name, int z) const {
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::read_txo
+//       Access: Published
+//  Description: Reads the texture from a Panda texture object.  This
+//               defines the complete Texture specification, including
+//               the image data as well as all texture properties.
+//
+//               The filename is just for reference.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+read_txo(istream &in, const string &filename) {
+  DatagramInputFile din;
+
+  if (!din.open(in)) {
+    gobj_cat.error()
+      << "Could not read texture object: " << filename << "\n";
+    return false;
+  }
+
+  string head;
+  if (!din.read_header(head, _bam_header.size())) {
+    gobj_cat.error()
+      << filename << " is not a texture object file.\n";
+    return false;
+  }
+
+  if (head != _bam_header) {
+    gobj_cat.error()
+      << filename << " is not a texture object file.\n";
+    return false;
+  }
+
+  BamReader reader(&din, filename);
+  if (!reader.init()) {
+    return false;
+  }
+
+  TypedWritable *object = reader.read_object();
+  if (object == (TypedWritable *)NULL) {
+    gobj_cat.error()
+      << "Texture object " << filename << " is empty.\n";
+    return false;
+
+  } else if (!object->is_of_type(Texture::get_class_type())) {
+    gobj_cat.error()
+      << "Texture object " << filename << "contains a "
+      << object->get_type() << ", not a Texture.\n";
+    return false;
+  }
+
+  PT(Texture) other = DCAST(Texture, object);
+  if (!reader.resolve()) {
+    gobj_cat.error()
+      << "Unable to fully resolve texture object file.\n";
+    return false;
+  }
+
+  (*this) = (*other);
+  _loaded_from_disk = true;
+  _loaded_from_txo = true;
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::write_txo
+//       Access: Published
+//  Description: Writes the texture to a Panda texture object.  This
+//               defines the complete Texture specification, including
+//               the image data as well as all texture properties.
+//
+//               The filename is just for reference.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+write_txo(ostream &out, const string &filename) const {
+  DatagramOutputFile dout;
+
+  if (!dout.open(out)) {
+    gobj_cat.error()
+      << "Could not write texture object: " << filename << "\n";
+    return false;
+  }
+
+  if (!dout.write_header(_bam_header)) {
+    gobj_cat.error()
+      << "Unable to write to " << filename << "\n";
+    return false;
+  }
+
+  BamWriter writer(&dout, filename);
+  if (!writer.init()) {
+    return false;
+  }
+
+  writer.set_file_texture_mode(BTM_rawdata);
+
+  if (!writer.write_object(this)) {
+    return false;
+  }
+
+  if (!has_ram_image()) {
+    gobj_cat.error()
+      << get_name() << " does not have ram image\n";
+    return false;
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::read_pages
 //       Access: Published
@@ -497,6 +682,10 @@ write(const Filename &name, int z) const {
 ////////////////////////////////////////////////////////////////////
 bool Texture::
 read_pages(Filename fullpath_pattern, int z_size) {
+  if (is_txo_filename(fullpath_pattern)) {
+    return read_txo_file(fullpath_pattern);
+  }
+
   fullpath_pattern.set_pattern(true);
   if (!fullpath_pattern.has_hash()) {
     gobj_cat.error()
@@ -566,6 +755,10 @@ read_pages(Filename fullpath_pattern, int z_size) {
 ////////////////////////////////////////////////////////////////////
 bool Texture::
 write_pages(Filename fullpath_pattern) {
+  if (is_txo_filename(fullpath_pattern)) {
+    return write_txo_file(fullpath_pattern);
+  }
+
   fullpath_pattern.set_pattern(true);
   if (!fullpath_pattern.has_hash()) {
     gobj_cat.error()
@@ -938,13 +1131,37 @@ set_border_color(const Colorf &color) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::set_compression
+//       Access: Published
+//  Description: Requests that this particular Texture be compressed
+//               when it is loaded into texture memory.  
+//
+//               This refers to the internal compression of the
+//               texture image within texture memory; it is not
+//               related to jpeg or png compression, which are disk
+//               file compression formats.  The actual disk file that
+//               generated this texture may be stored in a compressed
+//               or uncompressed format supported by Panda; it will be
+//               decompressed on load, and then recompressed by the
+//               graphics API if this parameter is not CM_off.
+//
+//               If the GSG does not support this texture compression
+//               mode, the texture will silently be loaded
+//               uncompressed.
+////////////////////////////////////////////////////////////////////
+void Texture::
+set_compression(Texture::CompressionMode compression) {
+  if (_compression != compression) {
+    mark_dirty(DF_image);
+    _compression = compression;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_render_to_texture
 //       Access: Published
-//  Description: Specifies the solid color of the texture's border.
-//               Some OpenGL implementations use a border for tiling
-//               textures; in Panda, it is only used for specifying
-//               the clamp color.
+//  Description: 
 ////////////////////////////////////////////////////////////////////
 void Texture::
 set_render_to_texture(bool render_to_texture) {
@@ -981,7 +1198,7 @@ set_render_to_texture(bool render_to_texture) {
 ////////////////////////////////////////////////////////////////////
 bool Texture::
 has_ram_image() const {
-  return !_image.empty();
+  return !_ram_image.empty();
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1018,26 +1235,28 @@ get_ram_image() {
     reload_ram_image();
   }
 
-  return _image;
+  return _ram_image;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::modify_ram_image
 //       Access: Published
 //  Description: Returns a modifiable pointer to the system-RAM image.
-//               If the RAM image has been dumped, creates a new one.
+//               This assumes the RAM image should be uncompressed.
+//               If the RAM image has been dumped, or is stored
+//               compressed, creates a new one.
 //
 //               This also implicitly sets keep_ram_image to true.
 ////////////////////////////////////////////////////////////////////
 PTA_uchar Texture::
 modify_ram_image() {
-  if (_image.empty()) {
+  if (_ram_image.empty() || _ram_image_compression != CM_off) {
     make_ram_image();
   }
 
   mark_dirty(DF_image);
   _keep_ram_image = true;
-  return _image;
+  return _ram_image;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1051,25 +1270,35 @@ modify_ram_image() {
 ////////////////////////////////////////////////////////////////////
 PTA_uchar Texture::
 make_ram_image() {
-  _image = PTA_uchar::empty_array(get_expected_ram_image_size());
+  _ram_image = PTA_uchar::empty_array(get_expected_ram_image_size());
+  _ram_image_compression = CM_off;
   mark_dirty(DF_image);
   _keep_ram_image = true;
-  return _image;
+  return _ram_image;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_ram_image
 //       Access: Published
 //  Description: Replaces the current system-RAM image with the new
-//               data.
+//               data.  If compression is not CM_off, it indicates
+//               that the new data is already pre-compressed in the
+//               indicated format.
 //
 //               This also implicitly sets keep_ram_image to true.
 ////////////////////////////////////////////////////////////////////
 void Texture::
-set_ram_image(PTA_uchar image) {
-  nassertv(image.size() == get_expected_ram_image_size());
-  if (_image != image) {
-    _image = image;
+set_ram_image(PTA_uchar image, Texture::CompressionMode compression,
+	      size_t page_size) {
+  nassertv(compression != CM_default);
+  nassertv(compression != CM_off || image.size() == get_expected_ram_image_size());
+  if (_ram_image != image || _ram_image_compression != compression) {
+    _ram_image = image;
+    _ram_image_compression = compression;
+    _ram_page_size = page_size;
+    if (page_size == 0) {
+      _ram_page_size = image.size();
+    }
     mark_dirty(DF_image);
   }
   _keep_ram_image = true;
@@ -1082,7 +1311,8 @@ set_ram_image(PTA_uchar image) {
 ////////////////////////////////////////////////////////////////////
 void Texture::
 clear_ram_image() {
-  _image.clear();
+  _ram_image.clear();
+  _ram_image_compression = CM_off;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -1172,6 +1402,169 @@ release_all() {
   return num_freed;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::write
+//       Access: Published
+//  Description: Not to be confused with write(Filename), this method
+//               simply describes the texture properties.
+////////////////////////////////////////////////////////////////////
+void Texture::
+write(ostream &out, int indent_level) const {
+  indent(out, indent_level)
+    << get_type() << " " << get_name();
+  if (!get_filename().empty()) {
+    out << " (from " << get_filename() << ")";
+  }
+  out << "\n";
+
+  indent(out, indent_level + 2);
+  
+  switch (get_texture_type()) {
+  case TT_1d_texture:
+    out << "1-d, " << get_x_size();
+    break;
+
+  case TT_2d_texture:
+    out << "2-d, " << get_x_size() << " x " << get_y_size();
+    break;
+
+  case TT_3d_texture:
+    out << "3-d, " << get_x_size() << " x " << get_y_size()
+	<< " x " << get_z_size();
+    break;
+
+  case TT_cube_map:
+    out << "cube map, " << get_x_size() << " x " << get_y_size();
+    break;
+  }
+
+  out << " pixels, each " << get_num_components();
+
+  switch (get_component_type()) {
+  case T_unsigned_byte:
+    out << " bytes";
+    break;
+
+  case T_unsigned_short:
+    out << " shorts";
+    break;
+
+  case T_float:
+    out << " floats";
+    break;
+  }
+
+  out << ", ";
+  switch (get_format()) {
+  case F_color_index:
+    out << "color_index";
+    break;
+  case F_stencil_index:
+    out << "stencil_index";
+    break;
+  case F_depth_component:
+    out << "depth_component";
+    break;
+
+  case F_rgba:
+    out << "rgba";
+    break;
+  case F_rgbm:
+    out << "rgbm";
+    break;
+  case F_rgba12:
+    out << "rgba12";
+    break;
+  case F_rgba8:
+    out << "rgba8";
+    break;
+  case F_rgba4:
+    out << "rgba4";
+    break;
+
+  case F_rgb:
+    out << "rgb";
+    break;
+  case F_rgb12:
+    out << "rgb12";
+    break;
+  case F_rgb8:
+    out << "rgb8";
+    break;
+  case F_rgb5:
+    out << "rgb5";
+    break;
+  case F_rgba5:
+    out << "rgba5";
+    break;
+  case F_rgb332:
+    out << "rgb332";
+    break;
+
+  case F_red:
+    out << "red";
+    break;
+  case F_green:
+    out << "green";
+    break;
+  case F_blue:
+    out << "blue";
+    break;
+  case F_alpha:
+    out << "alpha";
+    break;
+  case F_luminance:
+    out << "luminance";
+    break;
+  case F_luminance_alpha:
+    out << "luminance_alpha";
+    break;
+  case F_luminance_alphamask:
+    out << "luminance_alphamask";
+    break;
+  }
+
+  if (get_compression() != CM_default) {
+    out << ", compression " << get_compression();
+  }
+  out << "\n";
+
+  indent(out, indent_level + 2);
+  
+  switch (get_texture_type()) {
+  case TT_1d_texture:
+    out << get_wrap_u() << ", ";
+    break;
+
+  case TT_2d_texture:
+    out << get_wrap_u() << " x " << get_wrap_v() << ", ";
+    break;
+
+  case TT_3d_texture:
+    out << get_wrap_u() << " x " << get_wrap_v() 
+	<< " x " << get_wrap_w() << ", ";
+    break;
+
+  case TT_cube_map:
+    break;
+  }
+
+  out << "min " << get_minfilter()
+      << ", mag " << get_magfilter()
+      << ", aniso " << get_anisotropic_degree()
+      << ", border " << get_border_color()
+      << "\n";
+
+  indent(out, indent_level + 2);
+  if (has_ram_image()) {
+    out << get_ram_image_size() << " bytes in ram, compression "
+	<< get_ram_image_compression() << "\n";
+
+  } else {
+    out << "no ram image\n";
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_format
 //       Access: Published
@@ -1307,7 +1700,7 @@ prepare_now(PreparedGraphicsObjects *prepared_objects,
         gobj_cat.debug()
           << "Dumping RAM for texture " << get_name() << "\n";
       }
-      _image.clear();
+      _ram_image.clear();
     }
   }
   return tc;
@@ -1465,17 +1858,23 @@ reconsider_dirty() {
 ////////////////////////////////////////////////////////////////////
 void Texture::
 reload_ram_image() {
-  if (_texture_type == TT_1d_texture || _texture_type == TT_2d_texture) {
-    gobj_cat.info()
-      << "Reloading texture " << get_name() << "\n";
+  gobj_cat.info()
+    << "Reloading texture " << get_name() << "\n";
 
-    make_ram_image();
+  make_ram_image();
+
+  if (_texture_type == TT_1d_texture || _texture_type == TT_2d_texture) {
+    // 1-d or 2-d texture.  Just one page.
     if (has_alpha_fullpath()) {
       read(get_fullpath(), get_alpha_fullpath(),
            0, _primary_file_num_channels, _alpha_file_channel);
     } else {
       read(get_fullpath(), 0, _primary_file_num_channels);
     }
+    
+  } else {
+    // 3-d texture or cube map.  Multiple pages.
+    read_pages(get_fullpath(), _z_size);
   }
 }
 
@@ -1510,6 +1909,50 @@ down_to_power_2(int value) {
   return x;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::is_specific
+//       Access: Public, Static
+//  Description: Returns true if the indicated compression mode is one
+//               of the specific compression types, false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+is_specific(Texture::CompressionMode compression) {
+  switch (compression) {
+  case CM_default:
+  case CM_off:
+  case CM_on:
+    return false;
+
+  default:
+    return true;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::has_alpha
+//       Access: Public, Static
+//  Description: Returns true if the indicated format includes alpha,
+//               false otherwise.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+has_alpha(Format format) {
+  switch (format) {
+  case F_alpha:
+  case F_rgba:
+  case F_rgbm:
+  case F_rgba4:
+  case F_rgba5:
+  case F_rgba8:
+  case F_rgba12:
+  case F_luminance_alpha:
+  case F_luminance_alphamask:
+    return true;
+
+  default:
+    return false;
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::reconsider_z_size
 //       Access: Protected
@@ -1530,9 +1973,9 @@ reconsider_z_size(int z) {
     // Increase the size of the data buffer to make room for the new
     // texture level.
     size_t new_size = get_expected_ram_image_size();
-    if (!_image.is_null() && new_size > _image.size()) {
-      _image.insert(_image.end(), new_size - _image.size(), 0);
-      nassertr(_image.size() == new_size, false);
+    if (!_ram_image.is_null() && new_size > _ram_image.size()) {
+      _ram_image.insert(_ram_image.end(), new_size - _ram_image.size(), 0);
+      nassertr(_ram_image.size() == new_size, false);
     }
   }
 
@@ -1710,6 +2153,66 @@ consider_downgrade(PNMImage &pnmimage, int num_channels) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::read_txo_file
+//       Access: Private
+//  Description: Called internally when read() or read_pages() detects
+//               a txo file.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+read_txo_file(const Filename &fullpath) {
+  VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr();
+
+  Filename filename = Filename::binary_filename(fullpath);
+  PT(VirtualFile) file = vfs->get_file(filename);
+  if (file == (VirtualFile *)NULL) {
+    // No such file.
+    gobj_cat.error()
+      << "Could not find " << fullpath << "\n";
+    return false;
+  }
+  
+  if (gobj_cat.is_debug()) {
+    gobj_cat.debug()
+      << "Reading texture object " << filename << "\n";
+  }
+  
+  istream *in = file->open_read_file(true);
+  bool success = read_txo(*in, fullpath);
+  vfs->close_read_file(in);
+  
+  set_fullpath(fullpath);
+  clear_alpha_fullpath();
+  _keep_ram_image = false;
+  
+  return success;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::write_txo_file
+//       Access: Private
+//  Description: Called internally when write() or write_pages() detects
+//               a txo file.
+////////////////////////////////////////////////////////////////////
+bool Texture::
+write_txo_file(const Filename &fullpath) const {
+  Filename filename = Filename::binary_filename(fullpath);
+  ofstream out;
+  if (!filename.open_write(out)) {
+    gobj_cat.error()
+      << "Unable to open " << filename << "\n";
+    return false;
+  }
+  
+#ifdef HAVE_ZLIB
+  if (fullpath.get_extension() == "pz") {
+    OCompressStream compressor(&out, false);
+    return write_txo(compressor);
+  }
+#endif  // HAVE_ZLIB
+  return write_txo(out, fullpath);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::register_with_read_factory
 //       Access: Public, Static
@@ -1752,7 +2255,12 @@ make_from_bam(const FactoryParams &params) {
   if (has_rawdata) {
     // If the raw image data is included, then just create a Texture
     // and don't load from the file.
-    me = new Texture("");
+    me = new Texture(name);
+    me->_filename = filename;
+    me->_alpha_filename = alpha_filename;
+    me->_primary_file_num_channels = primary_file_num_channels;
+    me->_alpha_file_channel = alpha_file_channel;
+    me->_texture_type = texture_type;
 
   } else {
     if (filename.empty()) {
@@ -1828,6 +2336,10 @@ fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
   _magfilter = (FilterType)scan.get_uint8();
   _anisotropic_degree = scan.get_int16();
   _border_color.read_datagram(scan);
+  _compression = CM_default;
+  if (manager->get_file_minor_ver() >= 1) {
+    _compression = (CompressionMode)scan.get_uint8();
+  }
 
   Format format = (Format)scan.get_uint8();
   int num_components = scan.get_uint8();
@@ -1849,15 +2361,21 @@ fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
     _z_size = scan.get_uint32();
     _component_type = (ComponentType)scan.get_uint8();
     _component_width = scan.get_uint8();
+    _ram_image_compression = CM_off;
+    _ram_page_size = 0;
+    if (manager->get_file_minor_ver() >= 1) {
+      _ram_image_compression = (CompressionMode)scan.get_uint8();
+      _ram_page_size = scan.get_uint32();
+    }
     _loaded_from_disk = false;
 
     PN_uint32 u_size = scan.get_uint32();
 
     // fill the _image buffer with image data
     string temp_buff = scan.extract_bytes(u_size);
-    _image = PTA_uchar::empty_array((int) u_size);
+    _ram_image = PTA_uchar::empty_array((int) u_size);
     for (PN_uint32 u_idx=0; u_idx < u_size; ++u_idx) {
-      _image[(int)u_idx] = (uchar) temp_buff[u_idx];
+      _ram_image[(int)u_idx] = (uchar) temp_buff[u_idx];
     }
   }
 }
@@ -1870,11 +2388,6 @@ fillin(DatagramIterator &scan, BamReader *manager, bool has_rawdata) {
 ////////////////////////////////////////////////////////////////////
 void Texture::
 write_datagram(BamWriter *manager, Datagram &me) {
-  bool has_bam_dir = !manager->get_filename().empty();
-  Filename bam_dir = manager->get_filename().get_dirname();
-  Filename filename = get_filename();
-  Filename alpha_filename = get_alpha_filename();
-
   // Write out the texture's raw pixel data if (a) the current Bam
   // Texture Mode requires that, or (b) there's no filename, so the
   // file can't be loaded up from disk, but the raw pixel data is
@@ -1883,10 +2396,24 @@ write_datagram(BamWriter *manager, Datagram &me) {
   // Otherwise, we just write out the filename, and assume whoever
   // loads the bam file later will have access to the image file on
   // disk.
+  BamTextureMode file_texture_mode = manager->get_file_texture_mode();
   bool has_rawdata =
-    (bam_texture_mode == BTM_rawdata || (has_ram_image() && filename.empty()));
+    (file_texture_mode == BTM_rawdata || (has_ram_image() && get_filename().empty()));
+  if (has_rawdata && !has_ram_image()) {
+    get_ram_image();
+    if (!has_ram_image()) {
+      // No image data after all.
+      has_rawdata = false;
+    }
+  }
+
+  bool has_bam_dir = !manager->get_filename().empty();
+  Filename bam_dir = manager->get_filename().get_dirname();
+  Filename filename = get_filename();
+  Filename alpha_filename = get_alpha_filename();
+
 
-  switch (bam_texture_mode) {
+  switch (file_texture_mode) {
   case BTM_unchanged:
   case BTM_rawdata:
     break;
@@ -1928,7 +2455,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
 
   default:
     gobj_cat.error()
-      << "Unsupported bam-texture-mode: " << (int)bam_texture_mode << "\n";
+      << "Unsupported bam-texture-mode: " << (int)file_texture_mode << "\n";
   }
 
   me.add_string(get_name());
@@ -1947,6 +2474,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
   me.add_uint8(_magfilter);
   me.add_int16(_anisotropic_degree);
   _border_color.write_datagram(me);
+  me.add_uint8(_compression);
 
   me.add_uint8(_format);
   me.add_uint8(_num_components);
@@ -1959,9 +2487,11 @@ write_datagram(BamWriter *manager, Datagram &me) {
     me.add_uint32(_z_size);
     me.add_uint8(_component_type);
     me.add_uint8(_component_width);
+    me.add_uint8(_ram_image_compression);
+    me.add_uint32(_ram_page_size);
 
-    me.add_uint32(_image.size());
-    me.append_data(_image, _image.size());
+    me.add_uint32(_ram_image.size());
+    me.append_data(_ram_image, _ram_image.size());
   }
 }
 
@@ -2046,3 +2576,33 @@ operator >> (istream &in, Texture::WrapMode &wm) {
   wm = Texture::string_wrap_mode(word);
   return in;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::CompressionMode output operator
+//  Description:
+////////////////////////////////////////////////////////////////////
+ostream &
+operator << (ostream &out, Texture::CompressionMode cm) {
+  switch (cm) {
+  case Texture::CM_default:
+    return out << "default";
+  case Texture::CM_off:
+    return out << "off";
+  case Texture::CM_on:
+    return out << "on";
+  case Texture::CM_fxt1:
+    return out << "fxt1";
+  case Texture::CM_dxt1:
+    return out << "dxt1";
+  case Texture::CM_dxt2:
+    return out << "dxt2";
+  case Texture::CM_dxt3:
+    return out << "dxt3";
+  case Texture::CM_dxt4:
+    return out << "dxt4";
+  case Texture::CM_dxt5:
+    return out << "dxt5";
+  }
+
+  return out << "(**invalid Texture::CompressionMode(" << (int)cm << ")**)";
+}

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

@@ -144,10 +144,31 @@ PUBLISHED:
     WM_invalid
   };
 
+  enum CompressionMode {
+    // Generic compresssion modes.  Usually, you should choose one of
+    // these.
+    CM_default,  // on or off, according to compressed-textures
+    CM_off,      // uncompressed image
+    CM_on,       // whatever compression the driver supports
+
+    // Specific compression modes.  Use only when you really want to
+    // use a particular compression algorithm.  Use with caution; not
+    // all drivers support all compression modes.  You can use
+    // GSG::get_supports_compressed_texture_format() to query the
+    // available compression modes for a particular GSG.
+    CM_fxt1,
+    CM_dxt1,
+    CM_dxt2,
+    CM_dxt3,
+    CM_dxt4,
+    CM_dxt5,
+  };
+
 PUBLISHED:
   Texture(const string &name = string());
 protected:
   Texture(const Texture &copy);
+  void operator = (const Texture &copy);
 PUBLISHED:
   virtual ~Texture();
 
@@ -179,6 +200,9 @@ PUBLISHED:
         int primary_file_num_channels = 0, int alpha_file_channel = 0);
   bool write(const Filename &fullpath, int z = 0) const;
 
+  bool read_txo(istream &in, const string &filename = "stream");
+  bool write_txo(ostream &out, const string &filename = "stream") const;
+
   bool read_pages(Filename fullpath_pattern, int z_size = 0);
   bool write_pages(Filename fullpath_pattern);
 
@@ -213,6 +237,7 @@ PUBLISHED:
   void set_magfilter(FilterType filter);
   void set_anisotropic_degree(int anisotropic_degree);
   void set_border_color(const Colorf &color);
+  void set_compression(CompressionMode compression);
   void set_render_to_texture(bool render_to_texture);
 
   INLINE WrapMode get_wrap_u() const;
@@ -222,18 +247,22 @@ PUBLISHED:
   INLINE FilterType get_magfilter() const;
   INLINE int get_anisotropic_degree() const;
   INLINE Colorf get_border_color() const;
+  INLINE CompressionMode get_compression() const;
   INLINE bool get_render_to_texture() const;
   INLINE bool uses_mipmaps() const;
 
   virtual bool has_ram_image() const;
   INLINE bool might_have_ram_image() const;
   INLINE size_t get_ram_image_size() const;
+  INLINE size_t get_ram_page_size() const;
   INLINE size_t get_expected_ram_image_size() const;
   INLINE size_t get_expected_ram_page_size() const;
   CPTA_uchar get_ram_image();
+  INLINE CompressionMode get_ram_image_compression() const;
   PTA_uchar modify_ram_image();
   PTA_uchar make_ram_image();
-  void set_ram_image(PTA_uchar image);
+  void set_ram_image(PTA_uchar image, CompressionMode compression = CM_off,
+		     size_t page_size = 0);
   void clear_ram_image();
   INLINE void set_keep_ram_image(bool keep_ram_image);
   virtual bool get_keep_ram_image() const;
@@ -242,6 +271,8 @@ PUBLISHED:
   bool release(PreparedGraphicsObjects *prepared_objects);
   int release_all();
 
+  void write(ostream &out, int indent_level) const;
+
 PUBLISHED:
   // These are published, but in general, you shouldn't be mucking
   // with these values; they are set automatically when a texture is
@@ -264,6 +295,9 @@ PUBLISHED:
   INLINE void set_loaded_from_disk();
   INLINE bool get_loaded_from_disk() const;
 
+  INLINE void set_loaded_from_txo();
+  INLINE bool get_loaded_from_txo() const;
+
 public:
   INLINE bool get_match_framebuffer_format() const;
   INLINE void set_match_framebuffer_format(bool flag);
@@ -298,6 +332,9 @@ public:
   static int up_to_power_2(int value);
   static int down_to_power_2(int value);
 
+  static bool is_specific(CompressionMode compression);
+  static bool has_alpha(Format format);
+
 protected:
   virtual void reconsider_dirty();
   virtual void reload_ram_image();
@@ -319,6 +356,10 @@ private:
   INLINE double get_unsigned_byte(int &index) const;
   INLINE double get_unsigned_short(int &index) const;
 
+  INLINE static bool is_txo_filename(const Filename &fullpath);
+  bool read_txo_file(const Filename &fullpath);
+  bool write_txo_file(const Filename &fullpath) const;
+
 protected:
   Filename _filename;
   Filename _alpha_filename;
@@ -344,6 +385,7 @@ protected:
   ComponentType _component_type;
 
   bool _loaded_from_disk;
+  bool _loaded_from_txo;
 
   WrapMode _wrap_u;
   WrapMode _wrap_v;
@@ -353,6 +395,7 @@ protected:
   int _anisotropic_degree;
   bool _keep_ram_image;
   Colorf _border_color;
+  CompressionMode _compression;
   bool _render_to_texture;
   bool _match_framebuffer_format;
 
@@ -377,7 +420,9 @@ protected:
   // texture.
   int _all_dirty_flags;
 
-  PTA_uchar _image;
+  PTA_uchar _ram_image;
+  CompressionMode _ram_image_compression;
+  size_t _ram_page_size;
 
 
   // Datagram stuff
@@ -417,6 +462,8 @@ EXPCL_PANDA istream &operator >> (istream &in, Texture::FilterType &ft);
 EXPCL_PANDA ostream &operator << (ostream &out, Texture::WrapMode wm);
 EXPCL_PANDA istream &operator >> (istream &in, Texture::WrapMode &wm);
 
+EXPCL_PANDA ostream &operator << (ostream &out, Texture::CompressionMode cm);
+
 #include "texture.I"
 
 #endif

+ 4 - 1
panda/src/gobj/videoTexture.cxx

@@ -33,6 +33,9 @@ VideoTexture::
 VideoTexture(const string &name) : 
   Texture(name) 
 {
+  // We don't want to try to compress each frame as it's loaded.
+  _compression = CM_off;
+
   _video_width = 0;
   _video_height = 0;
 
@@ -69,7 +72,7 @@ has_ram_image() const {
   if (this_frame != _last_frame_update) {
     return false;
   }
-  return !_image.empty();
+  return !_ram_image.empty();
 }
 
 ////////////////////////////////////////////////////////////////////

+ 2 - 2
panda/src/grutil/openCVTexture.cxx

@@ -346,7 +346,7 @@ update_frame(int frame) {
       const unsigned char *source = page._color.get_frame_data(frame);
       if (source != NULL) {
         nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size);
-        unsigned char *dest = _image.p() + get_expected_ram_page_size() * z;
+        unsigned char *dest = _ram_image.p() + get_expected_ram_page_size() * z;
 
         int dest_row_width = (_x_size * _num_components * _component_width);
         int source_row_width = get_video_width() * 3;
@@ -385,7 +385,7 @@ update_frame(int frame) {
       const unsigned char *source = page._alpha.get_frame_data(frame);
       if (source != NULL) {
         nassertv(get_video_width() <= _x_size && get_video_height() <= _y_size);
-        unsigned char *dest = _image.p() + get_expected_ram_page_size() * z;
+        unsigned char *dest = _ram_image.p() + get_expected_ram_page_size() * z;
 
         int dest_row_width = (_x_size * _num_components * _component_width);
         int source_row_width = get_video_width() * 3;

+ 24 - 0
panda/src/pgraph/bamFile.cxx

@@ -369,6 +369,30 @@ get_current_minor_ver() {
   return _bam_minor_ver;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamFile::get_reader
+//       Access: Public
+//  Description: Returns the BamReader in charge of performing the
+//               read operations.  This will return NULL unless
+//               open_read() was called.
+////////////////////////////////////////////////////////////////////
+BamReader *BamFile::
+get_reader() {
+  return _reader;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamFile::get_writer
+//       Access: Public
+//  Description: Returns the BamWriter in charge of performing the
+//               write operations.  This will return NULL unless
+//               open_write() was called.
+////////////////////////////////////////////////////////////////////
+BamWriter *BamFile::
+get_writer() {
+  return _writer;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamFile::continue_open_read
 //       Access: Private

+ 3 - 0
panda/src/pgraph/bamFile.h

@@ -78,6 +78,9 @@ PUBLISHED:
   int get_current_major_ver();
   int get_current_minor_ver();
 
+  BamReader *get_reader();
+  BamWriter *get_writer();
+
 private:
   bool continue_open_read(const string &bam_filename, bool report_errors);
   bool continue_open_write(const string &bam_filename, bool report_errors);

+ 7 - 2
panda/src/putil/Sources.pp

@@ -12,7 +12,9 @@
     animInterface.h animInterface.I \
     bam.h bamEndian.h \
     bamReader.I bamReader.N bamReader.h bamReaderParam.I \
-    bamReaderParam.h bamWriter.I bamWriter.h \
+    bamReaderParam.h \
+    bamTextureMode.h \
+    bamWriter.I bamWriter.h \
     bitArray.I bitArray.h \
     bitMask.I bitMask.h \
     buttonHandle.I \
@@ -58,7 +60,9 @@
  #define INCLUDED_SOURCES \
     animInterface.cxx \
     bamEndian.cxx \
-    bamReader.cxx bamReaderParam.cxx bamWriter.cxx \
+    bamReader.cxx bamReaderParam.cxx \
+    bamTextureMode.cxx \
+    bamWriter.cxx \
     bitArray.cxx \
     bitMask.cxx \
     buttonHandle.cxx buttonRegistry.cxx \
@@ -91,6 +95,7 @@
     animInterface.h animInterface.I \
     bam.h bamEndian.h \
     bamReader.I bamReader.h bamReaderParam.I bamReaderParam.h \
+    bamTextureMode.h \
     bamWriter.I bamWriter.h \
     bitArray.I bitArray.h \
     bitMask.I bitMask.h \

+ 2 - 1
panda/src/putil/bam.h

@@ -36,7 +36,8 @@ static const unsigned short _bam_major_ver = 6;
 // Bumped to major version 5 on 5/6/05 for new Geom implementation.
 // Bumped to major version 6 on 2/11/06 to factor out PandaNode::CData.
 
-static const unsigned short _bam_minor_ver = 0;
+static const unsigned short _bam_minor_ver = 1;
+// Bumped to minor version 1 on 3/12/06 to add Texture::_compression.
 
 
 #endif

+ 66 - 0
panda/src/putil/bamTextureMode.cxx

@@ -0,0 +1,66 @@
+// Filename: bamTextureMode.cxx
+// Created by:  drose (14Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 "bamTextureMode.h"
+#include "config_util.h"
+
+ostream &
+operator << (ostream &out, BamTextureMode btm) {
+  switch (btm) {
+  case BTM_unchanged:
+    return out << "unchanged";
+   
+  case BTM_fullpath:
+    return out << "fullpath";
+    
+  case BTM_relative:
+    return out << "relative";
+    
+  case BTM_basename:
+    return out << "basename";
+
+  case BTM_rawdata:
+    return out << "rawdata";
+  }
+
+  return out << "**invalid BamTextureMode (" << (int)btm << ")**";
+}
+
+istream &
+operator >> (istream &in, BamTextureMode &btm) {
+  string word;
+  in >> word;
+
+  if (cmp_nocase(word, "unchanged") == 0) {
+    btm = BTM_unchanged;
+  } else if (cmp_nocase(word, "fullpath") == 0) {
+    btm = BTM_fullpath;
+  } else if (cmp_nocase(word, "relative") == 0) {
+    btm = BTM_relative;
+  } else if (cmp_nocase(word, "basename") == 0) {
+    btm = BTM_basename;
+  } else if (cmp_nocase(word, "rawdata") == 0) {
+    btm = BTM_rawdata;
+
+  } else {
+    util_cat->error() << "Invalid BamTextureMode value: " << word << "\n";
+    btm = BTM_relative;
+  }
+
+  return in;
+}

+ 36 - 0
panda/src/putil/bamTextureMode.h

@@ -0,0 +1,36 @@
+// Filename: bamTextureMode.h
+// Created by:  drose (14Mar06)
+//
+////////////////////////////////////////////////////////////////////
+//
+// 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 BAMTEXTUREMODE_H
+#define BAMTEXTUREMODE_H
+
+#include "pandabase.h"
+
+// This enum is used to control how textures are written to a bam
+// stream.
+enum BamTextureMode {
+  BTM_unchanged,
+  BTM_fullpath,
+  BTM_relative,
+  BTM_basename,
+  BTM_rawdata
+};
+EXPCL_PANDA ostream &operator << (ostream &out, BamTextureMode btm);
+EXPCL_PANDA istream &operator >> (istream &in, BamTextureMode &btm);
+
+#endif

+ 26 - 0
panda/src/putil/bamWriter.I

@@ -43,3 +43,29 @@ INLINE BamEndian BamWriter::
 get_file_endian() const {
   return _file_endian;
 }
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::get_file_texture_mode
+//       Access: Public
+//  Description: Returns the BamTextureMode preference indicated by
+//               the Bam file currently being written.  Texture
+//               objects written to this Bam file will be encoded
+//               according to the specified mode.
+////////////////////////////////////////////////////////////////////
+INLINE BamTextureMode BamWriter::
+get_file_texture_mode() const {
+  return _file_texture_mode;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: BamWriter::set_file_texture_mode
+//       Access: Public
+//  Description: Changes the BamTextureMode preference for
+//               the Bam file currently being written.  Texture
+//               objects written to this Bam file will be encoded
+//               according to the specified mode.
+////////////////////////////////////////////////////////////////////
+INLINE void BamWriter::
+set_file_texture_mode(BamTextureMode file_texture_mode) {
+  _file_texture_mode = file_texture_mode;
+}

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

@@ -78,6 +78,7 @@ init() {
   _long_pta_id = false;
 
   _file_endian = bam_endian;
+  _file_texture_mode = bam_texture_mode;
 
   // Write out the current major and minor BAM file version numbers.
   Datagram header;

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

@@ -22,6 +22,7 @@
 #include "pandabase.h"
 #include "notify.h"
 #include "bamEndian.h"
+#include "bamTextureMode.h"
 #include "typedWritable.h"
 #include "datagramSink.h"
 #include "pdeque.h"
@@ -88,6 +89,9 @@ public:
 
   INLINE BamEndian get_file_endian() const;
 
+  INLINE BamTextureMode get_file_texture_mode() const;
+  INLINE void set_file_texture_mode(BamTextureMode file_texture_mode);
+
 public:
   // Functions to support classes that write themselves to the Bam.
 
@@ -109,6 +113,7 @@ private:
   Filename _filename;
 
   BamEndian _file_endian;
+  BamTextureMode _file_texture_mode;
 
   // This is the set of all TypeHandles already written.
   pset<int, int_hash> _types_written;

+ 5 - 0
panda/src/putil/config_util.cxx

@@ -53,6 +53,11 @@ ConfigVariableEnum<BamEndian> bam_endian
           "may set it to \"littleendian\" or \"bigendian\" to target a "
           "particular platform."));
 
+ConfigVariableEnum<BamTextureMode> bam_texture_mode
+("bam-texture-mode", BTM_relative,
+ PRC_DESC("Set this to specify how textures should be written into Bam files."
+          "See the panda source or documentation for available options."));
+
 ConfigVariableSearchPath model_path
 ("model-path", 
  PRC_DESC("The default directories to search for all models and general "

+ 3 - 1
panda/src/putil/config_util.h

@@ -24,6 +24,7 @@
 #include "configVariableSearchPath.h"
 #include "configVariableEnum.h"
 #include "bamEndian.h"
+#include "bamTextureMode.h"
 #include "dconfig.h"
 
 class DSearchPath;
@@ -36,9 +37,10 @@ NotifyCategoryDecl(bam, EXPCL_PANDA, EXPTP_PANDA);
 // because we must be able to access it at static init time.  Instead
 // of declaring it a global constant, we'll make it a member of
 // MemoryUsage.
-
 //extern EXPCL_PANDA const bool track_memory_usage;
+
 extern EXPCL_PANDA ConfigVariableEnum<BamEndian> bam_endian;
+extern EXPCL_PANDA ConfigVariableEnum<BamTextureMode> bam_texture_mode;
 
 extern EXPCL_PANDA ConfigVariableSearchPath model_path;
 extern EXPCL_PANDA ConfigVariableSearchPath texture_path;

+ 2 - 2
panda/src/putil/load_prc_file.h

@@ -16,8 +16,8 @@
 //
 ////////////////////////////////////////////////////////////////////
 
-#ifndef LOAD_EGG_FILE_H
-#define LOAD_EGG_FILE_H
+#ifndef LOAD_PRC_FILE_H
+#define LOAD_PRC_FILE_H
 
 #include "pandabase.h"
 

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

@@ -2,6 +2,7 @@
 #include "bamEndian.cxx"
 #include "bamReader.cxx"
 #include "bamReaderParam.cxx"
+#include "bamTextureMode.cxx"
 #include "bamWriter.cxx"
 #include "bitArray.cxx"
 #include "bitMask.cxx"

+ 4 - 0
panda/src/text/dynamicTextPage.cxx

@@ -33,6 +33,10 @@ DynamicTextPage::
 DynamicTextPage(DynamicTextFont *font, int page_number) : 
   _font(font)
 {
+  // Since the texture might change frequently, don't try to compress
+  // it by default.
+  _compression = CM_off;
+
   _x_size = _font->get_page_x_size();
   _y_size = _font->get_page_y_size();
 

+ 15 - 0
pandatool/src/bam/bamInfo.cxx

@@ -21,6 +21,7 @@
 #include "bamFile.h"
 #include "pandaNode.h"
 #include "geomNode.h"
+#include "texture.h"
 #include "recorderHeader.h"
 #include "recorderFrame.h"
 #include "recorderTable.h"
@@ -145,6 +146,10 @@ get_info(const Filename &filename) {
       objects[0]->is_of_type(PandaNode::get_class_type())) {
     describe_scene_graph(DCAST(PandaNode, objects[0]));
 
+  } else if (objects.size() == 1 && 
+      objects[0]->is_of_type(Texture::get_class_type())) {
+    describe_texture(DCAST(Texture, objects[0]));
+
   } else if (!objects.empty() && objects[0]->is_of_type(RecorderHeader::get_class_type())) {
     describe_session(DCAST(RecorderHeader, objects[0]), objects);
 
@@ -189,6 +194,16 @@ describe_scene_graph(PandaNode *node) {
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: BamInfo::describe_texture
+//       Access: Private
+//  Description: Called for Bam files that contain a Texture object.
+////////////////////////////////////////////////////////////////////
+void BamInfo::
+describe_texture(Texture *tex) {
+  tex->write(nout, 2);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: BamInfo::describe_session
 //       Access: Private

+ 1 - 0
pandatool/src/bam/bamInfo.h

@@ -48,6 +48,7 @@ private:
 
   bool get_info(const Filename &filename);
   void describe_scene_graph(PandaNode *node);
+  void describe_texture(Texture *tex);
   void describe_session(RecorderHeader *header, const Objects &objects);
   void describe_general_object(TypedWritable *object);
   void list_hierarchy(PandaNode *node, int indent_level);

+ 242 - 2
pandatool/src/bam/eggToBam.cxx

@@ -24,6 +24,18 @@
 #include "config_egg2pg.h"
 #include "config_gobj.h"
 #include "config_chan.h"
+#include "pandaNode.h"
+#include "geomNode.h"
+#include "renderState.h"
+#include "textureAttrib.h"
+#include "dcast.h"
+#include "graphicsPipeSelection.h"
+#include "graphicsEngine.h"
+#include "graphicsBuffer.h"
+#include "graphicsStateGuardian.h"
+#include "load_prc_file.h"
+#include "windowProperties.h"
+#include "frameBufferProperties.h"
 
 ////////////////////////////////////////////////////////////////////
 //     Function: EggToBam::Constructor
@@ -36,7 +48,10 @@ EggToBam() :
 {
   set_program_description
     ("This program reads Egg files and outputs Bam files, the binary format "
-     "suitable for direct loading of animation and models into Panda.");
+     "suitable for direct loading of animation and models into Panda.  Bam "
+     "files are tied to a particular version of Panda, so should not be "
+     "considered replacements for egg files, but they tend to be smaller and "
+     "load much faster than the equivalent egg files.");
 
   // -f is always in effect for egg2bam.  It doesn't make sense to
   // provide it as an option to the user.
@@ -107,6 +122,53 @@ EggToBam() :
      "way to ensure the bam file is completely self-contained.",
      &EggToBam::dispatch_none, &_tex_rawdata);
 
+  add_option
+    ("txo", "", 0,
+     "Rather than writing texture data directly into the bam file, as in "
+     "-rawtex, create a texture object for each referenced texture.  A "
+     "texture object is a kind of mini-bam file, with a .txo extension, "
+     "that contains all of the data needed to recreate a texture, including "
+     "its image contents, filter and wrap settings, and so on.  3-D textures "
+     "and cube maps can also be represented in a single .txo file.  Texture "
+     "object files, like bam files, are tied to a particular version of "
+     "Panda.",
+     &EggToBam::dispatch_none, &_tex_txo);
+
+#ifdef HAVE_ZLIB
+  add_option
+    ("txopz", "", 0,
+     "In addition to writing texture object files as above, compress each "
+     "one using pzip to a .txo.pz file.  In many cases, this will yield a "
+     "disk file size comparable to that achieved by png compression.  This "
+     "is an on-disk compression only, and does not affect the amount of "
+     "RAM or texture memory consumed by the texture when it is loaded.",
+     &EggToBam::dispatch_none, &_tex_txopz);
+#endif  // HAVE_ZLIB
+
+  add_option
+    ("ctex", "", 0,
+     "Asks the graphics card to pre-compress the texture images when using "
+     "-rawtex or -txo.  "
+#ifdef HAVE_ZLIB
+     "This is unrelated to the on-disk compression achieved "
+     "via -txopz (and it may be used in conjunction with that parameter).  "
+#endif  // HAVE_ZLIB
+     "This will result in a smaller RAM and texture memory footprint for "
+     "the texture images.  The same "
+     "effect can be achieved at load time by setting compressed-textures in "
+     "your Config.prc file; but -ctex pre-compresses the "
+     "textures so that they do not need to be compressed at load time.  "
+     "Note that using -ctex makes .txo files that are only guaranteed to load "
+     "on the particular graphics card that was used to generate them.",
+     &EggToBam::dispatch_none, &_tex_ctex);
+
+  add_option
+    ("load-display", "display name", 0,
+     "Specifies the particular display module to load to perform the texture "
+     "compression requested by -ctex.  If this is omitted, the default is "
+     "taken from the Config.prc file.",
+     &EggToBam::dispatch_string, NULL, &_load_display);
+
   redescribe_option
     ("cs",
      "Specify the coordinate system of the resulting " + _format_name +
@@ -118,6 +180,7 @@ EggToBam() :
   _egg_flatten = 0;
   _egg_combine_geoms = 0;
   _egg_suppress_hidden = 1;
+  _tex_txopz = false;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -162,6 +225,31 @@ run() {
     nout << "Unable to build scene graph from egg file.\n";
     exit(1);
   }
+
+  if (_tex_ctex) {
+    if (!make_buffer()) {
+      nout << "Unable to initialize graphics context; cannot compress textures.\n";
+      exit(1);
+    }
+  }
+
+  if (_tex_txo || _tex_txopz || (_tex_ctex && _tex_rawdata)) {
+    collect_textures(root);
+    Textures::iterator ti;
+    for (ti = _textures.begin(); ti != _textures.end(); ++ti) {
+      Texture *tex = (*ti);
+      if (_tex_ctex) {
+        tex->set_keep_ram_image(true);
+        tex->set_compression(Texture::CM_on);
+        if (!_engine->extract_texture_data(tex, _gsg)) {
+          nout << "  couldn't compress " << tex->get_name() << "\n";
+        }
+      }
+      if (_tex_txo || _tex_txopz) {
+        convert_txo(tex);
+      }
+    }
+  }
   
   if (_ls) {
     root->ls(nout, 0);
@@ -170,7 +258,7 @@ run() {
   // This should be guaranteed because we pass false to the
   // constructor, above.
   nassertv(has_output_filename());
-  
+
   Filename filename = get_output_filename();
   filename.make_dir();
   nout << "Writing " << filename << "\n";
@@ -214,6 +302,158 @@ handle_args(ProgramBase::Args &args) {
   return EggToSomething::handle_args(args);
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: EggToBam::collect_textures
+//       Access: Private
+//  Description: Recursively walks the scene graph, looking for
+//               Texture references.
+////////////////////////////////////////////////////////////////////
+void EggToBam::
+collect_textures(PandaNode *node) {
+  collect_textures(node->get_state());
+  if (node->is_geom_node()) {
+    GeomNode *geom_node = DCAST(GeomNode, node);
+    int num_geoms = geom_node->get_num_geoms();
+    for (int i = 0; i < num_geoms; ++i) {
+      collect_textures(geom_node->get_geom_state(i));
+    }
+  }
+
+  PandaNode::Children children = node->get_children();
+  int num_children = children.get_num_children();
+  for (int i = 0; i < num_children; ++i) {
+    collect_textures(children.get_child(i));
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToBam::collect_textures
+//       Access: Private
+//  Description: Recursively walks the scene graph, looking for
+//               Texture references.
+////////////////////////////////////////////////////////////////////
+void EggToBam::
+collect_textures(const RenderState *state) {
+  const TextureAttrib *tex_attrib = state->get_texture();
+  if (tex_attrib != (TextureAttrib *)NULL) {
+    int num_on_stages = tex_attrib->get_num_on_stages();
+    for (int i = 0; i < num_on_stages; ++i) {
+      _textures.insert(tex_attrib->get_on_texture(tex_attrib->get_on_stage(i)));
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToBam::convert_txo
+//       Access: Private
+//  Description: If the indicated Texture was not already loaded from
+//               a txo file, writes it to a txo file and updates the
+//               Texture object to reference the new file.
+////////////////////////////////////////////////////////////////////
+void EggToBam::
+convert_txo(Texture *tex) {
+  if (!tex->get_loaded_from_txo()) {
+    Filename fullpath = tex->get_fullpath().get_filename_index(0);
+    if (_tex_txopz) {
+      fullpath.set_extension("txo.pz");
+      // We use this clumsy syntax so that the new extension appears to be
+      // two separate extensions, .txo followed by .pz, which is what
+      // Texture::write() expects to find.
+      fullpath = Filename(fullpath.get_fullpath());
+    } else {
+      fullpath.set_extension("txo");
+    }
+
+    if (tex->write(fullpath)) {
+      nout << "  Writing " << fullpath;
+      if (tex->get_ram_image_compression() != Texture::CM_off) {
+        nout << " (compressed " << tex->get_ram_image_compression() << ")";
+      }
+      nout << "\n";
+      tex->set_loaded_from_txo();
+      tex->set_fullpath(fullpath);
+      tex->clear_alpha_fullpath();
+
+      Filename filename = tex->get_filename().get_filename_index(0);
+      if (_tex_txopz) {
+        filename.set_extension("txo.pz");
+        filename = Filename(filename.get_fullpath());
+      } else {
+        filename.set_extension("txo");
+      }
+
+      tex->set_filename(filename);
+      tex->clear_alpha_filename();
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: EggToBam::make_buffer
+//       Access: Private
+//  Description: Creates a GraphicsBuffer for communicating with the
+//               graphics card.
+////////////////////////////////////////////////////////////////////
+bool EggToBam::
+make_buffer() {
+  if (!_load_display.empty()) {
+    // Override the user's config file with the command-line parameter.
+    string prc = "load-display " + _load_display;
+    load_prc_file_data("prc", prc);
+  }
+
+  GraphicsPipeSelection *selection = GraphicsPipeSelection::get_global_ptr();
+  _pipe = selection->make_default_pipe();
+  if (_pipe == (GraphicsPipe *)NULL) {
+    nout << "Unable to create graphics pipe.\n";
+    return false;
+  }
+
+  _engine = new GraphicsEngine;
+
+  FrameBufferProperties fbprops = FrameBufferProperties::get_default();
+
+  // Some graphics drivers can only create single-buffered offscreen
+  // buffers.  So request that.
+  fbprops.set_frame_buffer_mode((fbprops.get_frame_buffer_mode() & ~FrameBufferProperties::FM_buffer) | FrameBufferProperties::FM_single_buffer);
+
+  PT(GraphicsStateGuardian) gsg = 
+    _engine->make_gsg(_pipe, fbprops);
+  if (gsg == (GraphicsStateGuardian *)NULL) {
+    nout << "Unable to create GraphicsStateGuardian.\n";
+    return false;
+  }
+  _gsg = gsg;
+
+  // We don't care how big the buffer is; we just need it to manifest
+  // the GSG.
+  _buffer = _engine->make_buffer(_gsg, "buffer", 0, 1, 1);
+  _engine->open_windows();
+  if (_buffer == (GraphicsOutput *)NULL || !_buffer->is_valid()) {
+    if (_buffer != (GraphicsOutput *)NULL) {
+      _engine->remove_window(_buffer);
+    }
+
+    // Couldn't create a buffer; try for a window instead.
+    GraphicsWindow *window = _engine->make_window(_gsg, "window", 0);
+    if (window == (GraphicsWindow *)NULL) {
+      nout << "Unable to create graphics window.\n";
+      return false;
+    }
+    WindowProperties props;
+    props.set_size(1, 1);
+    props.set_origin(0, 0);
+    props.set_undecorated(true);
+    props.set_open(true);
+    props.set_z_order(WindowProperties::Z_bottom);
+    window->request_properties(props);
+    _buffer = window;
+    _engine->open_windows();
+  }
+
+  return true;
+}
+
 
 int main(int argc, char *argv[]) {
   EggToBam prog;

+ 29 - 0
pandatool/src/bam/eggToBam.h

@@ -22,6 +22,15 @@
 #include "pandatoolbase.h"
 
 #include "eggToSomething.h"
+#include "pset.h"
+#include "graphicsPipe.h"
+
+class PandaNode;
+class RenderState;
+class Texture;
+class GraphicsEngine;
+class GraphicsStateGuardian;
+class GraphicsOutput;
 
 ////////////////////////////////////////////////////////////////////
 //       Class : EggToBam
@@ -37,6 +46,16 @@ protected:
   virtual bool handle_args(Args &args);
 
 private:
+  void collect_textures(PandaNode *node);
+  void collect_textures(const RenderState *state);
+  void convert_txo(Texture *tex);
+
+  bool make_buffer();
+
+private:
+  typedef pset<Texture *> Textures;
+  Textures _textures;
+
   bool _has_egg_flatten;
   int _egg_flatten;
   bool _has_egg_combine_geoms;
@@ -47,6 +66,16 @@ private:
   int _compression_quality;
   bool _compression_off;
   bool _tex_rawdata;
+  bool _tex_txo;
+  bool _tex_txopz;
+  bool _tex_ctex;
+  string _load_display;
+
+  // The rest of this is required to support -ctex.
+  PT(GraphicsPipe) _pipe;
+  GraphicsStateGuardian *_gsg;
+  GraphicsEngine *_engine;
+  GraphicsOutput *_buffer;
 };
 
 #endif

Some files were not shown because too many files changed in this diff