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