Browse Source

respect maya's convention of separate textures for color and transparency

David Rose 23 years ago
parent
commit
064955a02b

+ 1 - 0
pandatool/src/maya/Sources.pp

@@ -14,6 +14,7 @@
     config_maya.cxx config_maya.h \
     mayaApi.cxx mayaApi.h \
     mayaShader.cxx mayaShader.h \
+    mayaShaderColorDef.cxx mayaShaderColorDef.h \
     mayaShaders.cxx mayaShaders.h \
     maya_funcs.I maya_funcs.cxx maya_funcs.h \
     post_maya_include.h pre_maya_include.h

+ 75 - 352
pandatool/src/maya/mayaShader.cxx

@@ -20,6 +20,7 @@
 #include "maya_funcs.h"
 #include "config_maya.h"
 #include "string_utils.h"
+#include "pnmImageHeader.h"  // for lumin_red, etc.
 #include "pset.h"
 
 #include "pre_maya_include.h"
@@ -40,35 +41,13 @@
 ////////////////////////////////////////////////////////////////////
 MayaShader::
 MayaShader(MObject engine) {
-  _has_color = false;
-  _transparency = 0.0;
-
-  _has_texture = false;
-  _projection_type = PT_off;
-  _map_uvs = NULL;
-
-  _coverage.set(1.0, 1.0);
-  _translate_frame.set(0.0, 0.0);
-  _rotate_frame = 0.0;
-
-  _mirror = false;
-  _stagger = false;
-  _wrap_u = true;
-  _wrap_v = true;
-
-  _repeat_uv.set(1.0, 1.0);
-  _offset.set(0.0, 0.0);
-  _rotate_uv = 0.0;
-
-  _color_object = (MObject *)NULL;
-
   MFnDependencyNode engine_fn(engine);
 
-  _name = engine_fn.name().asChar();
+  set_name(engine_fn.name().asChar());
 
   if (maya_cat.is_debug()) {
     maya_cat.debug()
-      << "Reading shading engine " << _name << "\n";
+      << "Reading shading engine " << get_name() << "\n";
   }
 
   bool found_shader = false;
@@ -91,108 +70,67 @@ MayaShader(MObject engine) {
 ////////////////////////////////////////////////////////////////////
 MayaShader::
 ~MayaShader() {
-  if (_color_object != (MObject *)NULL) {
-    delete _color_object;
-  }
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::compute_texture_matrix
-//       Access: Public
-//  Description: Returns a texture matrix corresponding to the texture
-//               transforms indicated by the shader.
-////////////////////////////////////////////////////////////////////
-LMatrix3d MayaShader::
-compute_texture_matrix() const {
-  LVector2d scale(_repeat_uv[0] / _coverage[0],
-                  _repeat_uv[1] / _coverage[1]);
-  LVector2d trans(_offset[0] - _translate_frame[0] / _coverage[0],
-                  _offset[1] - _translate_frame[1] / _coverage[1]);
-
-  return
-    (LMatrix3d::translate_mat(LVector2d(-0.5, -0.5)) *
-     LMatrix3d::rotate_mat(_rotate_frame) *
-     LMatrix3d::translate_mat(LVector2d(0.5, 0.5))) *
-    LMatrix3d::scale_mat(scale) *
-    LMatrix3d::translate_mat(trans);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::has_projection
-//       Access: Public
-//  Description: Returns true if the shader has a projection in effect.
-////////////////////////////////////////////////////////////////////
-bool MayaShader::
-has_projection() const {
-  return (_projection_type != PT_off);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::project_uv
+//     Function: MayaShader::output
 //       Access: Public
-//  Description: If the shader has a projection (has_projection()
-//               returns true), this computes the appropriate UV
-//               corresponding to the indicated 3-d point.  Seams that
-//               might be introduced on polygons that cross quadrants
-//               are closed up by ensuring the point is in the same
-//               quadrant as the indicated reference point.
+//  Description: 
 ////////////////////////////////////////////////////////////////////
-TexCoordd MayaShader::
-project_uv(const LPoint3d &pos, const LPoint3d &centroid) const {
-  nassertr(_map_uvs != NULL, TexCoordd::zero());
-  return (this->*_map_uvs)(pos * _projection_matrix, centroid * _projection_matrix);
+void MayaShader::
+output(ostream &out) const {
+  out << "Shader " << get_name();
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::output
+//     Function: MayaShader::write
 //       Access: Public
 //  Description: 
 ////////////////////////////////////////////////////////////////////
 void MayaShader::
-output(ostream &out) const {
-  out << "Shader " << _name << ":\n";
-  if (_has_texture) {
-    out << "  texture is " << _texture << "\n"
-        << "  coverage is " << _coverage << "\n"
-        << "  translate_frame is " << _translate_frame << "\n"
-        << "  rotate_frame is " << _rotate_frame << "\n"
-        << "  mirror is " << _mirror << "\n"
-        << "  stagger is " << _stagger << "\n"
-        << "  wrap_u is " << _wrap_u << "\n"
-        << "  wrap_v is " << _wrap_v << "\n"
-        << "  repeat_uv is " << _repeat_uv << "\n"
-        << "  offset is " << _offset << "\n"
-        << "  rotate_uv is " << _rotate_uv << "\n";
-
-  } else if (_has_color) {
-    out << "  color is " << _color << "\n";
-  }
+write(ostream &out) const {
+  out << "Shader " << get_name() << "\n"
+      << "  color:\n";
+  _color.write(out);
+  out << "  transparency:\n";
+  _transparency.write(out);
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::reset_maya_texture
+//     Function: MayaShader::get_rgba
 //       Access: Public
-//  Description: Changes the texture filename stored in the Maya file
-//               for this particular shader.
+//  Description: Returns the overall color of the shader as a
+//               single-precision rgba value, where the alpha
+//               component represents transparency according to the
+//               Panda convention.  If no overall color is specified
+//               (_has_flat_color is not true), this returns white.
+//
+//               Normally, Maya makes texture color override the flat
+//               color, so if a texture is also applied (_has_texture
+//               is true), this value is not used by Maya.
 ////////////////////////////////////////////////////////////////////
-bool MayaShader::
-reset_maya_texture(const Filename &texture) {
-  if (_color_object != (MObject *)NULL) {
-    _has_texture = set_string_attribute(*_color_object, "fileTextureName", 
-                                        texture);
-    _texture = texture;
+Colorf MayaShader::
+get_rgba() const {
+  Colorf rgba(1.0f, 1.0f, 1.0f, 1.0f);
 
-    if (!_has_texture) {
-      maya_cat.error()
-        << "Unable to reset texture filename.\n";
-    }
+  if (_color._has_flat_color) {
+    rgba[0] = (float)_color._flat_color[0];
+    rgba[1] = (float)_color._flat_color[1];
+    rgba[2] = (float)_color._flat_color[2];
+  }
 
-    return _has_texture;
+  if (_transparency._has_flat_color) {
+    // Maya supports colored transparency, but we only support
+    // grayscale transparency.  Use the pnmimage constants to
+    // convert color to grayscale.
+    double trans =
+      _transparency._flat_color[0] * lumin_red + 
+      _transparency._flat_color[1] * lumin_grn + 
+      _transparency._flat_color[2] * lumin_blu;
+    rgba[3] = 1.0f - (float)trans;
   }
 
-  maya_cat.error()
-    << "Attempt to reset texture on Maya object that has no color set.\n";
-  return false;
+  return rgba;
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -216,25 +154,34 @@ read_surface_shader(MObject shader) {
   // shader says for color.
 
   MPlug color_plug = shader_fn.findPlug("color");
+  if (color_plug.isNull()) {
+    // Or maybe a connection to outColor.  Not sure how this differs
+    // from just color, but empirically it seems that either might be
+    // used.
+    color_plug = shader_fn.findPlug("outColor");
+  }
+    
   if (!color_plug.isNull()) {
     MPlugArray color_pa;
     color_plug.connectedTo(color_pa, true, false);
 
     for (size_t i = 0; i < color_pa.length(); i++) {
-      read_surface_color(color_pa[0].node());
+      _color.read_surface_color(color_pa[0].node());
     }
   }
 
-  // Or maybe a connection to outColor.  Not sure how this differs
-  // from just color, but empirically it seems that either might be
-  // used.
-  MPlug out_color_plug = shader_fn.findPlug("outColor");
-  if (!out_color_plug.isNull()) {
-    MPlugArray color_pa;
-    out_color_plug.connectedTo(color_pa, true, false);
+  // Transparency is stored separately.
+  MPlug trans_plug = shader_fn.findPlug("transparency");
+  if (trans_plug.isNull()) {
+    trans_plug = shader_fn.findPlug("outTransparency");
+  }
+    
+  if (!trans_plug.isNull()) {
+    MPlugArray trans_pa;
+    trans_plug.connectedTo(trans_pa, true, false);
 
-    for (size_t i = 0; i < color_pa.length(); i++) {
-      read_surface_color(color_pa[0].node());
+    for (size_t i = 0; i < trans_pa.length(); i++) {
+      _transparency.read_surface_color(trans_pa[0].node());
     }
   }
 
@@ -244,12 +191,22 @@ read_surface_shader(MObject shader) {
     MFnLambertShader lambert_fn(shader);
     MColor color = lambert_fn.color(&status);
     if (status) {
-      _color.set(color.r, color.g, color.b, color.a);
-      _has_color = true;
+      // Warning! The alpha component of color doesn't mean
+      // transparency in Maya.
+      _color._has_flat_color = true;
+      _color._flat_color.set(color.r, color.g, color.b, color.a);
+      _transparency._flat_color.set(0.0, 0.0, 0.0, 0.0);
+
+      // Get the transparency separately.
+      color = lambert_fn.transparency(&status);
+      if (status) {
+        _transparency._has_flat_color = true;
+        _transparency._flat_color.set(color.r, color.g, color.b, color.a);
+      }
     }
   }
 
-  if (!_has_color && !_has_texture) {
+  if (!_color._has_flat_color && !_color._has_texture) {
     if (maya_cat.is_spam()) {
       maya_cat.spam()
         << "  Color definition not found.\n";
@@ -257,237 +214,3 @@ read_surface_shader(MObject shader) {
   }
   return true;
 }
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::read_surface_color
-//       Access: Private
-//  Description: Determines the surface color specified by the shader.
-//               This includes texturing and other advanced shader
-//               properties.
-////////////////////////////////////////////////////////////////////
-void MayaShader::
-read_surface_color(MObject color) {
-  if (color.hasFn(MFn::kFileTexture)) {
-    _color_object = new MObject(color);
-    string filename;
-    _has_texture = get_string_attribute(color, "fileTextureName", filename);
-    if (_has_texture) {
-      _texture = Filename::from_os_specific(filename);
-    }
-
-    get_vec2f_attribute(color, "coverage", _coverage);
-    get_vec2f_attribute(color, "translateFrame", _translate_frame);
-    get_angle_attribute(color, "rotateFrame", _rotate_frame);
-
-    get_bool_attribute(color, "mirror", _mirror);
-    get_bool_attribute(color, "stagger", _stagger);
-    get_bool_attribute(color, "wrapU", _wrap_u);
-    get_bool_attribute(color, "wrapV", _wrap_v);
-
-    get_vec2f_attribute(color, "repeatUV", _repeat_uv);
-    get_vec2f_attribute(color, "offset", _offset);
-    get_angle_attribute(color, "rotateUV", _rotate_uv);
-
-  } else if (color.hasFn(MFn::kProjection)) {
-    // This is a projected texture.  We will have to step one level
-    // deeper to find the actual texture.
-    MFnDependencyNode projection_fn(color);
-    MPlug image_plug = projection_fn.findPlug("image");
-    if (!image_plug.isNull()) {
-      MPlugArray image_pa;
-      image_plug.connectedTo(image_pa, true, false);
-      
-      for (size_t i = 0; i < image_pa.length(); i++) {
-        read_surface_color(image_pa[0].node());
-      }
-    }
-
-    if (!get_mat4d_attribute(color, "placementMatrix", _projection_matrix)) {
-      _projection_matrix = LMatrix4d::ident_mat();
-    }
-
-    // The uAngle and vAngle might be used for certain kinds of
-    // projections.
-    if (!get_angle_attribute(color, "uAngle", _u_angle)) {
-      _u_angle = 360.0;
-    }
-    if (!get_angle_attribute(color, "vAngle", _v_angle)) {
-      _v_angle = 180.0;
-    }
-
-    string type;
-    if (get_enum_attribute(color, "projType", type)) {
-      set_projection_type(type);
-    }
-
-  } else {
-    // This shader wasn't understood.
-    if (maya_cat.is_debug()) {
-      maya_cat.info()
-        << "**Don't know how to interpret color attribute type "
-        << color.apiTypeStr() << "\n";
-
-    } else {
-      // If we don't have a heavy verbose count, only report each type
-      // of unsupportted shader once.
-      static pset<MFn::Type> bad_types;
-      if (bad_types.insert(color.apiType()).second) {
-        maya_cat.info()
-          << "**Don't know how to interpret color attribute type "
-          << color.apiTypeStr() << "\n";
-      }
-    }
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::set_projection_type
-//       Access: Private
-//  Description: Sets up the shader to apply UV's according to the
-//               indicated projection type.
-////////////////////////////////////////////////////////////////////
-void MayaShader::
-set_projection_type(const string &type) {
-  if (cmp_nocase(type, "planar") == 0) {
-    _projection_type = PT_planar;
-    _map_uvs = &MayaShader::map_planar;
-
-    // The Planar projection normally projects to a range (-1, 1) in
-    // both axes.  Scale this into our UV range of (0, 1).
-    _projection_matrix = _projection_matrix * LMatrix4d(0.5, 0.0, 0.0, 0.0,
-                                                        0.0, 0.5, 0.0, 0.0,
-                                                        0.0, 0.0, 1.0, 0.0,
-                                                        0.5, 0.5, 0.0, 1.0);
-
-  } else if (cmp_nocase(type, "cylindrical") == 0) {
-    _projection_type = PT_cylindrical;
-    _map_uvs = &MayaShader::map_cylindrical;
-
-    // The cylindrical projection is orthographic in the Y axis; scale
-    // the range (-1, 1) in this axis into our UV range (0, 1).
-    _projection_matrix = _projection_matrix * LMatrix4d(1.0, 0.0, 0.0, 0.0,
-                                                        0.0, 0.5, 0.0, 0.0,
-                                                        0.0, 0.0, 1.0, 0.0,
-                                                        0.0, 0.5, 0.0, 1.0);
-
-  } else if (cmp_nocase(type, "spherical") == 0) {
-    _projection_type = PT_spherical;
-    _map_uvs = &MayaShader::map_spherical;
-
-  } else {
-    // Other projection types are currently unimplemented by the
-    // converter.
-    maya_cat.error()
-      << "Don't know how to handle type " << type << " projections.\n";
-    _projection_type = PT_off;
-    _map_uvs = NULL;
-  }
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::map_planar
-//       Access: Private
-//  Description: Computes a UV based on the given point in space,
-//               using a planar projection.
-////////////////////////////////////////////////////////////////////
-LPoint2d MayaShader::
-map_planar(const LPoint3d &pos, const LPoint3d &) const {
-  // A planar projection is about as easy as can be.  We ignore the Z
-  // axis, and project the point into the XY plane.  Done.
-  return LPoint2d(pos[0], pos[1]);
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::map_spherical
-//       Access: Private
-//  Description: Computes a UV based on the given point in space,
-//               using a spherical projection.
-////////////////////////////////////////////////////////////////////
-LPoint2d MayaShader::
-map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const {
-  // To compute the x position on the frame, we only need to consider
-  // the angle of the vector about the Y axis.  Project the vector
-  // into the XZ plane to do this.
-
-  LVector2d xz(pos[0], pos[2]);
-  double xz_length = xz.length();
-
-  if (xz_length < 0.01) {
-    // If we have a point on or near either pole, we've got problems.
-    // This point maps to the entire bottom edge of the image, so
-    // which U value should we choose?  It does make a difference,
-    // especially if we have a number of polygons around the south
-    // pole that all share the common vertex.
-
-    // We choose the U value based on the polygon's centroid.
-    xz.set(centroid[0], centroid[2]);
-  }
-
-  // Now, if the polygon crosses the seam, we also have problems.
-  // Make sure that the u value is in the same half of the texture as
-  // the centroid's u value.
-  double u = rad_2_deg(atan2(xz[0], xz[1])) / (2.0 * _u_angle);
-  double c = rad_2_deg(atan2(centroid[0], centroid[2])) / (2.0 * _u_angle);
-
-  if (u - c > 0.5) {
-    u -= floor(u - c + 0.5);
-  } else if (u - c < -0.5) {
-    u += floor(c - u + 0.5);
-  }
-
-  // Now rotate the vector into the YZ plane, and the V value is based
-  // on the latitude: the angle about the X axis.
-  LVector2d yz(pos[1], xz_length);
-  double v = rad_2_deg(atan2(yz[0], yz[1])) / (2.0 * _v_angle);
-
-  LPoint2d uv(u - 0.5, v - 0.5);
-
-  nassertr(fabs(u - c) <= 0.5, uv);
-  return uv;
-}
-
-////////////////////////////////////////////////////////////////////
-//     Function: MayaShader::map_cylindrical
-//       Access: Private
-//  Description: Computes a UV based on the given point in space,
-//               using a cylindrical projection.
-////////////////////////////////////////////////////////////////////
-LPoint2d MayaShader::
-map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const {
-  // This is almost identical to the spherical projection, except for
-  // the computation of V.
-
-  LVector2d xz(pos[0], pos[2]);
-  double xz_length = xz.length();
-
-  if (xz_length < 0.01) {
-    // A cylindrical mapping has the same singularity problem at the
-    // pole as a spherical mapping does: points at the pole do not map
-    // to a single point on the texture.  (It's technically a slightly
-    // different problem: in a cylindrical mapping, points at the pole
-    // do not map to any point on the texture, while in a spherical
-    // mapping, points at the pole map to the top or bottom edge of
-    // the texture.  But this is a technicality that doesn't really
-    // apply to us.)  We still solve it the same way: if our point is
-    // at or near the pole, compute the angle based on the centroid of
-    // the polygon (which we assume is further from the pole).
-    xz.set(centroid[0], centroid[2]);
-  }
-
-  // And cylinders do still have a seam at the back.
-  double u = rad_2_deg(atan2(xz[0], xz[1])) / _u_angle;
-  double c = rad_2_deg(atan2(centroid[0], centroid[2])) / _u_angle;
-
-  if (u - c > 0.5) {
-    u -= floor(u - c + 0.5);
-  } else if (u - c < -0.5) {
-    u += floor(c - u + 0.5);
-  }
-
-  // For a cylindrical mapping, the V value comes directly from Y.
-  // Easy.
-  LPoint2d uv(u - 0.5, pos[1]);
-
-  nassertr(fabs(u - c) <= 0.5, uv);
-  return uv;
-}

+ 7 - 53
pandatool/src/maya/mayaShader.h

@@ -20,9 +20,11 @@
 #define MAYASHADER_H
 
 #include "pandatoolbase.h"
+#include "mayaShaderColorDef.h"
 
 #include "luse.h"
 #include "lmatrix.h"
+#include "namable.h"
 
 class MObject;
 
@@ -34,69 +36,21 @@ class MObject;
 //               that we don't care about or don't know enough to
 //               extract.
 ////////////////////////////////////////////////////////////////////
-class MayaShader {
+class MayaShader : public Namable {
 public:
   MayaShader(MObject engine);
   ~MayaShader();
 
-  LMatrix3d compute_texture_matrix() const;
-  bool has_projection() const;
-  TexCoordd project_uv(const LPoint3d &pos, const LPoint3d &ref_point) const;
-
   void output(ostream &out) const;
-  bool reset_maya_texture(const Filename &texture);
-
-  string _name;
-
-  bool _has_color;
-  Colord _color;
-  double _transparency;
-
-  bool _has_texture;
-  Filename _texture;
-
-  enum ProjectionType {
-    PT_off,
-    PT_planar,
-    PT_spherical,
-    PT_cylindrical,
-    PT_ball,
-    PT_cubic,
-    PT_triplanar,
-    PT_concentric,
-    PT_perspective,
-  };
-  ProjectionType _projection_type;
-  LMatrix4d _projection_matrix;
-  double _u_angle;
-  double _v_angle;
+  void write(ostream &out) const;
 
-  LVector2f _coverage;
-  LVector2f _translate_frame;
-  double _rotate_frame;
+  Colorf get_rgba() const;
 
-  bool _mirror;
-  bool _stagger;
-  bool _wrap_u;
-  bool _wrap_v;
-
-  LVector2f _repeat_uv;
-  LVector2f _offset;
-  double _rotate_uv;
+  MayaShaderColorDef _color;
+  MayaShaderColorDef _transparency;
 
 private:
-  MObject *_color_object;
-
   bool read_surface_shader(MObject shader);
-  void read_surface_color(MObject color);
-  void set_projection_type(const string &type);
-
-  LPoint2d map_planar(const LPoint3d &pos, const LPoint3d &centroid) const;
-  LPoint2d map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const;
-  LPoint2d map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const;
-
-  // Define a pointer to one of the above member functions.
-  LPoint2d (MayaShader::*_map_uvs)(const LPoint3d &pos, const LPoint3d &centroid) const;
 };
 
 INLINE ostream &operator << (ostream &out, const MayaShader &shader) {

+ 405 - 0
pandatool/src/maya/mayaShaderColorDef.cxx

@@ -0,0 +1,405 @@
+// Filename: mayaShaderColorDef.cxx
+// Created by:  drose (12Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#include "mayaShaderColorDef.h"
+#include "maya_funcs.h"
+#include "config_maya.h"
+#include "string_utils.h"
+#include "pset.h"
+
+#include "pre_maya_include.h"
+#include <maya/MFnDependencyNode.h>
+#include <maya/MPlug.h>
+#include <maya/MPlugArray.h>
+#include <maya/MObject.h>
+#include <maya/MStatus.h>
+#include "post_maya_include.h"
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::Constructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MayaShaderColorDef::
+MayaShaderColorDef() {
+  _has_flat_color = false;
+  _flat_color.set(0.0, 0.0, 0.0, 0.0);
+
+  _has_texture = false;
+  _projection_type = PT_off;
+  _map_uvs = NULL;
+
+  _coverage.set(1.0, 1.0);
+  _translate_frame.set(0.0, 0.0);
+  _rotate_frame = 0.0;
+
+  _mirror = false;
+  _stagger = false;
+  _wrap_u = true;
+  _wrap_v = true;
+
+  _repeat_uv.set(1.0, 1.0);
+  _offset.set(0.0, 0.0);
+  _rotate_uv = 0.0;
+
+  _color_object = (MObject *)NULL;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::Destructor
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+MayaShaderColorDef::
+~MayaShaderColorDef() {
+  if (_color_object != (MObject *)NULL) {
+    delete _color_object;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::compute_texture_matrix
+//       Access: Public
+//  Description: Returns a texture matrix corresponding to the texture
+//               transforms indicated by the shader.
+////////////////////////////////////////////////////////////////////
+LMatrix3d MayaShaderColorDef::
+compute_texture_matrix() const {
+  LVector2d scale(_repeat_uv[0] / _coverage[0],
+                  _repeat_uv[1] / _coverage[1]);
+  LVector2d trans(_offset[0] - _translate_frame[0] / _coverage[0],
+                  _offset[1] - _translate_frame[1] / _coverage[1]);
+
+  return
+    (LMatrix3d::translate_mat(LVector2d(-0.5, -0.5)) *
+     LMatrix3d::rotate_mat(_rotate_frame) *
+     LMatrix3d::translate_mat(LVector2d(0.5, 0.5))) *
+    LMatrix3d::scale_mat(scale) *
+    LMatrix3d::translate_mat(trans);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::has_projection
+//       Access: Public
+//  Description: Returns true if the shader has a projection in effect.
+////////////////////////////////////////////////////////////////////
+bool MayaShaderColorDef::
+has_projection() const {
+  return (_projection_type != PT_off);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::project_uv
+//       Access: Public
+//  Description: If the shader has a projection (has_projection()
+//               returns true), this computes the appropriate UV
+//               corresponding to the indicated 3-d point.  Seams that
+//               might be introduced on polygons that cross quadrants
+//               are closed up by ensuring the point is in the same
+//               quadrant as the indicated reference point.
+////////////////////////////////////////////////////////////////////
+TexCoordd MayaShaderColorDef::
+project_uv(const LPoint3d &pos, const LPoint3d &centroid) const {
+  nassertr(_map_uvs != NULL, TexCoordd::zero());
+  return (this->*_map_uvs)(pos * _projection_matrix, centroid * _projection_matrix);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::write
+//       Access: Public
+//  Description: 
+////////////////////////////////////////////////////////////////////
+void MayaShaderColorDef::
+write(ostream &out) const {
+  if (_has_texture) {
+    out << "    texture is " << _texture << "\n"
+        << "    coverage is " << _coverage << "\n"
+        << "    translate_frame is " << _translate_frame << "\n"
+        << "    rotate_frame is " << _rotate_frame << "\n"
+        << "    mirror is " << _mirror << "\n"
+        << "    stagger is " << _stagger << "\n"
+        << "    wrap_u is " << _wrap_u << "\n"
+        << "    wrap_v is " << _wrap_v << "\n"
+        << "    repeat_uv is " << _repeat_uv << "\n"
+        << "    offset is " << _offset << "\n"
+        << "    rotate_uv is " << _rotate_uv << "\n";
+
+  } else if (_has_flat_color) {
+    out << "    flat color is " << _flat_color << "\n";
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::reset_maya_texture
+//       Access: Public
+//  Description: Changes the texture filename stored in the Maya file
+//               for this particular shader.
+////////////////////////////////////////////////////////////////////
+bool MayaShaderColorDef::
+reset_maya_texture(const Filename &texture) {
+  if (_color_object != (MObject *)NULL) {
+    _has_texture = set_string_attribute(*_color_object, "fileTextureName", 
+                                        texture);
+    _texture = texture;
+
+    if (!_has_texture) {
+      maya_cat.error()
+        << "Unable to reset texture filename.\n";
+    }
+
+    return _has_texture;
+  }
+
+  maya_cat.error()
+    << "Attempt to reset texture on Maya object that has no color set.\n";
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::read_surface_color
+//       Access: Private
+//  Description: Determines the surface color specified by the shader.
+//               This includes texturing and other advanced shader
+//               properties.
+////////////////////////////////////////////////////////////////////
+void MayaShaderColorDef::
+read_surface_color(MObject color) {
+  if (color.hasFn(MFn::kFileTexture)) {
+    _color_object = new MObject(color);
+    string filename;
+    _has_texture = get_string_attribute(color, "fileTextureName", filename);
+    if (_has_texture) {
+      _texture = Filename::from_os_specific(filename);
+    }
+
+    get_vec2f_attribute(color, "coverage", _coverage);
+    get_vec2f_attribute(color, "translateFrame", _translate_frame);
+    get_angle_attribute(color, "rotateFrame", _rotate_frame);
+
+    get_bool_attribute(color, "mirror", _mirror);
+    get_bool_attribute(color, "stagger", _stagger);
+    get_bool_attribute(color, "wrapU", _wrap_u);
+    get_bool_attribute(color, "wrapV", _wrap_v);
+
+    get_vec2f_attribute(color, "repeatUV", _repeat_uv);
+    get_vec2f_attribute(color, "offset", _offset);
+    get_angle_attribute(color, "rotateUV", _rotate_uv);
+
+  } else if (color.hasFn(MFn::kProjection)) {
+    // This is a projected texture.  We will have to step one level
+    // deeper to find the actual texture.
+    MFnDependencyNode projection_fn(color);
+    MPlug image_plug = projection_fn.findPlug("image");
+    if (!image_plug.isNull()) {
+      MPlugArray image_pa;
+      image_plug.connectedTo(image_pa, true, false);
+      
+      for (size_t i = 0; i < image_pa.length(); i++) {
+        read_surface_color(image_pa[0].node());
+      }
+    }
+
+    if (!get_mat4d_attribute(color, "placementMatrix", _projection_matrix)) {
+      _projection_matrix = LMatrix4d::ident_mat();
+    }
+
+    // The uAngle and vAngle might be used for certain kinds of
+    // projections.
+    if (!get_angle_attribute(color, "uAngle", _u_angle)) {
+      _u_angle = 360.0;
+    }
+    if (!get_angle_attribute(color, "vAngle", _v_angle)) {
+      _v_angle = 180.0;
+    }
+
+    string type;
+    if (get_enum_attribute(color, "projType", type)) {
+      set_projection_type(type);
+    }
+
+  } else {
+    // This shader wasn't understood.
+    if (maya_cat.is_debug()) {
+      maya_cat.info()
+        << "**Don't know how to interpret color attribute type "
+        << color.apiTypeStr() << "\n";
+
+    } else {
+      // If we don't have a heavy verbose count, only report each type
+      // of unsupported shader once.
+      static pset<MFn::Type> bad_types;
+      if (bad_types.insert(color.apiType()).second) {
+        maya_cat.info()
+          << "**Don't know how to interpret color attribute type "
+          << color.apiTypeStr() << "\n";
+      }
+    }
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::set_projection_type
+//       Access: Private
+//  Description: Sets up the shader to apply UV's according to the
+//               indicated projection type.
+////////////////////////////////////////////////////////////////////
+void MayaShaderColorDef::
+set_projection_type(const string &type) {
+  if (cmp_nocase(type, "planar") == 0) {
+    _projection_type = PT_planar;
+    _map_uvs = &MayaShaderColorDef::map_planar;
+
+    // The Planar projection normally projects to a range (-1, 1) in
+    // both axes.  Scale this into our UV range of (0, 1).
+    _projection_matrix = _projection_matrix * LMatrix4d(0.5, 0.0, 0.0, 0.0,
+                                                        0.0, 0.5, 0.0, 0.0,
+                                                        0.0, 0.0, 1.0, 0.0,
+                                                        0.5, 0.5, 0.0, 1.0);
+
+  } else if (cmp_nocase(type, "cylindrical") == 0) {
+    _projection_type = PT_cylindrical;
+    _map_uvs = &MayaShaderColorDef::map_cylindrical;
+
+    // The cylindrical projection is orthographic in the Y axis; scale
+    // the range (-1, 1) in this axis into our UV range (0, 1).
+    _projection_matrix = _projection_matrix * LMatrix4d(1.0, 0.0, 0.0, 0.0,
+                                                        0.0, 0.5, 0.0, 0.0,
+                                                        0.0, 0.0, 1.0, 0.0,
+                                                        0.0, 0.5, 0.0, 1.0);
+
+  } else if (cmp_nocase(type, "spherical") == 0) {
+    _projection_type = PT_spherical;
+    _map_uvs = &MayaShaderColorDef::map_spherical;
+
+  } else {
+    // Other projection types are currently unimplemented by the
+    // converter.
+    maya_cat.error()
+      << "Don't know how to handle type " << type << " projections.\n";
+    _projection_type = PT_off;
+    _map_uvs = NULL;
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::map_planar
+//       Access: Private
+//  Description: Computes a UV based on the given point in space,
+//               using a planar projection.
+////////////////////////////////////////////////////////////////////
+LPoint2d MayaShaderColorDef::
+map_planar(const LPoint3d &pos, const LPoint3d &) const {
+  // A planar projection is about as easy as can be.  We ignore the Z
+  // axis, and project the point into the XY plane.  Done.
+  return LPoint2d(pos[0], pos[1]);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::map_spherical
+//       Access: Private
+//  Description: Computes a UV based on the given point in space,
+//               using a spherical projection.
+////////////////////////////////////////////////////////////////////
+LPoint2d MayaShaderColorDef::
+map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const {
+  // To compute the x position on the frame, we only need to consider
+  // the angle of the vector about the Y axis.  Project the vector
+  // into the XZ plane to do this.
+
+  LVector2d xz(pos[0], pos[2]);
+  double xz_length = xz.length();
+
+  if (xz_length < 0.01) {
+    // If we have a point on or near either pole, we've got problems.
+    // This point maps to the entire bottom edge of the image, so
+    // which U value should we choose?  It does make a difference,
+    // especially if we have a number of polygons around the south
+    // pole that all share the common vertex.
+
+    // We choose the U value based on the polygon's centroid.
+    xz.set(centroid[0], centroid[2]);
+  }
+
+  // Now, if the polygon crosses the seam, we also have problems.
+  // Make sure that the u value is in the same half of the texture as
+  // the centroid's u value.
+  double u = rad_2_deg(atan2(xz[0], xz[1])) / (2.0 * _u_angle);
+  double c = rad_2_deg(atan2(centroid[0], centroid[2])) / (2.0 * _u_angle);
+
+  if (u - c > 0.5) {
+    u -= floor(u - c + 0.5);
+  } else if (u - c < -0.5) {
+    u += floor(c - u + 0.5);
+  }
+
+  // Now rotate the vector into the YZ plane, and the V value is based
+  // on the latitude: the angle about the X axis.
+  LVector2d yz(pos[1], xz_length);
+  double v = rad_2_deg(atan2(yz[0], yz[1])) / (2.0 * _v_angle);
+
+  LPoint2d uv(u - 0.5, v - 0.5);
+
+  nassertr(fabs(u - c) <= 0.5, uv);
+  return uv;
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShaderColorDef::map_cylindrical
+//       Access: Private
+//  Description: Computes a UV based on the given point in space,
+//               using a cylindrical projection.
+////////////////////////////////////////////////////////////////////
+LPoint2d MayaShaderColorDef::
+map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const {
+  // This is almost identical to the spherical projection, except for
+  // the computation of V.
+
+  LVector2d xz(pos[0], pos[2]);
+  double xz_length = xz.length();
+
+  if (xz_length < 0.01) {
+    // A cylindrical mapping has the same singularity problem at the
+    // pole as a spherical mapping does: points at the pole do not map
+    // to a single point on the texture.  (It's technically a slightly
+    // different problem: in a cylindrical mapping, points at the pole
+    // do not map to any point on the texture, while in a spherical
+    // mapping, points at the pole map to the top or bottom edge of
+    // the texture.  But this is a technicality that doesn't really
+    // apply to us.)  We still solve it the same way: if our point is
+    // at or near the pole, compute the angle based on the centroid of
+    // the polygon (which we assume is further from the pole).
+    xz.set(centroid[0], centroid[2]);
+  }
+
+  // And cylinders do still have a seam at the back.
+  double u = rad_2_deg(atan2(xz[0], xz[1])) / _u_angle;
+  double c = rad_2_deg(atan2(centroid[0], centroid[2])) / _u_angle;
+
+  if (u - c > 0.5) {
+    u -= floor(u - c + 0.5);
+  } else if (u - c < -0.5) {
+    u += floor(c - u + 0.5);
+  }
+
+  // For a cylindrical mapping, the V value comes directly from Y.
+  // Easy.
+  LPoint2d uv(u - 0.5, pos[1]);
+
+  nassertr(fabs(u - c) <= 0.5, uv);
+  return uv;
+}

+ 100 - 0
pandatool/src/maya/mayaShaderColorDef.h

@@ -0,0 +1,100 @@
+// Filename: mayaShaderColorDef.h
+// Created by:  drose (12Apr03)
+//
+////////////////////////////////////////////////////////////////////
+//
+// PANDA 3D SOFTWARE
+// Copyright (c) 2001, 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://www.panda3d.org/license.txt .
+//
+// To contact the maintainers of this program write to
+// [email protected] .
+//
+////////////////////////////////////////////////////////////////////
+
+#ifndef MAYASHADERCOLORDEF_H
+#define MAYASHADERCOLORDEF_H
+
+#include "pandatoolbase.h"
+
+#include "luse.h"
+#include "lmatrix.h"
+
+class MObject;
+
+////////////////////////////////////////////////////////////////////
+//       Class : MayaShaderColorDef
+// Description : This defines the various attributes that Maya may
+//               associate with the "color" channel for a particular
+//               shader (as well as on the "transparency" channel).
+////////////////////////////////////////////////////////////////////
+class MayaShaderColorDef {
+public:
+  MayaShaderColorDef();
+  ~MayaShaderColorDef();
+  
+  LMatrix3d compute_texture_matrix() const;
+  bool has_projection() const;
+  TexCoordd project_uv(const LPoint3d &pos, const LPoint3d &ref_point) const;
+  bool reset_maya_texture(const Filename &texture);
+  
+  void write(ostream &out) const;
+
+  enum ProjectionType {
+    PT_off,
+    PT_planar,
+    PT_spherical,
+    PT_cylindrical,
+    PT_ball,
+    PT_cubic,
+    PT_triplanar,
+    PT_concentric,
+    PT_perspective,
+  };
+
+  bool _has_texture;
+  Filename _texture;
+  
+  bool _has_flat_color;
+  Colord _flat_color;
+  
+  ProjectionType _projection_type;
+  LMatrix4d _projection_matrix;
+  double _u_angle;
+  double _v_angle;
+    
+  LVector2f _coverage;
+  LVector2f _translate_frame;
+  double _rotate_frame;
+  
+  bool _mirror;
+  bool _stagger;
+  bool _wrap_u;
+  bool _wrap_v;
+  
+  LVector2f _repeat_uv;
+  LVector2f _offset;
+  double _rotate_uv;
+  
+private:
+  void read_surface_color(MObject color);
+  void set_projection_type(const string &type);
+
+  LPoint2d map_planar(const LPoint3d &pos, const LPoint3d &centroid) const;
+  LPoint2d map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const;
+  LPoint2d map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const;
+
+  // Define a pointer to one of the above member functions.
+  LPoint2d (MayaShaderColorDef::*_map_uvs)(const LPoint3d &pos, const LPoint3d &centroid) const;
+  
+  MObject *_color_object;
+  
+  friend class MayaShader;
+};
+
+#endif
+

+ 153 - 32
pandatool/src/mayaegg/mayaToEggConverter.cxx

@@ -1388,11 +1388,13 @@ make_polyset(const MDagPath &dag_path, const MFnMesh &mesh,
       shader = default_shader;
     }
 
+    const MayaShaderColorDef &color_def = shader->_color;
+
     // Since a texture completely replaces a polygon or vertex color,
     // we need to know up front whether we have a texture.
     bool has_texture = false;
     if (shader != (MayaShader *)NULL) {
-      has_texture = shader->_has_texture;
+      has_texture = color_def._has_texture;
     }
 
     // Get the vertices for the polygon.
@@ -1400,7 +1402,7 @@ make_polyset(const MDagPath &dag_path, const MFnMesh &mesh,
     long i;
     LPoint3d centroid(0.0, 0.0, 0.0);
 
-    if (shader != (MayaShader *)NULL && shader->has_projection()) {
+    if (shader != (MayaShader *)NULL && color_def.has_projection()) {
       // If the shader has a projection, we may need to compute the
       // polygon's centroid to avoid seams at the edges.
       for (i = 0; i < num_verts; i++) {
@@ -1430,10 +1432,10 @@ make_polyset(const MDagPath &dag_path, const MFnMesh &mesh,
         vert.set_normal(n3d);
       }
 
-      if (shader != (MayaShader *)NULL && shader->has_projection()) {
+      if (shader != (MayaShader *)NULL && color_def.has_projection()) {
         // If the shader has a projection, use it instead of the
         // polygon's built-in UV's.
-        vert.set_uv(shader->project_uv(p3d, centroid));
+        vert.set_uv(color_def.project_uv(p3d, centroid));
 
       } else if (pi.hasUVs()) {
         // Get the UV's from the polygon.
@@ -1778,7 +1780,7 @@ get_egg_table(const MDagPath &dag_path, EggGroupNode *egg_root) {
 
 ////////////////////////////////////////////////////////////////////
 //     Function: MayaShader::set_shader_attributes
-//       Access: Public
+//       Access: Private
 //  Description: Applies the known shader attributes to the indicated
 //               egg primitive.
 ////////////////////////////////////////////////////////////////////
@@ -1786,40 +1788,162 @@ void MayaToEggConverter::
 set_shader_attributes(EggPrimitive &primitive, const MayaShader &shader) {
   // In Maya, a polygon is either textured or colored.  The texture,
   // if present, replaces the color.
+  const MayaShaderColorDef &color_def = shader._color;
+  const MayaShaderColorDef &trans_def = shader._transparency;
+  if (color_def._has_texture || trans_def._has_texture) {
+    EggTexture tex(shader.get_name(), "");
+
+    if (color_def._has_texture) {
+      // If we have a texture on color, apply it as the filename.
+      Filename filename = Filename::from_os_specific(color_def._texture);
+      Filename fullpath = 
+        _path_replace->match_path(filename, get_texture_path());
+      tex.set_filename(_path_replace->store_path(fullpath));
+      tex.set_fullpath(fullpath);
+      apply_texture_properties(tex, color_def);
+
+      // If we also have a texture on transparency, apply it as the
+      // alpha filename.
+      if (trans_def._has_texture) {
+        if (color_def._wrap_u != trans_def._wrap_u ||
+            color_def._wrap_u != trans_def._wrap_u) {
+          mayaegg_cat.warning()
+            << "Shader " << shader.get_name()
+            << " has contradictory wrap modes on color and texture.\n";
+        }
+            
+        filename = Filename::from_os_specific(trans_def._texture);
+        fullpath = _path_replace->match_path(filename, get_texture_path());
+        tex.set_alpha_filename(_path_replace->store_path(fullpath));
+        tex.set_alpha_fullpath(fullpath);
+        tex.set_format(EggTexture::F_rgba);
+
+        if (!compare_texture_properties(tex, trans_def)) {
+          // Only report each broken shader once.
+          static pset<string> bad_shaders;
+          if (bad_shaders.insert(shader.get_name()).second) {
+            mayaegg_cat.error()
+              << "Color and transparency texture properties differ on shader "
+              << shader.get_name() << "\n";
+          }
+        }
 
-  if (shader._has_texture) {
-    Filename filename = Filename::from_os_specific(shader._texture);
-    Filename fullpath = 
-      _path_replace->match_path(filename, get_texture_path());
-    EggTexture tex(shader._name, _path_replace->store_path(fullpath));
-    tex.set_fullpath(fullpath);
-
-    tex.set_wrap_u(shader._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp);
-    tex.set_wrap_v(shader._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp);
- 
-    // Let's mipmap all textures by default.
-    tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
-    tex.set_magfilter(EggTexture::FT_linear);
-
-    LMatrix3d mat = shader.compute_texture_matrix();
-    if (!mat.almost_equal(LMatrix3d::ident_mat())) {
-      tex.set_transform(mat);
-    }
+      } else {
+        // Otherwise, we don't have any transparency, so tell the egg
+        // format to ignore any alpha channel that might be on the
+        // color texture.
+        tex.set_format(EggTexture::F_rgb);
+      }
 
+    } else {  // trans_def._has_texture
+      // We have a texture on transparency only.  Apply it as the
+      // primary filename, and set the format accordingly.
+      Filename filename = Filename::from_os_specific(trans_def._texture);
+      Filename fullpath = 
+        _path_replace->match_path(filename, get_texture_path());
+      tex.set_filename(_path_replace->store_path(fullpath));
+      tex.set_fullpath(fullpath);
+      tex.set_format(EggTexture::F_alpha);
+      apply_texture_properties(tex, trans_def);
+    }
+  
     EggTexture *new_tex =
       _textures.create_unique_texture(tex, ~EggTexture::E_tref_name);
-
+    
     primitive.set_texture(new_tex);
 
-  } else if (shader._has_color) {
-    primitive.set_color(Colorf(shader._color[0], shader._color[1], 
-                               shader._color[2], 1.0f));
   }
+
+  // Also apply an overall color to the primitive.
+  Colorf rgba = shader.get_rgba();
+
+  // This is a placeholder for a parameter on the shader or group that
+  // we have yet to define.
+  static const bool modulate = false;
+  
+  if (!modulate) {
+    // If modulate is not specified, the existence of a texture on
+    // either color channel completely replaces the flat color.
+    if (color_def._has_texture) {
+      rgba[0] = 1.0f;
+      rgba[1] = 1.0f;
+      rgba[2] = 1.0f;
+    }
+    if (trans_def._has_texture) {
+      rgba[3] = 1.0f;
+    }
+  }
+
+  primitive.set_color(rgba);
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShader::apply_texture_properties
+//       Access: Private
+//  Description: Applies all the appropriate texture properties to the
+//               EggTexture object, including wrap modes and texture
+//               matrix.
+////////////////////////////////////////////////////////////////////
+void MayaToEggConverter::
+apply_texture_properties(EggTexture &tex, const MayaShaderColorDef &color_def) {
+  // Let's mipmap all textures by default.
+  tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
+  tex.set_magfilter(EggTexture::FT_linear);
+
+  EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
+  EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
+
+  tex.set_wrap_u(wrap_u);
+  tex.set_wrap_v(wrap_v);
+  
+  LMatrix3d mat = color_def.compute_texture_matrix();
+  if (!mat.almost_equal(LMatrix3d::ident_mat())) {
+    tex.set_transform(mat);
+  }
+}
+
+////////////////////////////////////////////////////////////////////
+//     Function: MayaShader::compare_texture_properties
+//       Access: Private
+//  Description: Compares the texture properties already on the
+//               texture (presumably set by a previous call to
+//               apply_texture_properties()) and returns false if they
+//               differ from that specified by the indicated color_def
+//               object, or true if they match.
+////////////////////////////////////////////////////////////////////
+bool MayaToEggConverter::
+compare_texture_properties(EggTexture &tex, 
+                           const MayaShaderColorDef &color_def) {
+  bool okflag = true;
+
+  EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
+  EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
+  
+  if (wrap_u != tex.determine_wrap_u()) {
+    // Choose the more general of the two.
+    if (wrap_u == EggTexture::WM_repeat) {
+      tex.set_wrap_u(wrap_u);
+    }
+    okflag = false;
+  }
+  if (wrap_v != tex.determine_wrap_v()) {
+    if (wrap_v == EggTexture::WM_repeat) {
+      tex.set_wrap_v(wrap_v);
+    }
+    okflag = false;
+  }
+  
+  LMatrix3d mat = color_def.compute_texture_matrix();
+  if (!mat.almost_equal(tex.get_transform())) {
+    okflag = false;
+  }
+
+  return okflag;
 }
 
 ////////////////////////////////////////////////////////////////////
 //     Function: MayaShader::reparent_decals
-//       Access: Public
+//       Access: Private
 //  Description: Recursively walks the egg hierarchy, reparenting
 //               "decal" type nodes below their corresponding
 //               "decalbase" type nodes, and setting the flags.
@@ -1876,13 +2000,10 @@ reparent_decals(EggGroupNode *egg_parent) {
       // All the decal children get moved to be a child of decal base.
       // This usually will not affect the vertex positions, but it
       // could if the decal base has a transform and the decal child
-      // is an instance node.  So don't do that.  Also, we assume it's
-      // undesired to have a transform on a decal, so we flatten those
-      // out here--there's no real requirement to do this, however.
+      // is an instance node.  So don't do that.
       pvector<EggGroup *>::iterator di;
       for (di = decal_children.begin(); di != decal_children.end(); ++di) {
         EggGroup *child_group = (*di);
-        child_group->flatten_transforms();
         decal_base->add_child(child_group);
       }
 

+ 5 - 0
pandatool/src/mayaegg/mayaToEggConverter.h

@@ -39,6 +39,7 @@ class EggVertexPool;
 class EggNurbsCurve;
 class EggPrimitive;
 class EggXfmSAnim;
+class MayaShaderColorDef;
 
 class MDagPath;
 class MFnNurbsSurface;
@@ -119,6 +120,10 @@ private:
   JointAnim *get_egg_table(const string &name, EggGroupNode *egg_root);
   void set_shader_attributes(EggPrimitive &primitive,
                              const MayaShader &shader);
+  void apply_texture_properties(EggTexture &tex, 
+                                const MayaShaderColorDef &color_def);
+  bool compare_texture_properties(EggTexture &tex, 
+                                  const MayaShaderColorDef &color_def);
 
   bool reparent_decals(EggGroupNode *egg_parent);
 

+ 45 - 26
pandatool/src/mayaprogs/mayaCopy.cxx

@@ -167,32 +167,11 @@ copy_maya_file(const Filename &source, const Filename &dest,
   int num_shaders = _shaders.get_num_shaders();
   for (int i = 0; i < num_shaders; i++) {
     MayaShader *shader = _shaders.get_shader(i);
-    if (shader->_has_texture) {
-      Filename texture_filename = 
-        _path_replace->convert_path(shader->_texture);
-      if (!texture_filename.exists()) {
-        nout << "*** Warning: texture " << texture_filename
-             << " does not exist.\n";
-      } else if (!texture_filename.is_regular_file()) {
-        nout << "*** Warning: texture " << texture_filename
-             << " is not a regular file.\n";
-      } else {
-        ExtraData ed;
-        ed._type = FT_texture;
-
-        CVSSourceDirectory *texture_dir =
-          import(texture_filename, &ed, _map_dir);
-        if (texture_dir == (CVSSourceDirectory *)NULL) {
-          return false;
-        }
-
-        // Update the texture reference to point to the new texture
-        // filename, relative to the flt file.  Not sure how to do
-        // this right now.
-        Filename new_filename = dir->get_rel_to(texture_dir) + "/" +
-          texture_filename.get_basename();
-        shader->reset_maya_texture(new_filename);
-      }
+    if (!extract_texture(shader->_color, dir)) {
+      return false;
+    }
+    if (!extract_texture(shader->_transparency, dir)) {
+      return false;
     }
   }
 
@@ -236,6 +215,46 @@ copy_maya_file(const Filename &source, const Filename &dest,
   return true;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: MayaCopy::extract_texture
+//       Access: Private
+//  Description: Gets the texture out of the indicated color channel
+//               and copies it in, updating the channel with the new
+//               texture filename.  Returns true on success, false on
+//               failure.
+////////////////////////////////////////////////////////////////////
+bool MayaCopy::
+extract_texture(MayaShaderColorDef &color_def, CVSSourceDirectory *dir) {
+  if (color_def._has_texture) {
+    Filename texture_filename = 
+      _path_replace->convert_path(color_def._texture);
+    if (!texture_filename.exists()) {
+      nout << "*** Warning: texture " << texture_filename
+           << " does not exist.\n";
+    } else if (!texture_filename.is_regular_file()) {
+      nout << "*** Warning: texture " << texture_filename
+           << " is not a regular file.\n";
+    } else {
+      ExtraData ed;
+      ed._type = FT_texture;
+      
+      CVSSourceDirectory *texture_dir =
+        import(texture_filename, &ed, _map_dir);
+      if (texture_dir == (CVSSourceDirectory *)NULL) {
+        return false;
+      }
+      
+      // Update the texture reference to point to the new texture
+      // filename, relative to the maya file.
+      Filename new_filename = dir->get_rel_to(texture_dir) + "/" +
+        texture_filename.get_basename();
+      color_def.reset_maya_texture(new_filename);
+    }
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: MayaCopy::copy_texture
 //       Access: Private

+ 2 - 0
pandatool/src/mayaprogs/mayaCopy.h

@@ -29,6 +29,7 @@
 #include "pset.h"
 
 class MayaShader;
+class MayaShaderColorDef;
 class MDagPath;
 
 ////////////////////////////////////////////////////////////////////
@@ -63,6 +64,7 @@ private:
 
   bool copy_maya_file(const Filename &source, const Filename &dest,
                      CVSSourceDirectory *dir);
+  bool extract_texture(MayaShaderColorDef &color_def, CVSSourceDirectory *dir);
   bool copy_texture(const Filename &source, const Filename &dest,
                     CVSSourceDirectory *dir);