Panda3D
 All Classes Functions Variables Enumerations
mayaShader.cxx
00001 // Filename: mayaShader.cxx
00002 // Created by:  drose (01Feb00)
00003 // Modified 19Mar10 by ETC PandaSE team (see
00004 //   header comment for mayaToEgg.cxx for details)
00005 //
00006 ////////////////////////////////////////////////////////////////////
00007 //
00008 // PANDA 3D SOFTWARE
00009 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00010 //
00011 // All use of this software is subject to the terms of the revised BSD
00012 // license.  You should have received a copy of this license along
00013 // with this source code in a file named "LICENSE."
00014 //
00015 ////////////////////////////////////////////////////////////////////
00016 
00017 #include "mayaShader.h"
00018 #include "maya_funcs.h"
00019 #include "config_maya.h"
00020 #include "string_utils.h"
00021 #include "pnmImageHeader.h"  // for lumin_red, etc.
00022 #include "pset.h"
00023 
00024 #include "pre_maya_include.h"
00025 #include <maya/MFnDependencyNode.h>
00026 #include <maya/MFnLambertShader.h>
00027 #include <maya/MFnPhongShader.h>
00028 #include <maya/MFnMesh.h>
00029 #include <maya/MPlug.h>
00030 #include <maya/MPlugArray.h>
00031 #include <maya/MColor.h>
00032 #include <maya/MObject.h>
00033 #include <maya/MStatus.h>
00034 #include "post_maya_include.h"
00035 
00036 ////////////////////////////////////////////////////////////////////
00037 //     Function: MayaShader::Constructor
00038 //       Access: Public
00039 //  Description: Reads the Maya "shading engine" to determine the
00040 //               relevant shader properties.
00041 ////////////////////////////////////////////////////////////////////
00042 MayaShader::
00043 MayaShader(MObject engine, bool legacy_shader) {
00044   MFnDependencyNode engine_fn(engine);
00045 
00046   set_name(engine_fn.name().asChar());
00047 
00048   if (maya_cat.is_debug()) {
00049     maya_cat.debug()
00050       << "Reading shading engine " << get_name() << "\n";
00051   }
00052   _legacy_mode = false;
00053   _flat_color.set(1,1,1,1);
00054 
00055   MPlug shader_plug = engine_fn.findPlug("surfaceShader");
00056   bool found_shader = false;
00057   if (!shader_plug.isNull()) {
00058     MPlugArray shader_pa;
00059     shader_plug.connectedTo(shader_pa, true, false);
00060     maya_cat.spam() << "shader plug connected to: " << shader_pa.length() << endl;
00061     for (size_t i = 0; i < shader_pa.length() && !found_shader; i++) {
00062       MObject shader = shader_pa[0].node();
00063       if (shader.hasFn(MFn::kPhong)) { 
00064         if (legacy_shader) {
00065           found_shader = find_textures_legacy(shader);
00066         } else {
00067           found_shader = find_textures_modern(shader);
00068         }
00069       } else if (shader.hasFn(MFn::kLambert)) {
00070         found_shader = find_textures_legacy(shader);
00071         if (found_shader) {
00072           _legacy_mode = true;
00073         }
00074       } else if (shader.hasFn(MFn::kSurfaceShader)) {
00075         found_shader = find_textures_legacy(shader);
00076         if (found_shader) {
00077           _legacy_mode = true;
00078         }
00079       } else {
00080         maya_cat.warning() <<
00081           "Unrecognized shader type: only lambert and phong supported (lambert deprecated).\n";
00082       }
00083     }
00084   }
00085 }
00086 
00087 ////////////////////////////////////////////////////////////////////
00088 //     Function: MayaShader::Destructor
00089 //       Access: Public
00090 //  Description: 
00091 ////////////////////////////////////////////////////////////////////
00092 MayaShader::
00093 ~MayaShader() {
00094 }
00095 
00096 ////////////////////////////////////////////////////////////////////
00097 //     Function: MayaShader::output
00098 //       Access: Public
00099 //  Description: 
00100 ////////////////////////////////////////////////////////////////////
00101 void MayaShader::
00102 output(ostream &out) const {
00103   out << "Shader " << get_name();
00104 }
00105 
00106 ////////////////////////////////////////////////////////////////////
00107 //     Function: MayaShader::write
00108 //       Access: Public
00109 //  Description: 
00110 ////////////////////////////////////////////////////////////////////
00111 void MayaShader::
00112 write(ostream &out) const {
00113   out << "Shader " << get_name() << "\n";
00114 }
00115 
00116 ////////////////////////////////////////////////////////////////////
00117 //     Function: MayaShader::get_color_def
00118 //       Access: Public
00119 //  Description: This is part of the deprecated codepath.
00120 //               return the color def i.e. texture at idx
00121 ////////////////////////////////////////////////////////////////////
00122 MayaShaderColorDef *MayaShader::
00123 get_color_def(size_t idx) const {
00124   if (_color.size() > 0)
00125     return _color[idx];
00126   else
00127     return (MayaShaderColorDef *)NULL;
00128 }
00129 ////////////////////////////////////////////////////////////////////
00130 //     Function: MayaShader::get_rgba
00131 //       Access: Public
00132 //  Description: Returns the overall color of the shader as a
00133 //               single-precision rgba value, where the alpha
00134 //               component represents transparency according to the
00135 //               Panda convention.  If no overall color is specified
00136 //               (_has_flat_color is not true), this returns white.
00137 //
00138 //               Normally, Maya makes texture color override the flat
00139 //               color, so if a texture is also applied (_has_texture
00140 //               is true), this value is not used by Maya.
00141 ////////////////////////////////////////////////////////////////////
00142 LColor MayaShader::
00143 get_rgba(size_t idx) const {
00144   LColor rgba(1.0f, 1.0f, 1.0f, 1.0f);
00145 
00146   if (_color.size() && _color[idx]->_has_flat_color) {
00147     rgba[0] = (PN_stdfloat)_color[idx]->_flat_color[0];
00148     rgba[1] = (PN_stdfloat)_color[idx]->_flat_color[1];
00149     rgba[2] = (PN_stdfloat)_color[idx]->_flat_color[2];
00150   }
00151 
00152   if (_transparency._has_flat_color) {
00153     // Maya supports colored transparency, but we only support
00154     // grayscale transparency.  Use the pnmimage constants to
00155     // convert color to grayscale.
00156     double trans =
00157       _transparency._flat_color[0] * lumin_red + 
00158       _transparency._flat_color[1] * lumin_grn + 
00159       _transparency._flat_color[2] * lumin_blu;
00160     rgba[3] = 1.0f - (PN_stdfloat)trans;
00161   }
00162 
00163   return rgba;
00164 }
00165 
00166 ////////////////////////////////////////////////////////////////////
00167 //     Function: MayaShader::collect_maps
00168 //       Access: Private
00169 //  Description: Recalculates the all_maps list.
00170 ////////////////////////////////////////////////////////////////////
00171 void MayaShader::
00172 collect_maps() {
00173   _all_maps.clear();
00174 
00175   for (size_t i=0; i<_color_maps.size(); i++) {
00176     _all_maps.push_back(_color_maps[i]);
00177   }
00178   for (size_t i=0; i<_trans_maps.size(); i++) {
00179     _all_maps.push_back(_trans_maps[i]);
00180   }
00181   for (size_t i=0; i<_normal_maps.size(); i++) {
00182     _all_maps.push_back(_normal_maps[i]);
00183   }
00184   for (size_t i=0; i<_glow_maps.size(); i++) {
00185     _all_maps.push_back(_glow_maps[i]);
00186   }
00187   for (size_t i=0; i<_gloss_maps.size(); i++) {
00188     _all_maps.push_back(_gloss_maps[i]);
00189   }
00190   for (size_t i=0; i<_height_maps.size(); i++) {
00191     _all_maps.push_back(_height_maps[i]);
00192   }
00193   
00194   for (size_t i=0; i<_color.size(); i++) {
00195     if (_color[i]->_has_texture) {
00196       _all_maps.push_back(_color[i]);
00197     }
00198   }
00199   if (_transparency._has_texture) {
00200     _all_maps.push_back(&_transparency);
00201   }
00202 }
00203 
00204 ////////////////////////////////////////////////////////////////////
00205 //     Function: MayaShader::find_textures_modern
00206 //       Access: Private
00207 //  Description: Locates all file textures leading into the given
00208 //               shader.
00209 ////////////////////////////////////////////////////////////////////
00210 bool MayaShader::
00211 find_textures_modern(MObject shader) {
00212   if (!shader.hasFn(MFn::kPhong)) {
00213     maya_cat.warning() 
00214       << "The new codepath expects to see phong shaders only.\n";
00215     return false;
00216   }
00217   MStatus status;
00218   MFnPhongShader phong_fn(shader);
00219   MFnDependencyNode shader_fn(shader);
00220   
00221   if (maya_cat.is_spam()) {
00222     maya_cat.spam()
00223       << "  Reading modern surface shader " << shader_fn.name().asChar() << "\n";
00224   }
00225 
00226   string n = shader_fn.name().asChar();
00227   
00228   MayaShaderColorDef::find_textures_modern(n, _color_maps,  shader_fn.findPlug("color"), false);
00229   if (_color_maps.size() == 0) {
00230     MayaShaderColorDef::find_textures_modern(n, _color_maps,  shader_fn.findPlug("colorR"), false);
00231   }
00232   MayaShaderColorDef::find_textures_modern(n, _trans_maps,  shader_fn.findPlug("transparency"), true);
00233   if (_trans_maps.size() == 0) {
00234     MayaShaderColorDef::find_textures_modern(n, _trans_maps,  shader_fn.findPlug("transparencyR"), true);
00235   }
00236   MayaShaderColorDef::find_textures_modern(n, _normal_maps, shader_fn.findPlug("normalCamera"), false);
00237   if (_normal_maps.size() == 0) {
00238     MayaShaderColorDef::find_textures_modern(n, _normal_maps, shader_fn.findPlug("normalCameraR"), false);
00239   }
00240   MayaShaderColorDef::find_textures_modern(n, _gloss_maps,  shader_fn.findPlug("specularColor"), true);
00241   if (_gloss_maps.size() == 0) {
00242     MayaShaderColorDef::find_textures_modern(n, _gloss_maps,  shader_fn.findPlug("specularColorR"), true);
00243   }
00244   MayaShaderColorDef::find_textures_modern(n, _glow_maps,  shader_fn.findPlug("incandescence"), true);
00245   if (_glow_maps.size() == 0) {
00246     MayaShaderColorDef::find_textures_modern(n, _glow_maps,  shader_fn.findPlug("incandescenceR"), true);
00247   }
00248   MayaShaderColorDef::find_textures_modern(n, _height_maps,  shader_fn.findPlug("surfaceThickness"), true);
00249   if (_height_maps.size() == 0) {
00250     MayaShaderColorDef::find_textures_modern(n, _height_maps,  shader_fn.findPlug("surfaceThicknessR"), true);
00251   }
00252   
00253   collect_maps();
00254 
00255   MColor color = phong_fn.color(&status);
00256   if (status) {
00257     _flat_color.set(color.r, color.g, color.b, color.a);
00258   }
00259   
00260   color = phong_fn.transparency(&status);
00261   if (status) {
00262     _flat_color[3] = 1.0 - ((color[0] + color[1] + color[2]) * (1.0/3.0));
00263   }
00264   return true;
00265 }
00266 
00267 ////////////////////////////////////////////////////////////////////
00268 //     Function: MayaShader::bind_uvsets
00269 //       Access: Public
00270 //  Description: Assigns the uvset_name of each MayaShaderColorDef
00271 //               using the given file-to-uvset map.
00272 ////////////////////////////////////////////////////////////////////
00273 void MayaShader::
00274 bind_uvsets(MayaFileToUVSetMap &map) {
00275   for (size_t i=0; i<_all_maps.size(); i++) {
00276     MayaShaderColorDef *def = _all_maps[i];
00277     MayaFileToUVSetMap::iterator p = map.find(def->_texture_name);
00278     if (p == map.end()) {
00279       def->_uvset_name = "map1";
00280     } else {
00281       def->_uvset_name = (*p).second;
00282     }
00283   }
00284   
00285   calculate_pairings();
00286 }
00287 
00288 ////////////////////////////////////////////////////////////////////
00289 //     Function: MayaShader::calculate_pairings
00290 //       Access: Public
00291 //  Description: For each Alpha texture, try to find an RGB texture
00292 //               that has the same properties.  Attempt to make it
00293 //               so that the alpha texture isn't a separate texture,
00294 //               but rather, an Alpha-Filename associated with an
00295 //               existing texture.
00296 ////////////////////////////////////////////////////////////////////
00297 void MayaShader::
00298 calculate_pairings() {
00299 
00300   if (_legacy_mode) {
00301     return;
00302   }
00303   
00304   for (size_t i=0; i<_all_maps.size(); i++) {
00305     _all_maps[i]->_opposite = 0;
00306   }
00307   
00308   bool using_transparency = (_trans_maps.size() > 0);
00309   
00310   for (int retry=0; retry<2; retry++) {
00311     bool perfect=(retry==0);
00312     for (size_t i=0; i<_color_maps.size(); i++) {
00313       if ((_color_maps[i]->_blend_type == MayaShaderColorDef::BT_modulate)||
00314           (_color_maps[i]->_blend_type == MayaShaderColorDef::BT_unspecified)) {
00315         for (size_t j=0; j<_trans_maps.size(); j++) {
00316           try_pair(_color_maps[i], _trans_maps[j], perfect);
00317         }
00318       }
00319     }
00320   }
00321   
00322   if (!using_transparency) {
00323     for (int retry=0; retry<2; retry++) {
00324       bool perfect=(retry==0);
00325       for (size_t i=0; i<_color_maps.size(); i++) {
00326         for (size_t j=0; j<_glow_maps.size(); j++) {
00327           try_pair(_color_maps[i], _glow_maps[j], perfect);
00328         }
00329         for (size_t j=0; j<_gloss_maps.size(); j++) {
00330           try_pair(_color_maps[i], _gloss_maps[j], perfect);
00331         }
00332       }
00333     }
00334   }
00335   
00336   for (int retry=0; retry<2; retry++) {
00337     bool perfect=(retry==0);
00338     for (size_t i=0; i<_normal_maps.size(); i++) {
00339       for (size_t j=0; j<_height_maps.size(); j++) {
00340         try_pair(_normal_maps[i], _height_maps[j], perfect);
00341       }
00342     }
00343   }
00344   
00345   for (size_t i=0; i<_normal_maps.size(); i++) {
00346     _normal_maps[i]->_blend_type = MayaShaderColorDef::BT_normal;
00347   }
00348   for (size_t i=0; i<_glow_maps.size(); i++) {
00349     if (_glow_maps[i]->_opposite) {
00350       _glow_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
00351       _glow_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_modulate_glow;
00352     } else {
00353       _glow_maps[i]->_blend_type = MayaShaderColorDef::BT_glow;
00354     }
00355   }
00356   for (size_t i=0; i<_gloss_maps.size(); i++) {
00357     if (_gloss_maps[i]->_opposite) {
00358       _gloss_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
00359       _gloss_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_modulate_gloss;
00360     } else {
00361       _gloss_maps[i]->_blend_type = MayaShaderColorDef::BT_gloss;
00362     }
00363   }
00364   for (size_t i=0; i<_height_maps.size(); i++) {
00365     if (_height_maps[i]->_opposite) {
00366       _height_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
00367       _height_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_normal_height;
00368     } else {
00369       _height_maps[i]->_blend_type = MayaShaderColorDef::BT_height;
00370     }
00371   }
00372   for (size_t i=0; i<_trans_maps.size(); i++) {
00373     if (_trans_maps[i]->_opposite) {
00374       _trans_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
00375       _trans_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_modulate;
00376     } else {
00377       _trans_maps[i]->_blend_type = MayaShaderColorDef::BT_modulate;
00378     }
00379   }
00380 }
00381 
00382 ////////////////////////////////////////////////////////////////////
00383 //     Function: MayaShader::try_pair
00384 //       Access: Private
00385 //  Description: Try to associate an RGB tex with an Alpha tex.
00386 ////////////////////////////////////////////////////////////////////
00387 bool MayaShader::try_pair(MayaShaderColorDef *map1,
00388                           MayaShaderColorDef *map2,
00389                           bool perfect) {
00390   if ((map1->_opposite)||(map2->_opposite)) {
00391     // one of the maps is already paired
00392     return false;
00393   }
00394   if (perfect) {
00395     if (map1->_texture_filename != map2->_texture_filename) {
00396       // perfect mode requires a filename match.
00397       return false;
00398     }
00399   } else {
00400     string pre1 = get_file_prefix(map1->_texture_filename);
00401     string pre2 = get_file_prefix(map2->_texture_filename);
00402     if (pre1 != pre2) {
00403       // imperfect mode requires a filename prefix match.
00404       return false;
00405     }
00406   }
00407   
00408   if ((map1->_projection_type   != map2->_projection_type) ||
00409       (map1->_projection_matrix != map2->_projection_matrix) ||
00410       (map1->_u_angle           != map2->_u_angle) ||
00411       (map1->_v_angle           != map2->_v_angle) ||
00412       (map1->_uvset_name        != map2->_uvset_name) ||
00413       (map1->_mirror            != map2->_mirror) ||
00414       (map1->_stagger           != map2->_stagger) ||
00415       (map1->_wrap_u            != map2->_wrap_u) ||
00416       (map1->_wrap_v            != map2->_wrap_v) ||
00417       (map1->_repeat_uv         != map2->_repeat_uv) ||
00418       (map1->_offset            != map2->_offset) ||
00419       (map1->_rotate_uv         != map2->_rotate_uv)) {
00420     return false;
00421   }
00422   // Pairing successful.
00423   map1->_opposite = map2;
00424   map2->_opposite = map1;
00425   return true;
00426 }
00427 
00428 ////////////////////////////////////////////////////////////////////
00429 //     Function: MayaShader::get_file_prefix
00430 //       Access: Private
00431 //  Description: Try to associate an RGB tex with an Alpha tex.
00432 ////////////////////////////////////////////////////////////////////
00433 string MayaShader::
00434 get_file_prefix(const string &fn) {
00435   Filename pfn = Filename::from_os_specific(fn);
00436   string base = pfn.get_basename_wo_extension();
00437   size_t offs = base.find("_");
00438   if (offs != string::npos) {
00439     base = base.substr(0, offs);
00440   }
00441   offs = base.find("-");
00442   if (offs != string::npos) {
00443     base = base.substr(0, offs);
00444   }
00445   return base;
00446 }
00447 
00448 ////////////////////////////////////////////////////////////////////
00449 //     Function: MayaShader::find_textures_legacy
00450 //       Access: Private
00451 //  Description: This is part of the legacy codepath.  
00452 //               Extracts out the shading information from the Maya
00453 //               surface shader.
00454 ////////////////////////////////////////////////////////////////////
00455 bool MayaShader::
00456 find_textures_legacy(MObject shader) {
00457   MStatus status;
00458   MFnDependencyNode shader_fn(shader);
00459   
00460   if (maya_cat.is_spam()) {
00461     maya_cat.spam()
00462       << "  Reading legacy surface shader " << shader_fn.name().asChar() << "\n";
00463   }
00464 
00465   // First, check for a connection to the color attribute.  This could
00466   // be a texture map or something, and will override whatever the
00467   // shader says for color.
00468 
00469   MPlug color_plug = shader_fn.findPlug("color");
00470   if (color_plug.isNull()) {
00471     // Or maybe a connection to outColor.  Not sure how this differs
00472     // from just color, but empirically it seems that either might be
00473     // used.
00474     color_plug = shader_fn.findPlug("outColor");
00475   }
00476 
00477   if (!color_plug.isNull()) {
00478     MPlugArray color_pa;
00479     color_plug.connectedTo(color_pa, true, false);
00480 
00481     MayaShaderColorDef *color_p = new MayaShaderColorDef;
00482     for (size_t i = 0; i < color_pa.length(); i++) {
00483       maya_cat.spam() << "color_pa[" << i << "]:" << color_pa[i].name().asChar() << endl;
00484       color_p->find_textures_legacy(this, color_pa[0].node());
00485     }
00486 
00487     if (color_pa.length() < 1) {
00488       // assign a blank default color to this shader
00489       maya_cat.spam() << shader_fn.name().asChar() << " was not connected to texture" << endl;
00490       this->_color.push_back(color_p);
00491     }
00492   }
00493 
00494   // Transparency is stored separately.
00495   MPlug trans_plug = shader_fn.findPlug("transparency");
00496   if (trans_plug.isNull()) {
00497     trans_plug = shader_fn.findPlug("outTransparency");
00498   }
00499     
00500   if (!trans_plug.isNull()) {
00501     MPlugArray trans_pa;
00502     trans_plug.connectedTo(trans_pa, true, false);
00503 
00504     for (size_t i = 0; i < trans_pa.length(); i++) {
00505       maya_cat.spam() << "read a transparency texture" << endl;
00506       _transparency.find_textures_legacy(this, trans_pa[0].node(), true);
00507     }
00508   }
00509 
00510   // Also try to get the ordinary color directly from the surface
00511   // shader.
00512   bool b_color_def = true;
00513   if (shader.hasFn(MFn::kLambert)) {
00514     MFnLambertShader lambert_fn(shader);
00515     MColor color = lambert_fn.color(&status);
00516     if (status) {
00517       // Warning! The alpha component of color doesn't mean
00518       // transparency in Maya.
00519       for (size_t i=0; i<_color.size(); ++i) {
00520         _color[i]->_has_flat_color = true;
00521         _color[i]->_flat_color.set(color.r, color.g, color.b, color.a);
00522         maya_cat.spam() << shader_fn.name().asChar() << " set shader color" << endl;
00523         // needed to print the final check
00524         if (!_color[i]->_has_flat_color && !_color[i]->_has_texture)
00525           b_color_def = false;
00526 
00527         _transparency._flat_color.set(0.0, 0.0, 0.0, 0.0);
00528         
00529         // Get the transparency separately.
00530         color = lambert_fn.transparency(&status);
00531         if (status) {
00532           _transparency._has_flat_color = true;
00533           _transparency._flat_color.set(color.r, color.g, color.b, color.a);
00534         }
00535       }
00536     }
00537   }
00538   //  if (!_color._has_flat_color && !_color._has_texture) {
00539   if (!b_color_def) {
00540     maya_cat.info() << shader_fn.name().asChar() << "Color def not found" << endl;
00541     if (maya_cat.is_spam()) {
00542       maya_cat.spam()
00543         << "  Color definition not found.\n";
00544     }
00545   }
00546 
00547   collect_maps();
00548   return true;
00549 }
 All Classes Functions Variables Enumerations