Browse Source

add Texture::set_ram_image_as(), Texture::string_*(), Texture::get_tex_scale()

David Rose 15 years ago
parent
commit
e3a3170e5b

+ 7 - 20
panda/src/framework/windowFramework.cxx

@@ -1214,31 +1214,18 @@ load_image_as_model(const Filename &filename) {
     return NULL;
   }
 
-  int x_size = tex->get_x_size() - tex->get_pad_x_size();
-  int y_size = tex->get_y_size() - tex->get_pad_y_size();
-  int full_x = tex->get_x_size();
-  int full_y = tex->get_y_size();
-  bool has_alpha = true;
-  LVecBase2f tex_scale((x_size)*1.0f/full_x, (y_size*1.0f)/full_y);
-
-  if (tex->is_of_type(VideoTexture::get_class_type())) {
-    // Get the size from the video stream.
-    VideoTexture *vtex = DCAST(VideoTexture, tex);
-    x_size = vtex->get_video_width();
-    y_size = vtex->get_video_height();
-    tex_scale = vtex->get_tex_scale();
-  } else {
-    // Get the size from the original image (the texture may have
-    // scaled it to make a power of 2).
-    x_size = tex->get_orig_file_x_size();
-    y_size = tex->get_orig_file_y_size();
-  }
-
   // Yes, it is an image file; make a texture out of it.
   tex->set_minfilter(Texture::FT_linear_mipmap_linear);
   tex->set_magfilter(Texture::FT_linear);
 
   // Ok, now make a polygon to show the texture.
+  bool has_alpha = true;
+  LVecBase2f tex_scale = tex->get_tex_scale();
+
+  // Get the size from the original image (the texture may have
+  // scaled it to make a power of 2).
+  int x_size = tex->get_orig_file_x_size();
+  int y_size = tex->get_orig_file_y_size();
 
   // Choose the dimensions of the polygon appropriately.
   float left,right,top,bottom;

+ 6 - 0
panda/src/gobj/config_gobj.cxx

@@ -551,6 +551,9 @@ operator << (ostream &out, AutoTextureScale ats) {
     
   case ATS_up:
     return out << "up";
+    
+  case ATS_pad:
+    return out << "pad";
 
   case ATS_UNSPECIFIED:
     return out << "UNSPECIFIED";
@@ -579,6 +582,9 @@ operator >> (istream &in, AutoTextureScale &ats) {
   } else if (cmp_nocase(word, "up") == 0) {
     ats = ATS_up;
 
+  } else if (cmp_nocase(word, "pad") == 0) {
+    ats = ATS_pad;
+
   } else {
     gobj_cat->error() << "Invalid AutoTextureScale value: " << word << "\n";
     ats = ATS_none;

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

@@ -31,6 +31,7 @@ enum AutoTextureScale {
   ATS_none,
   ATS_down,
   ATS_up,
+  ATS_pad,
   ATS_UNSPECIFIED,
 };
 enum ShaderUtilization {

+ 25 - 0
panda/src/gobj/texture.I

@@ -517,6 +517,31 @@ get_pad_z_size() const {
   return _pad_z_size;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::get_tex_scale
+//       Access: Published
+//  Description: Returns a scale pair that is suitable for applying to
+//               geometry via NodePath::set_tex_scale(), which will
+//               convert texture coordinates on the geometry from the
+//               range 0..1 into the appropriate range to render the
+//               video part of the texture.
+//
+//               This is necessary only if a padding size has been set
+//               via set_pad_size() (or implicitly via something like
+//               "textures-power-2 pad" in the config.prc file).  In
+//               this case, this is a convenient way to generate UV's
+//               that reflect the built-in padding size.
+////////////////////////////////////////////////////////////////////
+INLINE LVecBase2f Texture::
+get_tex_scale() const {
+  if (_pad_x_size == 0 || _pad_y_size == 0 ||
+      _x_size == 0 || _y_size == 0) {
+    LVecBase2f(1.0f, 1.0f);
+  }
+  return LVecBase2f((float)(_x_size - _pad_x_size) / (float)_x_size,
+                    (float)(_y_size - _pad_y_size) / (float)_y_size);
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::set_pad_size
 //       Access: Published

+ 628 - 238
panda/src/gobj/texture.cxx

@@ -853,6 +853,130 @@ set_ram_image(CPTA_uchar image, Texture::CompressionMode compression,
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::set_ram_image_as
+//       Access: Published
+//  Description: Replaces the current system-RAM image with the new
+//               data, converting it first if necessary from the
+//               indicated component-order format.  See
+//               get_ram_image_as() for specifications about the
+//               format.  This method cannot support compressed image
+//               data or sub-pages; use set_ram_image() for that.
+////////////////////////////////////////////////////////////////////
+void Texture::
+set_ram_image_as(CPTA_uchar image, const string &supplied_format) {
+  string format = upcase(supplied_format);
+
+  // Make sure we can grab something that's uncompressed.
+  int imgsize = _x_size * _y_size;
+  nassertv(image.size() == (size_t)(_component_width * format.size() * imgsize));
+
+  // Check if the format is already what we have internally.
+  if ((_num_components == 1 && format.size() == 1) ||
+      (_num_components == 2 && format.size() == 2 && format.at(1) == 'A' && format.at(0) != 'A') ||
+      (_num_components == 3 && format == "BGR") ||
+      (_num_components == 4 && format == "BGRA")) {
+    // The format string is already our format, so we just need to copy it.
+    set_ram_image(image);
+    return;
+  }
+
+  // Create a new empty array that can hold our image.
+  PTA_uchar newdata = PTA_uchar::empty_array(imgsize * _num_components * _component_width, get_class_type());
+
+  // These ifs are for optimization of commonly used image types.
+  if (format == "RGBA" && _num_components == 4 && _component_width == 1) {
+    imgsize *= 4;
+    for (int p = 0; p < imgsize; p += 4) {
+      newdata[p + 2] = image[p    ];
+      newdata[p + 1] = image[p + 1];
+      newdata[p    ] = image[p + 2];
+      newdata[p + 3] = image[p + 3];
+    }
+    set_ram_image(newdata);
+    return;
+  }
+  if (format == "RGB" && _num_components == 3 && _component_width == 1) {
+    imgsize *= 3;
+    for (int p = 0; p < imgsize; p += 3) {
+      newdata[p + 2] = image[p    ];
+      newdata[p + 1] = image[p + 1];
+      newdata[p    ] = image[p + 2];
+    }
+    set_ram_image(newdata);
+    return;
+  }
+  if (format == "A" && _component_width == 1 && _num_components != 3) {
+    // We can generally rely on alpha to be the last component.
+    int component = _num_components - 1;
+    for (int p = 0; p < imgsize; ++p) {
+      newdata[component] = image[p];
+    }
+    set_ram_image(newdata);
+    return;
+  }
+  if (_component_width == 1) {
+    for (int p = 0; p < imgsize; ++p) {
+      for (uchar s = 0; s < format.size(); ++s) {
+        signed char component = -1;
+        if (format.at(s) == 'B' || (_num_components <= 2 && format.at(s) != 'A')) {
+          component = 0;
+        } else if (format.at(s) == 'G') {
+          component = 1;
+        } else if (format.at(s) == 'R') {
+          component = 2;
+        } else if (format.at(s) == 'A') {
+          nassertv(_num_components != 3);
+          component = _num_components - 1;
+        } else if (format.at(s) == '0') {
+          // Ignore.
+        } else if (format.at(s) == '1') {
+          // Ignore.
+        } else {
+          gobj_cat.error() << "Unexpected component character '"
+            << format.at(s) << "', expected one of RGBA!\n";
+          return;
+        }
+        if (component >= 0) {
+          newdata[p * _num_components + component] = image[p * format.size() + s];
+        }
+      }
+    }
+    set_ram_image(newdata);
+    return;
+  }
+  for (int p = 0; p < imgsize; ++p) {
+    for (uchar s = 0; s < format.size(); ++s) {
+      signed char component = -1;
+      if (format.at(s) == 'B' || (_num_components <= 2 && format.at(s) != 'A')) {
+        component = 0;
+      } else if (format.at(s) == 'G') {
+        component = 1;
+      } else if (format.at(s) == 'R') {
+        component = 2;
+      } else if (format.at(s) == 'A') {
+        nassertv(_num_components != 3);
+        component = _num_components - 1;
+      } else if (format.at(s) == '0') {
+        // Ignore.
+      } else if (format.at(s) == '1') {
+        // Ignore.
+      } else {
+        gobj_cat.error() << "Unexpected component character '"
+          << format.at(s) << "', expected one of RGBA!\n";
+        return;
+      }
+      if (component >= 0) {
+        memcpy((void*)(newdata + (p * _num_components + component) * _component_width),
+               (void*)(image + (p * format.size() + s) * _component_width),
+               _component_width);
+      }
+    }
+  }
+  set_ram_image(newdata);
+  return;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::get_keep_ram_image
 //       Access: Published, Virtual
@@ -1700,11 +1824,464 @@ void Texture::
 consider_rescale(PNMImage &pnmimage, const string &name) {
   int new_x_size = pnmimage.get_x_size();
   int new_y_size = pnmimage.get_y_size();
-  if (adjust_size(new_x_size, new_y_size, name)) {
+  if (adjust_size(new_x_size, new_y_size, name, false)) {
     pnmimage.set_read_size(new_x_size, new_y_size);
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::format_texture_type
+//       Access: Published, Static
+//  Description: Returns the indicated TextureType converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string Texture::
+format_texture_type(TextureType tt) {
+  switch (tt) {
+  case TT_1d_texture:
+    return "1d_texture";
+  case TT_2d_texture:
+    return "2d_texture";
+  case TT_3d_texture:
+    return "3d_texture";
+  case TT_2d_texture_array:
+    return "2d_texture_array";
+  case TT_cube_map:
+    return "cube_map";
+  }
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::string_texture_type
+//       Access: Published, Static
+//  Description: Returns the TextureType corresponding to the
+//               indicated string word.
+////////////////////////////////////////////////////////////////////
+Texture::TextureType Texture::
+string_texture_type(const string &str) {
+  if (cmp_nocase(str, "1d_texture") == 0) {
+    return TT_1d_texture;
+  } else if (cmp_nocase(str, "2d_texture") == 0) {
+    return TT_2d_texture;
+  } else if (cmp_nocase(str, "3d_texture") == 0) {
+    return TT_3d_texture;
+  } else if (cmp_nocase(str, "2d_texture_array") == 0) {
+    return TT_2d_texture_array;
+  } else if (cmp_nocase(str, "cube_map") == 0) {
+    return TT_cube_map;
+  }
+
+  gobj_cat->error()
+    << "Invalid Texture::TextureLevel value: " << str << "\n";
+  return TT_2d_texture;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::format_component_type
+//       Access: Published, Static
+//  Description: Returns the indicated ComponentType converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string Texture::
+format_component_type(ComponentType ct) {
+  switch (ct) {
+  case T_unsigned_byte:
+    return "unsigned_byte";
+  case T_unsigned_short:
+    return "unsigned_short";
+  case T_float:
+    return "float";
+  case T_unsigned_int_24_8:
+    return "unsigned_int_24_8";
+  }
+
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::string_component_type
+//       Access: Published, Static
+//  Description: Returns the ComponentType corresponding to the
+//               indicated string word.
+////////////////////////////////////////////////////////////////////
+Texture::ComponentType Texture::
+string_component_type(const string &str) {
+  if (cmp_nocase(str, "unsigned_byte") == 0) {
+    return T_unsigned_byte;
+  } else if (cmp_nocase(str, "unsigned_short") == 0) {
+    return T_unsigned_short;
+  } else if (cmp_nocase(str, "float") == 0) {
+    return T_float;
+  } else if (cmp_nocase(str, "unsigned_int_24_8") == 0) {
+    return T_unsigned_int_24_8;
+  }
+
+  gobj_cat->error()
+    << "Invalid Texture::ComponentType value: " << str << "\n";
+  return T_unsigned_byte;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::format_format
+//       Access: Published, Static
+//  Description: Returns the indicated Format converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string Texture::
+format_format(Format format) {
+  switch (format) {
+  case F_depth_stencil:
+    return "depth_stencil";
+  case F_depth_component:
+    return "depth_component";
+  case F_depth_component16:
+    return "depth_component16";
+  case F_depth_component24:
+    return "depth_component24";
+  case F_depth_component32:
+    return "depth_component32";
+  case F_color_index:
+    return "color_index";
+  case F_red:
+    return "red";
+  case F_green:
+    return "green";
+  case F_blue:
+    return "blue";
+  case F_alpha:
+    return "alpha";
+  case F_rgb:
+    return "rgb";
+  case F_rgb5:
+    return "rgb5";
+  case F_rgb8:
+    return "rgb8";
+  case F_rgb12:
+    return "rgb12";
+  case F_rgb332:
+    return "rgb332";
+  case F_rgba:
+    return "rgba";
+  case F_rgbm:
+    return "rgbm";
+  case F_rgba4:
+    return "rgba4";
+  case F_rgba5:
+    return "rgba5";
+  case F_rgba8:
+    return "rgba8";
+  case F_rgba12:
+    return "rgba12";
+  case F_luminance:
+    return "luminance";
+  case F_luminance_alpha:
+    return "luminance_alpha";
+  case F_luminance_alphamask:
+    return "luminance_alphamask";
+  case F_rgba16:
+    return "rgba16";
+  case F_rgba32:
+    return "rgba32";
+  }
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::string_format
+//       Access: Published, Static
+//  Description: Returns the Format corresponding to the
+//               indicated string word.
+////////////////////////////////////////////////////////////////////
+Texture::Format Texture::
+string_format(const string &str) {
+  if (cmp_nocase(str, "depth_stencil") == 0) {
+    return F_depth_stencil;
+  } else if (cmp_nocase(str, "depth_component") == 0) {
+    return F_depth_component;
+  } else if (cmp_nocase(str, "depth_component16") == 0) {
+    return F_depth_component16;
+  } else if (cmp_nocase(str, "depth_component24") == 0) {
+    return F_depth_component24;
+  } else if (cmp_nocase(str, "depth_component32") == 0) {
+    return F_depth_component32;
+  } else if (cmp_nocase(str, "color_index") == 0) {
+    return F_color_index;
+  } else if (cmp_nocase(str, "red") == 0) {
+    return F_red;
+  } else if (cmp_nocase(str, "green") == 0) {
+    return F_green;
+  } else if (cmp_nocase(str, "blue") == 0) {
+    return F_blue;
+  } else if (cmp_nocase(str, "alpha") == 0) {
+    return F_alpha;
+  } else if (cmp_nocase(str, "rgb") == 0) {
+    return F_rgb;
+  } else if (cmp_nocase(str, "rgb5") == 0) {
+    return F_rgb5;
+  } else if (cmp_nocase(str, "rgb8") == 0) {
+    return F_rgb8;
+  } else if (cmp_nocase(str, "rgb12") == 0) {
+    return F_rgb12;
+  } else if (cmp_nocase(str, "rgb332") == 0) {
+    return F_rgb332;
+  } else if (cmp_nocase(str, "rgba") == 0) {
+    return F_rgba;
+  } else if (cmp_nocase(str, "rgbm") == 0) {
+    return F_rgbm;
+  } else if (cmp_nocase(str, "rgba4") == 0) {
+    return F_rgba4;
+  } else if (cmp_nocase(str, "rgba5") == 0) {
+    return F_rgba5;
+  } else if (cmp_nocase(str, "rgba8") == 0) {
+    return F_rgba8;
+  } else if (cmp_nocase(str, "rgba12") == 0) {
+    return F_rgba12;
+  } else if (cmp_nocase(str, "luminance") == 0) {
+    return F_luminance;
+  } else if (cmp_nocase(str, "luminance_alpha") == 0) {
+    return F_luminance_alpha;
+  } else if (cmp_nocase(str, "luminance_alphamask") == 0) {
+    return F_luminance_alphamask;
+  } else if (cmp_nocase(str, "rgba16") == 0) {
+    return F_rgba16;
+  } else if (cmp_nocase(str, "rgba32") == 0) {
+    return F_rgba32;
+  }
+  
+  gobj_cat->error()
+    << "Invalid Texture::Format value: " << str << "\n";
+  return F_rgba;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::format_filter_type
+//       Access: Published, Static
+//  Description: Returns the indicated FilterType converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string Texture::
+format_filter_type(FilterType ft) {
+  switch (ft) {
+  case FT_nearest:
+    return "nearest";
+  case FT_linear:
+    return "linear";
+
+  case FT_nearest_mipmap_nearest:
+    return "nearest_mipmap_nearest";
+  case FT_linear_mipmap_nearest:
+    return "linear_mipmap_nearest";
+  case FT_nearest_mipmap_linear:
+    return "nearest_mipmap_linear";
+  case FT_linear_mipmap_linear:
+    return "linear_mipmap_linear";
+
+  case FT_shadow:
+    return "shadow";
+
+  case FT_default:
+    return "default";
+
+  case FT_invalid:
+    return "invalid";
+  }
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::string_filter_type
+//       Access: Public
+//  Description: Returns the FilterType value associated with the given
+//               string representation, or FT_invalid if the string
+//               does not match any known FilterType value.
+////////////////////////////////////////////////////////////////////
+Texture::FilterType Texture::
+string_filter_type(const string &string) {
+  if (cmp_nocase_uh(string, "nearest") == 0) {
+    return FT_nearest;
+  } else if (cmp_nocase_uh(string, "linear") == 0) {
+    return FT_linear;
+  } else if (cmp_nocase_uh(string, "nearest_mipmap_nearest") == 0) {
+    return FT_nearest_mipmap_nearest;
+  } else if (cmp_nocase_uh(string, "linear_mipmap_nearest") == 0) {
+    return FT_linear_mipmap_nearest;
+  } else if (cmp_nocase_uh(string, "nearest_mipmap_linear") == 0) {
+    return FT_nearest_mipmap_linear;
+  } else if (cmp_nocase_uh(string, "linear_mipmap_linear") == 0) {
+    return FT_linear_mipmap_linear;
+  } else if (cmp_nocase_uh(string, "mipmap") == 0) {
+    return FT_linear_mipmap_linear;
+  } else if (cmp_nocase_uh(string, "shadow") == 0) {
+    return FT_shadow;
+  } else if (cmp_nocase_uh(string, "default") == 0) {
+    return FT_default;
+  } else {
+    return FT_invalid;
+  }
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::format_wrap_mode
+//       Access: Published, Static
+//  Description: Returns the indicated WrapMode converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string Texture::
+format_wrap_mode(WrapMode wm) {
+  switch (wm) {
+  case WM_clamp:
+    return "clamp";
+  case WM_repeat:
+    return "repeat";
+  case WM_mirror:
+    return "mirror";
+  case WM_mirror_once:
+    return "mirror_once";
+  case WM_border_color:
+    return "border_color";
+
+  case WM_invalid:
+    return "invalid";
+  }
+
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::string_wrap_mode
+//       Access: Public
+//  Description: Returns the WrapMode value associated with the given
+//               string representation, or WM_invalid if the string
+//               does not match any known WrapMode value.
+////////////////////////////////////////////////////////////////////
+Texture::WrapMode Texture::
+string_wrap_mode(const string &string) {
+  if (cmp_nocase_uh(string, "repeat") == 0) {
+    return WM_repeat;
+  } else if (cmp_nocase_uh(string, "clamp") == 0) {
+    return WM_clamp;
+  } else if (cmp_nocase_uh(string, "mirror") == 0) {
+    return WM_clamp;
+  } else if (cmp_nocase_uh(string, "mirror_once") == 0) {
+    return WM_clamp;
+  } else if (cmp_nocase_uh(string, "border_color") == 0) {
+    return WM_border_color;
+  } else {
+    return WM_invalid;
+  }
+}
+  
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::format_compression_mode
+//       Access: Published, Static
+//  Description: Returns the indicated CompressionMode converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string Texture::
+format_compression_mode(CompressionMode cm) {
+  switch (cm) {
+  case CM_default:
+    return "default";
+  case CM_off:
+    return "off";
+  case CM_on:
+    return "on";
+  case CM_fxt1:
+    return "fxt1";
+  case CM_dxt1:
+    return "dxt1";
+  case CM_dxt2:
+    return "dxt2";
+  case CM_dxt3:
+    return "dxt3";
+  case CM_dxt4:
+    return "dxt4";
+  case CM_dxt5:
+    return "dxt5";
+  }
+
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::string_compression_mode
+//       Access: Public
+//  Description: Returns the CompressionMode value associated with the
+//               given string representation.
+////////////////////////////////////////////////////////////////////
+Texture::CompressionMode Texture::
+string_compression_mode(const string &str) {
+  if (cmp_nocase_uh(str, "default") == 0) {
+    return CM_default;
+  } else if (cmp_nocase_uh(str, "off") == 0) {
+    return CM_off;
+  } else if (cmp_nocase_uh(str, "on") == 0) {
+    return CM_on;
+  } else if (cmp_nocase_uh(str, "fxt1") == 0) {
+    return CM_fxt1;
+  } else if (cmp_nocase_uh(str, "dxt1") == 0) {
+    return CM_dxt1;
+  } else if (cmp_nocase_uh(str, "dxt2") == 0) {
+    return CM_dxt2;
+  } else if (cmp_nocase_uh(str, "dxt3") == 0) {
+    return CM_dxt3;
+  } else if (cmp_nocase_uh(str, "dxt4") == 0) {
+    return CM_dxt4;
+  } else if (cmp_nocase_uh(str, "dxt5") == 0) {
+    return CM_dxt5;
+  }
+  
+  gobj_cat->error()
+    << "Invalid Texture::CompressionMode value: " << str << "\n";
+  return CM_default;
+}
+
+  
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::format_quality_level
+//       Access: Published, Static
+//  Description: Returns the indicated QualityLevel converted to a
+//               string word.
+////////////////////////////////////////////////////////////////////
+string Texture::
+format_quality_level(QualityLevel ql) {
+  switch (ql) {
+  case QL_default:
+    return "default";
+  case QL_fastest:
+    return "fastest";
+  case QL_normal:
+    return "normal";
+  case QL_best:
+    return "best";
+  }
+
+  return "**invalid**";
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: Texture::string_quality_level
+//       Access: Public
+//  Description: Returns the QualityLevel value associated with the
+//               given string representation.
+////////////////////////////////////////////////////////////////////
+Texture::QualityLevel Texture::
+string_quality_level(const string &str) {
+  if (cmp_nocase(str, "default") == 0) {
+    return QL_default;
+  } else if (cmp_nocase(str, "fastest") == 0) {
+    return QL_fastest;
+  } else if (cmp_nocase(str, "normal") == 0) {
+    return QL_normal;
+  } else if (cmp_nocase(str, "best") == 0) {
+    return QL_best;
+  }
+
+  gobj_cat->error()
+    << "Invalid Texture::QualityLevel value: " << str << "\n";
+  return QL_default;
+}
 
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::texture_uploaded
@@ -1767,62 +2344,6 @@ cull_callback(CullTraverser *, const CullTraverserData &) const {
   return true;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::string_wrap_mode
-//       Access: Public
-//  Description: Returns the WrapMode value associated with the given
-//               string representation, or WM_invalid if the string
-//               does not match any known WrapMode value.
-////////////////////////////////////////////////////////////////////
-Texture::WrapMode Texture::
-string_wrap_mode(const string &string) {
-  if (cmp_nocase_uh(string, "repeat") == 0) {
-    return WM_repeat;
-  } else if (cmp_nocase_uh(string, "clamp") == 0) {
-    return WM_clamp;
-  } else if (cmp_nocase_uh(string, "mirror") == 0) {
-    return WM_clamp;
-  } else if (cmp_nocase_uh(string, "mirror_once") == 0) {
-    return WM_clamp;
-  } else if (cmp_nocase_uh(string, "border_color") == 0) {
-    return WM_border_color;
-  } else {
-    return WM_invalid;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: Texture::string_filter_type
-//       Access: Public
-//  Description: Returns the FilterType value associated with the given
-//               string representation, or FT_invalid if the string
-//               does not match any known FilterType value.
-////////////////////////////////////////////////////////////////////
-Texture::FilterType Texture::
-string_filter_type(const string &string) {
-  if (cmp_nocase_uh(string, "nearest") == 0) {
-    return FT_nearest;
-  } else if (cmp_nocase_uh(string, "linear") == 0) {
-    return FT_linear;
-  } else if (cmp_nocase_uh(string, "nearest_mipmap_nearest") == 0) {
-    return FT_nearest_mipmap_nearest;
-  } else if (cmp_nocase_uh(string, "linear_mipmap_nearest") == 0) {
-    return FT_linear_mipmap_nearest;
-  } else if (cmp_nocase_uh(string, "nearest_mipmap_linear") == 0) {
-    return FT_nearest_mipmap_linear;
-  } else if (cmp_nocase_uh(string, "linear_mipmap_linear") == 0) {
-    return FT_linear_mipmap_linear;
-  } else if (cmp_nocase_uh(string, "mipmap") == 0) {
-    return FT_linear_mipmap_linear;
-  } else if (cmp_nocase_uh(string, "shadow") == 0) {
-    return FT_shadow;
-  } else if (cmp_nocase_uh(string, "default") == 0) {
-    return FT_default;
-  } else {
-    return FT_invalid;
-  }
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: Texture::make_texture
 //       Access: Public, Static
@@ -1911,7 +2432,8 @@ has_binary_alpha(Format format) {
 //               adjusted, or false if it is the same.
 ////////////////////////////////////////////////////////////////////
 bool Texture::
-adjust_size(int &x_size, int &y_size, const string &name) {
+adjust_size(int &x_size, int &y_size, const string &name,
+            bool for_padding) {
   bool exclude = false;
   int num_excludes = exclude_texture_scale.get_num_unique_values();
   for (int i = 0; i < num_excludes && !exclude; ++i) {
@@ -1934,13 +2456,22 @@ adjust_size(int &x_size, int &y_size, const string &name) {
     new_y_size = min(max(new_y_size, (int)texture_scale_limit), y_size);
   }
 
-  switch (get_textures_power_2()) {
+  AutoTextureScale ats = get_textures_power_2();
+  if (!for_padding && ats == ATS_pad) {
+    // If we're not calculating the padding size--that is, we're
+    // calculating the initial scaling size instead--then ignore
+    // ATS_pad, and treat it the same as ATS_none.
+    ats = ATS_none;
+  }
+
+  switch (ats) {
   case ATS_down:
     new_x_size = down_to_power_2(new_x_size);
     new_y_size = down_to_power_2(new_y_size);
     break;
 
   case ATS_up:
+  case ATS_pad:
     new_x_size = up_to_power_2(new_x_size);
     new_y_size = up_to_power_2(new_y_size);
     break;
@@ -1950,12 +2481,17 @@ adjust_size(int &x_size, int &y_size, const string &name) {
     break;
   }
 
-  switch (textures_square.get_value()) {
+  ats = textures_square.get_value();
+  if (!for_padding && ats == ATS_pad) {
+    ats = ATS_none;
+  }
+  switch (ats) {
   case ATS_down:
     new_x_size = new_y_size = min(new_x_size, new_y_size);
     break;
 
   case ATS_up:
+  case ATS_pad:
     new_x_size = new_y_size = max(new_x_size, new_y_size);
     break;
 
@@ -2425,7 +2961,29 @@ do_read_one(const Filename &fullpath, const Filename &alpha_fullpath,
     }
   }
 
-  return do_load_one(image, fullpath.get_basename(), z, n, options);
+  // Now see if we want to pad the image within a larger power-of-2
+  // image.
+  int pad_x_size = 0;
+  int pad_y_size = 0;
+  if (get_textures_power_2() == ATS_pad) {
+    int new_x_size = image.get_x_size();
+    int new_y_size = image.get_y_size();
+    if (adjust_size(new_x_size, new_y_size, fullpath.get_basename(), true)) {
+      pad_x_size = new_x_size - image.get_x_size();
+      pad_y_size = new_y_size - image.get_y_size();
+      PNMImage new_image(new_x_size, new_y_size, image.get_num_channels(),
+                         image.get_maxval());
+      new_image.copy_sub_image(image, 0, new_y_size - image.get_y_size());
+      image.take_from(new_image);
+    }
+  }
+  
+  if (!do_load_one(image, fullpath.get_basename(), z, n, options)) {
+    return false;
+  }
+
+  do_set_pad_size(pad_x_size, pad_y_size, 0);
+  return true;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -3271,7 +3829,7 @@ do_reload_ram_image(bool allow_compression) {
       // changed, and we want a different-sized texture now.
       int x_size = _orig_file_x_size;
       int y_size = _orig_file_y_size;
-      Texture::adjust_size(x_size, y_size, _filename.get_basename());
+      Texture::adjust_size(x_size, y_size, _filename.get_basename(), true);
       if (x_size != tex->get_x_size() || y_size != tex->get_y_size()) {
         if (gobj_cat.is_debug()) {
           gobj_cat.debug()
@@ -6498,20 +7056,7 @@ write_datagram(BamWriter *manager, Datagram &me) {
 ////////////////////////////////////////////////////////////////////
 ostream &
 operator << (ostream &out, Texture::TextureType tt) {
-  switch (tt) {
-  case Texture::TT_1d_texture:
-    return out << "1d_texture";
-  case Texture::TT_2d_texture:
-    return out << "2d_texture";
-  case Texture::TT_3d_texture:
-    return out << "3d_texture";
-  case Texture::TT_2d_texture_array:
-    return out << "2d_texture_array";
-  case Texture::TT_cube_map:
-    return out << "cube_map";
-  }
-
-  return out << "(**invalid Texture::TextureType(" << (int)tt << ")**)";
+  return out << Texture::format_texture_type(tt);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -6520,18 +7065,7 @@ operator << (ostream &out, Texture::TextureType tt) {
 ////////////////////////////////////////////////////////////////////
 ostream &
 operator << (ostream &out, Texture::ComponentType ct) {
-  switch (ct) {
-  case Texture::T_unsigned_byte:
-    return out << "unsigned_byte";
-  case Texture::T_unsigned_short:
-    return out << "unsigned_short";
-  case Texture::T_float:
-    return out << "float";
-  case Texture::T_unsigned_int_24_8:
-    return out << "unsigned_int_24_8";
-  }
-
-  return out << "(**invalid Texture::ComponentType(" << (int)ct << ")**)";
+  return out << Texture::format_component_type(ct);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -6540,62 +7074,7 @@ operator << (ostream &out, Texture::ComponentType ct) {
 ////////////////////////////////////////////////////////////////////
 ostream &
 operator << (ostream &out, Texture::Format f) {
-  switch (f) {
-  case Texture::F_depth_stencil:
-    return out << "depth_stencil";
-  case Texture::F_depth_component:
-    return out << "depth_component";
-  case Texture::F_depth_component16:
-    return out << "depth_component16";
-  case Texture::F_depth_component24:
-    return out << "depth_component24";
-  case Texture::F_depth_component32:
-    return out << "depth_component32";
-  case Texture::F_color_index:
-    return out << "color_index";
-  case Texture::F_red:
-    return out << "red";
-  case Texture::F_green:
-    return out << "green";
-  case Texture::F_blue:
-    return out << "blue";
-  case Texture::F_alpha:
-    return out << "alpha";
-  case Texture::F_rgb:
-    return out << "rgb";
-  case Texture::F_rgb5:
-    return out << "rgb5";
-  case Texture::F_rgb8:
-    return out << "rgb8";
-  case Texture::F_rgb12:
-    return out << "rgb12";
-  case Texture::F_rgb332:
-    return out << "rgb332";
-  case Texture::F_rgba:
-    return out << "rgba";
-  case Texture::F_rgbm:
-    return out << "rgbm";
-  case Texture::F_rgba4:
-    return out << "rgba4";
-  case Texture::F_rgba5:
-    return out << "rgba5";
-  case Texture::F_rgba8:
-    return out << "rgba8";
-  case Texture::F_rgba12:
-    return out << "rgba12";
-  case Texture::F_luminance:
-    return out << "luminance";
-  case Texture::F_luminance_alpha:
-    return out << "luminance_alpha";
-  case Texture::F_luminance_alphamask:
-    return out << "luminance_alphamask";
-  case Texture::F_rgba16:
-    return out << "rgba16";
-  case Texture::F_rgba32:
-    return out << "rgba32";
-  }
-
-  return out << "(**invalid Texture::Format(" << (int)f << ")**)";
+  return out << Texture::format_format(f);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -6604,32 +7083,7 @@ operator << (ostream &out, Texture::Format f) {
 ////////////////////////////////////////////////////////////////////
 ostream &
 operator << (ostream &out, Texture::FilterType ft) {
-  switch (ft) {
-  case Texture::FT_nearest:
-    return out << "nearest";
-  case Texture::FT_linear:
-    return out << "linear";
-
-  case Texture::FT_nearest_mipmap_nearest:
-    return out << "nearest_mipmap_nearest";
-  case Texture::FT_linear_mipmap_nearest:
-    return out << "linear_mipmap_nearest";
-  case Texture::FT_nearest_mipmap_linear:
-    return out << "nearest_mipmap_linear";
-  case Texture::FT_linear_mipmap_linear:
-    return out << "linear_mipmap_linear";
-
-  case Texture::FT_shadow:
-    return out << "shadow";
-
-  case Texture::FT_default:
-    return out << "default";
-
-  case Texture::FT_invalid:
-    return out << "invalid";
-  }
-
-  return out << "(**invalid Texture::FilterType(" << (int)ft << ")**)";
+  return out << Texture::format_filter_type(ft);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -6651,23 +7105,7 @@ operator >> (istream &in, Texture::FilterType &ft) {
 ////////////////////////////////////////////////////////////////////
 ostream &
 operator << (ostream &out, Texture::WrapMode wm) {
-  switch (wm) {
-  case Texture::WM_clamp:
-    return out << "clamp";
-  case Texture::WM_repeat:
-    return out << "repeat";
-  case Texture::WM_mirror:
-    return out << "mirror";
-  case Texture::WM_mirror_once:
-    return out << "mirror_once";
-  case Texture::WM_border_color:
-    return out << "border_color";
-
-  case Texture::WM_invalid:
-    return out << "invalid";
-  }
-
-  return out << "(**invalid Texture::WrapMode(" << (int)wm << ")**)";
+  return out << Texture::format_wrap_mode(wm);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -6689,28 +7127,7 @@ operator >> (istream &in, Texture::WrapMode &wm) {
 ////////////////////////////////////////////////////////////////////
 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 << ")**)";
+  return out << Texture::format_compression_mode(cm);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -6719,18 +7136,7 @@ operator << (ostream &out, Texture::CompressionMode cm) {
 ////////////////////////////////////////////////////////////////////
 ostream &
 operator << (ostream &out, Texture::QualityLevel tql) {
-  switch (tql) {
-  case Texture::QL_default:
-    return out << "default";
-  case Texture::QL_fastest:
-    return out << "fastest";
-  case Texture::QL_normal:
-    return out << "normal";
-  case Texture::QL_best:
-    return out << "best";
-  }
-
-  return out << "**invalid Texture::QualityLevel (" << (int)tql << ")**";
+  return out << Texture::format_quality_level(tql);
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -6742,22 +7148,6 @@ operator >> (istream &in, Texture::QualityLevel &tql) {
   string word;
   in >> word;
 
-  if (cmp_nocase(word, "default") == 0) {
-    tql = Texture::QL_default;
-
-  } else if (cmp_nocase(word, "fastest") == 0) {
-    tql = Texture::QL_fastest;
-
-  } else if (cmp_nocase(word, "normal") == 0) {
-    tql = Texture::QL_normal;
-
-  } else if (cmp_nocase(word, "best") == 0) {
-    tql = Texture::QL_best;
-
-  } else {
-    gobj_cat->error() << "Invalid Texture::QualityLevel value: " << word << "\n";
-    tql = Texture::QL_default;
-  }
-
+  tql = Texture::string_quality_level(word);
   return in;
 }

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

@@ -322,6 +322,7 @@ PUBLISHED:
   INLINE PTA_uchar make_ram_image();
   void set_ram_image(CPTA_uchar image, CompressionMode compression = CM_off,
                      size_t page_size = 0);
+  void set_ram_image_as(CPTA_uchar image, const string &provided_format);
   INLINE void clear_ram_image();
   INLINE void set_keep_ram_image(bool keep_ram_image);
   virtual bool get_keep_ram_image() const;
@@ -410,6 +411,7 @@ PUBLISHED:
   INLINE int get_pad_x_size() const;
   INLINE int get_pad_y_size() const;
   INLINE int get_pad_z_size() const;
+  INLINE LVecBase2f get_tex_scale() const;
   
   INLINE void set_pad_size(int x=0, int y=0, int z=0);
   void set_size_padded(int x=1, int y=1, int z=1);
@@ -445,15 +447,33 @@ PUBLISHED:
   void consider_rescale(PNMImage &pnmimage);
   static void consider_rescale(PNMImage &pnmimage, const string &name);
 
+  static string format_texture_type(TextureType tt);
+  static TextureType string_texture_type(const string &str);
+
+  static string format_component_type(ComponentType ct);
+  static ComponentType string_component_type(const string &str);
+
+  static string format_format(Format f);
+  static Format string_format(const string &str);
+  
+  static string format_filter_type(FilterType ft);
+  static FilterType string_filter_type(const string &str);
+  
+  static string format_wrap_mode(WrapMode wm);
+  static WrapMode string_wrap_mode(const string &str);
+  
+  static string format_compression_mode(CompressionMode cm);
+  static CompressionMode string_compression_mode(const string &str);
+
+  static string format_quality_level(QualityLevel tql);
+  static QualityLevel string_quality_level(const string &str);
+    
 public:
   void texture_uploaded();
   
   virtual bool has_cull_callback() const;
   virtual bool cull_callback(CullTraverser *trav, const CullTraverserData &data) const;
 
-  static WrapMode string_wrap_mode(const string &string);
-  static FilterType string_filter_type(const string &string);
-
   static PT(Texture) make_texture();
 
 public:
@@ -461,7 +481,8 @@ public:
   static bool has_alpha(Format format);
   static bool has_binary_alpha(Format format);
 
-  static bool adjust_size(int &x_size, int &y_size, const string &name);
+  static bool adjust_size(int &x_size, int &y_size, const string &name,
+                          bool for_padding);
 
 protected:
   virtual void reconsider_dirty();

+ 1 - 1
panda/src/gobj/texturePool.cxx

@@ -1056,7 +1056,7 @@ try_load_cache(PT(Texture) &tex, BamCache *cache, const Filename &filename,
           compressed_cache_record = (tex->get_ram_image_compression() != Texture::CM_off);
           int x_size = tex->get_orig_file_x_size();
           int y_size = tex->get_orig_file_y_size();
-          Texture::adjust_size(x_size, y_size, filename.get_basename());
+          Texture::adjust_size(x_size, y_size, filename.get_basename(), true);
 
           if (!cache->get_cache_textures() && !compressed_cache_record) {
             // We're not supposed to be caching uncompressed textures.

+ 0 - 27
panda/src/gobj/videoTexture.I

@@ -39,33 +39,6 @@ get_video_height() const {
   return _video_height;
 }
 
-////////////////////////////////////////////////////////////////////
-//     Function: VideoTexture::get_tex_scale
-//       Access: Published
-//  Description: Returns a scale pair that is suitable for applying to
-//               geometry via NodePath::set_tex_scale(), which will
-//               convert texture coordinates on the geometry from the
-//               range 0..1 into the appropriate range to render the
-//               video part of the texture.
-//
-//               This is necessary in the event the video source is
-//               not a power of two and set_power_2() is true.  In
-//               this case, the video image will be mapped to the
-//               lower-left corner of the texture, and the rest of the
-//               texture space will be unused; so we will need to
-//               remap any texture coordinates to fill the space
-//               correctly.
-////////////////////////////////////////////////////////////////////
-INLINE LVecBase2f VideoTexture::
-get_tex_scale() const {
-  if (_video_width == 0 || _video_height == 0 ||
-      _x_size == 0 || _y_size == 0) {
-    LVecBase2f(1.0f, 1.0f);
-  }
-  return LVecBase2f((float)_video_width / _x_size,
-                    (float)_video_height / _y_size);
-}
-
 ////////////////////////////////////////////////////////////////////
 //     Function: VideoTexture::clear_current_frame
 //       Access: Protected

+ 3 - 0
panda/src/gobj/videoTexture.cxx

@@ -118,6 +118,9 @@ void VideoTexture::
 set_video_size(int video_width, int video_height) {
   _video_width = video_width;
   _video_height = video_height;
+  _orig_file_x_size = video_width;
+  _orig_file_y_size = video_height;
+
   do_set_pad_size(max(_x_size - _video_width, 0), 
                   max(_y_size - _video_height, 0),
                   0);

+ 0 - 1
panda/src/gobj/videoTexture.h

@@ -38,7 +38,6 @@ PUBLISHED:
 
   INLINE int get_video_width() const;
   INLINE int get_video_height() const;
-  INLINE LVecBase2f get_tex_scale() const;
 
 public:
   virtual bool has_cull_callback() const;