Panda3D

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