Panda3D
 All Classes Functions Variables Enumerations
mayaShader.cxx
1 // Filename: mayaShader.cxx
2 // Created by: drose (01Feb00)
3 // Modified 19Mar10 by ETC PandaSE team (see
4 // header comment for mayaToEgg.cxx for details)
5 //
6 ////////////////////////////////////////////////////////////////////
7 //
8 // PANDA 3D SOFTWARE
9 // Copyright (c) Carnegie Mellon University. All rights reserved.
10 //
11 // All use of this software is subject to the terms of the revised BSD
12 // license. You should have received a copy of this license along
13 // with this source code in a file named "LICENSE."
14 //
15 ////////////////////////////////////////////////////////////////////
16 
17 #include "mayaShader.h"
18 #include "maya_funcs.h"
19 #include "config_maya.h"
20 #include "string_utils.h"
21 #include "pnmImageHeader.h" // for lumin_red, etc.
22 #include "pset.h"
23 
24 #include "pre_maya_include.h"
25 #include <maya/MFnDependencyNode.h>
26 #include <maya/MFnLambertShader.h>
27 #include <maya/MFnPhongShader.h>
28 #include <maya/MFnMesh.h>
29 #include <maya/MPlug.h>
30 #include <maya/MPlugArray.h>
31 #include <maya/MColor.h>
32 #include <maya/MObject.h>
33 #include <maya/MStatus.h>
34 #include "post_maya_include.h"
35 
36 ////////////////////////////////////////////////////////////////////
37 // Function: MayaShader::Constructor
38 // Access: Public
39 // Description: Reads the Maya "shading engine" to determine the
40 // relevant shader properties.
41 ////////////////////////////////////////////////////////////////////
43 MayaShader(MObject engine, bool legacy_shader) {
44  MFnDependencyNode engine_fn(engine);
45 
46  set_name(engine_fn.name().asChar());
47 
48  if (maya_cat.is_debug()) {
49  maya_cat.debug()
50  << "Reading shading engine " << get_name() << "\n";
51  }
52  _legacy_mode = false;
53  _flat_color.set(1,1,1,1);
54 
55  MPlug shader_plug = engine_fn.findPlug("surfaceShader");
56  bool found_shader = false;
57  if (!shader_plug.isNull()) {
58  MPlugArray shader_pa;
59  shader_plug.connectedTo(shader_pa, true, false);
60  maya_cat.spam() << "shader plug connected to: " << shader_pa.length() << endl;
61  for (size_t i = 0; i < shader_pa.length() && !found_shader; i++) {
62  MObject shader = shader_pa[0].node();
63  if (shader.hasFn(MFn::kPhong)) {
64  if (legacy_shader) {
65  found_shader = find_textures_legacy(shader);
66  } else {
67  found_shader = find_textures_modern(shader);
68  }
69  } else if (shader.hasFn(MFn::kLambert)) {
70  found_shader = find_textures_legacy(shader);
71  if (found_shader) {
72  _legacy_mode = true;
73  }
74  } else if (shader.hasFn(MFn::kSurfaceShader)) {
75  found_shader = find_textures_legacy(shader);
76  if (found_shader) {
77  _legacy_mode = true;
78  }
79  } else {
80  maya_cat.warning() <<
81  "Unrecognized shader type: only lambert and phong supported (lambert deprecated).\n";
82  }
83  }
84  }
85 }
86 
87 ////////////////////////////////////////////////////////////////////
88 // Function: MayaShader::Destructor
89 // Access: Public
90 // Description:
91 ////////////////////////////////////////////////////////////////////
92 MayaShader::
93 ~MayaShader() {
94 }
95 
96 ////////////////////////////////////////////////////////////////////
97 // Function: MayaShader::output
98 // Access: Public
99 // Description:
100 ////////////////////////////////////////////////////////////////////
101 void MayaShader::
102 output(ostream &out) const {
103  out << "Shader " << get_name();
104 }
105 
106 ////////////////////////////////////////////////////////////////////
107 // Function: MayaShader::write
108 // Access: Public
109 // Description:
110 ////////////////////////////////////////////////////////////////////
111 void MayaShader::
112 write(ostream &out) const {
113  out << "Shader " << get_name() << "\n";
114 }
115 
116 ////////////////////////////////////////////////////////////////////
117 // Function: MayaShader::get_color_def
118 // Access: Public
119 // Description: This is part of the deprecated codepath.
120 // return the color def i.e. texture at idx
121 ////////////////////////////////////////////////////////////////////
123 get_color_def(size_t idx) const {
124  if (_color.size() > 0)
125  return _color[idx];
126  else
127  return (MayaShaderColorDef *)NULL;
128 }
129 ////////////////////////////////////////////////////////////////////
130 // Function: MayaShader::get_rgba
131 // Access: Public
132 // Description: Returns the overall color of the shader as a
133 // single-precision rgba value, where the alpha
134 // component represents transparency according to the
135 // Panda convention. If no overall color is specified
136 // (_has_flat_color is not true), this returns white.
137 //
138 // Normally, Maya makes texture color override the flat
139 // color, so if a texture is also applied (_has_texture
140 // is true), this value is not used by Maya.
141 ////////////////////////////////////////////////////////////////////
143 get_rgba(size_t idx) const {
144  LColor rgba(1.0f, 1.0f, 1.0f, 1.0f);
145 
146  if (_color.size() && _color[idx]->_has_flat_color) {
147  rgba[0] = (PN_stdfloat)_color[idx]->_flat_color[0];
148  rgba[1] = (PN_stdfloat)_color[idx]->_flat_color[1];
149  rgba[2] = (PN_stdfloat)_color[idx]->_flat_color[2];
150  }
151 
152  if (_transparency._has_flat_color) {
153  // Maya supports colored transparency, but we only support
154  // grayscale transparency. Use the pnmimage constants to
155  // convert color to grayscale.
156  double trans =
157  _transparency._flat_color[0] * lumin_red +
158  _transparency._flat_color[1] * lumin_grn +
159  _transparency._flat_color[2] * lumin_blu;
160  rgba[3] = 1.0f - (PN_stdfloat)trans;
161  }
162 
163  return rgba;
164 }
165 
166 ////////////////////////////////////////////////////////////////////
167 // Function: MayaShader::collect_maps
168 // Access: Private
169 // Description: Recalculates the all_maps list.
170 ////////////////////////////////////////////////////////////////////
171 void MayaShader::
173  _all_maps.clear();
174 
175  for (size_t i=0; i<_color_maps.size(); i++) {
176  _all_maps.push_back(_color_maps[i]);
177  }
178  for (size_t i=0; i<_trans_maps.size(); i++) {
179  _all_maps.push_back(_trans_maps[i]);
180  }
181  for (size_t i=0; i<_normal_maps.size(); i++) {
182  _all_maps.push_back(_normal_maps[i]);
183  }
184  for (size_t i=0; i<_glow_maps.size(); i++) {
185  _all_maps.push_back(_glow_maps[i]);
186  }
187  for (size_t i=0; i<_gloss_maps.size(); i++) {
188  _all_maps.push_back(_gloss_maps[i]);
189  }
190  for (size_t i=0; i<_height_maps.size(); i++) {
191  _all_maps.push_back(_height_maps[i]);
192  }
193 
194  for (size_t i=0; i<_color.size(); i++) {
195  if (_color[i]->_has_texture) {
196  _all_maps.push_back(_color[i]);
197  }
198  }
199  if (_transparency._has_texture) {
200  _all_maps.push_back(&_transparency);
201  }
202 }
203 
204 ////////////////////////////////////////////////////////////////////
205 // Function: MayaShader::find_textures_modern
206 // Access: Private
207 // Description: Locates all file textures leading into the given
208 // shader.
209 ////////////////////////////////////////////////////////////////////
210 bool MayaShader::
211 find_textures_modern(MObject shader) {
212  if (!shader.hasFn(MFn::kPhong)) {
213  maya_cat.warning()
214  << "The new codepath expects to see phong shaders only.\n";
215  return false;
216  }
217  MStatus status;
218  MFnPhongShader phong_fn(shader);
219  MFnDependencyNode shader_fn(shader);
220 
221  if (maya_cat.is_spam()) {
222  maya_cat.spam()
223  << " Reading modern surface shader " << shader_fn.name().asChar() << "\n";
224  }
225 
226  string n = shader_fn.name().asChar();
227 
228  MayaShaderColorDef::find_textures_modern(n, _color_maps, shader_fn.findPlug("color"), false);
229  if (_color_maps.size() == 0) {
230  MayaShaderColorDef::find_textures_modern(n, _color_maps, shader_fn.findPlug("colorR"), false);
231  }
232  MayaShaderColorDef::find_textures_modern(n, _trans_maps, shader_fn.findPlug("transparency"), true);
233  if (_trans_maps.size() == 0) {
234  MayaShaderColorDef::find_textures_modern(n, _trans_maps, shader_fn.findPlug("transparencyR"), true);
235  }
236  MayaShaderColorDef::find_textures_modern(n, _normal_maps, shader_fn.findPlug("normalCamera"), false);
237  if (_normal_maps.size() == 0) {
238  MayaShaderColorDef::find_textures_modern(n, _normal_maps, shader_fn.findPlug("normalCameraR"), false);
239  }
240  MayaShaderColorDef::find_textures_modern(n, _gloss_maps, shader_fn.findPlug("specularColor"), true);
241  if (_gloss_maps.size() == 0) {
242  MayaShaderColorDef::find_textures_modern(n, _gloss_maps, shader_fn.findPlug("specularColorR"), true);
243  }
244  MayaShaderColorDef::find_textures_modern(n, _glow_maps, shader_fn.findPlug("incandescence"), true);
245  if (_glow_maps.size() == 0) {
246  MayaShaderColorDef::find_textures_modern(n, _glow_maps, shader_fn.findPlug("incandescenceR"), true);
247  }
248  MayaShaderColorDef::find_textures_modern(n, _height_maps, shader_fn.findPlug("surfaceThickness"), true);
249  if (_height_maps.size() == 0) {
250  MayaShaderColorDef::find_textures_modern(n, _height_maps, shader_fn.findPlug("surfaceThicknessR"), true);
251  }
252 
253  collect_maps();
254 
255  MColor color = phong_fn.color(&status);
256  if (status) {
257  _flat_color.set(color.r, color.g, color.b, color.a);
258  }
259 
260  color = phong_fn.transparency(&status);
261  if (status) {
262  _flat_color[3] = 1.0 - ((color[0] + color[1] + color[2]) * (1.0/3.0));
263  }
264  return true;
265 }
266 
267 ////////////////////////////////////////////////////////////////////
268 // Function: MayaShader::bind_uvsets
269 // Access: Public
270 // Description: Assigns the uvset_name of each MayaShaderColorDef
271 // using the given file-to-uvset map.
272 ////////////////////////////////////////////////////////////////////
273 void MayaShader::
275  for (size_t i=0; i<_all_maps.size(); i++) {
276  MayaShaderColorDef *def = _all_maps[i];
277  MayaFileToUVSetMap::iterator p = map.find(def->_texture_name);
278  if (p == map.end()) {
279  def->_uvset_name = "map1";
280  } else {
281  def->_uvset_name = (*p).second;
282  }
283  }
284 
285  calculate_pairings();
286 }
287 
288 ////////////////////////////////////////////////////////////////////
289 // Function: MayaShader::calculate_pairings
290 // Access: Public
291 // Description: For each Alpha texture, try to find an RGB texture
292 // that has the same properties. Attempt to make it
293 // so that the alpha texture isn't a separate texture,
294 // but rather, an Alpha-Filename associated with an
295 // existing texture.
296 ////////////////////////////////////////////////////////////////////
297 void MayaShader::
298 calculate_pairings() {
299 
300  if (_legacy_mode) {
301  return;
302  }
303 
304  for (size_t i=0; i<_all_maps.size(); i++) {
305  _all_maps[i]->_opposite = 0;
306  }
307 
308  bool using_transparency = (_trans_maps.size() > 0);
309 
310  for (int retry=0; retry<2; retry++) {
311  bool perfect=(retry==0);
312  for (size_t i=0; i<_color_maps.size(); i++) {
313  if ((_color_maps[i]->_blend_type == MayaShaderColorDef::BT_modulate)||
314  (_color_maps[i]->_blend_type == MayaShaderColorDef::BT_unspecified)) {
315  for (size_t j=0; j<_trans_maps.size(); j++) {
316  try_pair(_color_maps[i], _trans_maps[j], perfect);
317  }
318  }
319  }
320  }
321 
322  if (!using_transparency) {
323  for (int retry=0; retry<2; retry++) {
324  bool perfect=(retry==0);
325  for (size_t i=0; i<_color_maps.size(); i++) {
326  for (size_t j=0; j<_glow_maps.size(); j++) {
327  try_pair(_color_maps[i], _glow_maps[j], perfect);
328  }
329  for (size_t j=0; j<_gloss_maps.size(); j++) {
330  try_pair(_color_maps[i], _gloss_maps[j], perfect);
331  }
332  }
333  }
334  }
335 
336  for (int retry=0; retry<2; retry++) {
337  bool perfect=(retry==0);
338  for (size_t i=0; i<_normal_maps.size(); i++) {
339  for (size_t j=0; j<_height_maps.size(); j++) {
340  try_pair(_normal_maps[i], _height_maps[j], perfect);
341  }
342  }
343  }
344 
345  for (size_t i=0; i<_normal_maps.size(); i++) {
346  _normal_maps[i]->_blend_type = MayaShaderColorDef::BT_normal;
347  }
348  for (size_t i=0; i<_glow_maps.size(); i++) {
349  if (_glow_maps[i]->_opposite) {
350  _glow_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
351  _glow_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_modulate_glow;
352  } else {
353  _glow_maps[i]->_blend_type = MayaShaderColorDef::BT_glow;
354  }
355  }
356  for (size_t i=0; i<_gloss_maps.size(); i++) {
357  if (_gloss_maps[i]->_opposite) {
358  _gloss_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
359  _gloss_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_modulate_gloss;
360  } else {
361  _gloss_maps[i]->_blend_type = MayaShaderColorDef::BT_gloss;
362  }
363  }
364  for (size_t i=0; i<_height_maps.size(); i++) {
365  if (_height_maps[i]->_opposite) {
366  _height_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
367  _height_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_normal_height;
368  } else {
369  _height_maps[i]->_blend_type = MayaShaderColorDef::BT_height;
370  }
371  }
372  for (size_t i=0; i<_trans_maps.size(); i++) {
373  if (_trans_maps[i]->_opposite) {
374  _trans_maps[i]->_blend_type = MayaShaderColorDef::BT_unspecified;
375  _trans_maps[i]->_opposite->_blend_type = MayaShaderColorDef::BT_modulate;
376  } else {
377  _trans_maps[i]->_blend_type = MayaShaderColorDef::BT_modulate;
378  }
379  }
380 }
381 
382 ////////////////////////////////////////////////////////////////////
383 // Function: MayaShader::try_pair
384 // Access: Private
385 // Description: Try to associate an RGB tex with an Alpha tex.
386 ////////////////////////////////////////////////////////////////////
387 bool MayaShader::try_pair(MayaShaderColorDef *map1,
388  MayaShaderColorDef *map2,
389  bool perfect) {
390  if ((map1->_opposite)||(map2->_opposite)) {
391  // one of the maps is already paired
392  return false;
393  }
394  if (perfect) {
395  if (map1->_texture_filename != map2->_texture_filename) {
396  // perfect mode requires a filename match.
397  return false;
398  }
399  } else {
400  string pre1 = get_file_prefix(map1->_texture_filename);
401  string pre2 = get_file_prefix(map2->_texture_filename);
402  if (pre1 != pre2) {
403  // imperfect mode requires a filename prefix match.
404  return false;
405  }
406  }
407 
408  if ((map1->_projection_type != map2->_projection_type) ||
409  (map1->_projection_matrix != map2->_projection_matrix) ||
410  (map1->_u_angle != map2->_u_angle) ||
411  (map1->_v_angle != map2->_v_angle) ||
412  (map1->_uvset_name != map2->_uvset_name) ||
413  (map1->_mirror != map2->_mirror) ||
414  (map1->_stagger != map2->_stagger) ||
415  (map1->_wrap_u != map2->_wrap_u) ||
416  (map1->_wrap_v != map2->_wrap_v) ||
417  (map1->_repeat_uv != map2->_repeat_uv) ||
418  (map1->_offset != map2->_offset) ||
419  (map1->_rotate_uv != map2->_rotate_uv)) {
420  return false;
421  }
422  // Pairing successful.
423  map1->_opposite = map2;
424  map2->_opposite = map1;
425  return true;
426 }
427 
428 ////////////////////////////////////////////////////////////////////
429 // Function: MayaShader::get_file_prefix
430 // Access: Private
431 // Description: Try to associate an RGB tex with an Alpha tex.
432 ////////////////////////////////////////////////////////////////////
433 string MayaShader::
434 get_file_prefix(const string &fn) {
436  string base = pfn.get_basename_wo_extension();
437  size_t offs = base.find("_");
438  if (offs != string::npos) {
439  base = base.substr(0, offs);
440  }
441  offs = base.find("-");
442  if (offs != string::npos) {
443  base = base.substr(0, offs);
444  }
445  return base;
446 }
447 
448 ////////////////////////////////////////////////////////////////////
449 // Function: MayaShader::find_textures_legacy
450 // Access: Private
451 // Description: This is part of the legacy codepath.
452 // Extracts out the shading information from the Maya
453 // surface shader.
454 ////////////////////////////////////////////////////////////////////
455 bool MayaShader::
456 find_textures_legacy(MObject shader) {
457  MStatus status;
458  MFnDependencyNode shader_fn(shader);
459 
460  if (maya_cat.is_spam()) {
461  maya_cat.spam()
462  << " Reading legacy surface shader " << shader_fn.name().asChar() << "\n";
463  }
464 
465  // First, check for a connection to the color attribute. This could
466  // be a texture map or something, and will override whatever the
467  // shader says for color.
468 
469  MPlug color_plug = shader_fn.findPlug("color");
470  if (color_plug.isNull()) {
471  // Or maybe a connection to outColor. Not sure how this differs
472  // from just color, but empirically it seems that either might be
473  // used.
474  color_plug = shader_fn.findPlug("outColor");
475  }
476 
477  if (!color_plug.isNull()) {
478  MPlugArray color_pa;
479  color_plug.connectedTo(color_pa, true, false);
480 
481  MayaShaderColorDef *color_p = new MayaShaderColorDef;
482  for (size_t i = 0; i < color_pa.length(); i++) {
483  maya_cat.spam() << "color_pa[" << i << "]:" << color_pa[i].name().asChar() << endl;
484  color_p->find_textures_legacy(this, color_pa[0].node());
485  }
486 
487  if (color_pa.length() < 1) {
488  // assign a blank default color to this shader
489  maya_cat.spam() << shader_fn.name().asChar() << " was not connected to texture" << endl;
490  this->_color.push_back(color_p);
491  }
492  }
493 
494  // Transparency is stored separately.
495  MPlug trans_plug = shader_fn.findPlug("transparency");
496  if (trans_plug.isNull()) {
497  trans_plug = shader_fn.findPlug("outTransparency");
498  }
499 
500  if (!trans_plug.isNull()) {
501  MPlugArray trans_pa;
502  trans_plug.connectedTo(trans_pa, true, false);
503 
504  for (size_t i = 0; i < trans_pa.length(); i++) {
505  maya_cat.spam() << "read a transparency texture" << endl;
506  _transparency.find_textures_legacy(this, trans_pa[0].node(), true);
507  }
508  }
509 
510  // Also try to get the ordinary color directly from the surface
511  // shader.
512  bool b_color_def = true;
513  if (shader.hasFn(MFn::kLambert)) {
514  MFnLambertShader lambert_fn(shader);
515  MColor color = lambert_fn.color(&status);
516  if (status) {
517  // Warning! The alpha component of color doesn't mean
518  // transparency in Maya.
519  for (size_t i=0; i<_color.size(); ++i) {
520  _color[i]->_has_flat_color = true;
521  _color[i]->_flat_color.set(color.r, color.g, color.b, color.a);
522  maya_cat.spam() << shader_fn.name().asChar() << " set shader color" << endl;
523  // needed to print the final check
524  if (!_color[i]->_has_flat_color && !_color[i]->_has_texture)
525  b_color_def = false;
526 
527  _transparency._flat_color.set(0.0, 0.0, 0.0, 0.0);
528 
529  // Get the transparency separately.
530  color = lambert_fn.transparency(&status);
531  if (status) {
532  _transparency._has_flat_color = true;
533  _transparency._flat_color.set(color.r, color.g, color.b, color.a);
534  }
535  }
536  }
537  }
538  // if (!_color._has_flat_color && !_color._has_texture) {
539  if (!b_color_def) {
540  maya_cat.info() << shader_fn.name().asChar() << "Color def not found" << endl;
541  if (maya_cat.is_spam()) {
542  maya_cat.spam()
543  << " Color definition not found.\n";
544  }
545  }
546 
547  collect_maps();
548  return true;
549 }
LColor get_rgba(size_t idx=0) const
Returns the overall color of the shader as a single-precision rgba value, where the alpha component r...
Definition: mayaShader.cxx:143
void collect_maps()
Recalculates the all_maps list.
Definition: mayaShader.cxx:172
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This defines the various attributes that Maya may associate with the &quot;color&quot; channel for a particular...
MayaShaderColorDef * get_color_def(size_t idx=0) const
This is part of the deprecated codepath.
Definition: mayaShader.cxx:123
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
MayaShader(MObject engine, bool legacy_shader)
Reads the Maya &quot;shading engine&quot; to determine the relevant shader properties.
Definition: mayaShader.cxx:43
void bind_uvsets(MayaFileToUVSetMap &map)
Assigns the uvset_name of each MayaShaderColorDef using the given file-to-uvset map.
Definition: mayaShader.cxx:274
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332