Panda3D
mayaShaderColorDef.cxx
1 // Filename: mayaShaderColorDef.cxx
2 // Created by: drose (12Apr03)
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 "mayaShaderColorDef.h"
18 #include "mayaShader.h"
19 #include "maya_funcs.h"
20 #include "config_maya.h"
21 #include "string_utils.h"
22 #include "pset.h"
23 
24 #include "pre_maya_include.h"
25 #include <maya/MFnDependencyNode.h>
26 #include <maya/MPlug.h>
27 #include <maya/MPlugArray.h>
28 #include <maya/MObject.h>
29 #include <maya/MStatus.h>
30 #include <maya/MFnEnumAttribute.h>
31 #include "post_maya_include.h"
32 
33 ////////////////////////////////////////////////////////////////////
34 // Function: MayaShaderColorDef::Constructor
35 // Access: Public
36 // Description:
37 ////////////////////////////////////////////////////////////////////
38 MayaShaderColorDef::
39 MayaShaderColorDef() {
40 
41  _blend_type = BT_unspecified;
42 
43  _projection_type = PT_off;
44  _projection_matrix = LMatrix4d::ident_mat();
45  _u_angle = 0.0;
46  _v_angle = 0.0;
47 
48  _texture_filename = "";
49  _texture_name = "";
50  _color_gain.set(1.0f, 1.0f, 1.0f, 1.0f);
51 
52  _coverage.set(1.0, 1.0);
53  _translate_frame.set(0.0, 0.0);
54  _rotate_frame = 0.0;
55 
56  _mirror = false;
57  _stagger = false;
58  _wrap_u = true;
59  _wrap_v = true;
60 
61  _repeat_uv.set(1.0, 1.0);
62  _offset.set(0.0, 0.0);
63  _rotate_uv = 0.0;
64 
65  _is_alpha = false;
66 
67  _opposite = 0;
68 
69  _color_object = (MObject *)NULL;
70 
71  _has_texture = false;
72  _has_flat_color = false;
73  _flat_color.set(0.0, 0.0, 0.0, 0.0);
74  _has_alpha_channel = false;
75  _keep_color = false; // classic mode overwrites color: new mode retains color with a 3rd layer
76  _keep_alpha = false;
77  _interpolate = false;
78  _uvset_name = "map1";
79 
80  _map_uvs = NULL;
81 }
82 
83 ////////////////////////////////////////////////////////////////////
84 // Function: MayaShaderColorDef::Constructor
85 // Access: Public
86 // Description:
87 ////////////////////////////////////////////////////////////////////
88 MayaShaderColorDef::
89 MayaShaderColorDef(MayaShaderColorDef &copy) {
90  _has_texture = copy._has_texture;
91  _texture_filename = copy._texture_filename;
92  _texture_name = copy._texture_name;
93  _uvset_name = copy._uvset_name;
94  _color_gain = copy._color_gain;
95 
96  _has_flat_color = copy._has_flat_color;
97  _flat_color = copy._flat_color;
98 
99  _projection_type = copy._projection_type;
100  _projection_matrix = copy._projection_matrix;
101  _u_angle = copy._u_angle;
102  _v_angle = copy._v_angle;
103 
104  _coverage = copy._coverage;
105  _translate_frame = copy._translate_frame;
106  _rotate_frame = copy._rotate_frame;
107 
108  _mirror = copy._mirror;
109  _stagger = copy._stagger;
110  _wrap_u = copy._wrap_u;
111  _wrap_v = copy._wrap_v;
112 
113  _blend_type = copy._blend_type;
114  _has_alpha_channel = copy._has_alpha_channel;
115  _keep_color = copy._keep_color;
116  _keep_alpha = copy._keep_alpha;
117  _interpolate = copy._interpolate;
118 
119  _repeat_uv = copy._repeat_uv;
120  _offset = copy._offset;
121  _rotate_uv = copy._rotate_uv;
122 
123  _is_alpha = copy._is_alpha;
124 
125  _map_uvs = copy._map_uvs;
126  _color_object = copy._color_object;
127 
128  _opposite = 0;
129 }
130 
131 ////////////////////////////////////////////////////////////////////
132 // Function: MayaShaderColorDef::Destructor
133 // Access: Public
134 // Description:
135 ////////////////////////////////////////////////////////////////////
136 MayaShaderColorDef::
137 ~MayaShaderColorDef() {
138  if (_color_object != (MObject *)NULL) {
139  delete _color_object;
140  }
141 }
142 
143 ////////////////////////////////////////////////////////////////////
144 // Function: MayaShaderColorDef::compute_texture_matrix
145 // Access: Public
146 // Description: Returns a texture matrix corresponding to the texture
147 // transforms indicated by the shader.
148 ////////////////////////////////////////////////////////////////////
151  LVector2d scale(_repeat_uv[0] / _coverage[0],
152  _repeat_uv[1] / _coverage[1]);
153  LVector2d trans(_offset[0] - _translate_frame[0] / _coverage[0],
154  _offset[1] - _translate_frame[1] / _coverage[1]);
155 
156  return
157  (LMatrix3d::translate_mat(LVector2d(-0.5, -0.5)) *
158  LMatrix3d::rotate_mat(_rotate_frame) *
159  LMatrix3d::translate_mat(LVector2d(0.5, 0.5))) *
160  LMatrix3d::scale_mat(scale) *
162 }
163 
164 ////////////////////////////////////////////////////////////////////
165 // Function: MayaShaderColorDef::has_projection
166 // Access: Public
167 // Description: Returns true if the shader has a projection in effect.
168 ////////////////////////////////////////////////////////////////////
170 has_projection() const {
171  return (_projection_type != PT_off);
172 }
173 
174 ////////////////////////////////////////////////////////////////////
175 // Function: MayaShaderColorDef::project_uv
176 // Access: Public
177 // Description: If the shader has a projection (has_projection()
178 // returns true), this computes the appropriate UV
179 // corresponding to the indicated 3-d point. Seams that
180 // might be introduced on polygons that cross quadrants
181 // are closed up by ensuring the point is in the same
182 // quadrant as the indicated reference point.
183 ////////////////////////////////////////////////////////////////////
185 project_uv(const LPoint3d &pos, const LPoint3d &centroid) const {
186  nassertr(_map_uvs != NULL, LTexCoordd::zero());
187  return (this->*_map_uvs)(pos * _projection_matrix, centroid * _projection_matrix);
188 }
189 
190 ////////////////////////////////////////////////////////////////////
191 // Function: MayaShaderColorDef::write
192 // Access: Public
193 // Description:
194 ////////////////////////////////////////////////////////////////////
195 void MayaShaderColorDef::
196 write(ostream &out) const {
197  if (_has_texture) {
198  out << " texture filename is " << _texture_filename << "\n"
199  << " texture name is " << _texture_name << "\n"
200  << " uv_set name is " << _uvset_name << "\n"
201  << " coverage is " << _coverage << "\n"
202  << " translate_frame is " << _translate_frame << "\n"
203  << " rotate_frame is " << _rotate_frame << "\n"
204  << " mirror is " << _mirror << "\n"
205  << " stagger is " << _stagger << "\n"
206  << " wrap_u is " << _wrap_u << "\n"
207  << " wrap_v is " << _wrap_v << "\n"
208  << " repeat_uv is " << _repeat_uv << "\n"
209  << " offset is " << _offset << "\n"
210  << " rotate_uv is " << _rotate_uv << "\n"
211  << " color_gain is " << _color_gain << "\n";
212 
213  } else if (_has_flat_color) {
214  out << " flat color is " << _flat_color << "\n";
215  }
216 }
217 
218 ////////////////////////////////////////////////////////////////////
219 // Function: MayaShaderColorDef::reset_maya_texture
220 // Access: Public
221 // Description: Changes the texture filename stored in the Maya file
222 // for this particular shader.
223 ////////////////////////////////////////////////////////////////////
225 reset_maya_texture(const Filename &texture) {
226  if (_color_object != (MObject *)NULL) {
227  _has_texture = set_string_attribute(*_color_object, "fileTextureName",
228  texture.to_os_generic());
229  _texture_filename = texture;
230 
231  if (!_has_texture) {
232  maya_cat.error()
233  << "Unable to reset texture filename.\n";
234  }
235 
236  return _has_texture;
237  }
238 
239  maya_cat.error()
240  << "Attempt to reset texture on Maya object that has no color set.\n";
241  return false;
242 }
243 
244 
245 ////////////////////////////////////////////////////////////////////
246 // Function: MayaShaderColorDef::get_panda_uvset_name
247 // Access: Private
248 // Description: Maya's default uvset name is "map1". Panda's default
249 // uvset name is "default". Otherwise, leaves uvset
250 // name untranslated.
251 ////////////////////////////////////////////////////////////////////
254  if (_uvset_name == "map1") {
255  return "default";
256  }
257  return _uvset_name;
258 }
259 
260 ////////////////////////////////////////////////////////////////////
261 // Function: MayaShaderColorDef::find_textures_legacy
262 // Access: Private
263 // Description: This is part of the deprecated codepath.
264 // Determines the surface color specified by the shader.
265 // This includes texturing and other advanced shader
266 // properties.
267 ////////////////////////////////////////////////////////////////////
268 void MayaShaderColorDef::
269 find_textures_legacy(MayaShader *shader, MObject color, bool trans) {
270  LRGBColor color_gain;
271  if (get_vec3_attribute(color, "colorGain", color_gain)) {
272  color_gain[0] = color_gain[0] > 1.0 ? 1.0 : color_gain[0];
273  color_gain[0] = color_gain[0] < 0.0 ? 0.0 : color_gain[0];
274  _color_gain[0] *= color_gain[0];
275  color_gain[1] = color_gain[1] > 1.0 ? 1.0 : color_gain[1];
276  color_gain[1] = color_gain[1] < 0.0 ? 0.0 : color_gain[1];
277  _color_gain[1] *= color_gain[1];
278  color_gain[2] = color_gain[2] > 1.0 ? 1.0 : color_gain[2];
279  color_gain[2] = color_gain[2] < 0.0 ? 0.0 : color_gain[2];
280  _color_gain[2] *= color_gain[2];
281  }
282  PN_stdfloat alpha_gain;
283  if (get_maya_attribute(color, "alphaGain", alpha_gain)) {
284  alpha_gain = alpha_gain > 1.0 ? 1.0 : alpha_gain;
285  alpha_gain = alpha_gain < 0.0 ? 0.0 : alpha_gain;
286  _color_gain[3] *= alpha_gain;
287  }
288  if (color.hasFn(MFn::kFileTexture)) {
289  MFnDependencyNode dfn(color);
290  _color_object = new MObject(color);
291  _texture_name = dfn.name().asChar();
292  string filename;
293  _has_texture = get_string_attribute(color, "fileTextureName", filename);
294  _has_texture = _has_texture && !filename.empty();
295  if (_has_texture) {
296  _texture_filename = Filename::from_os_specific(filename);
297  if (_texture_filename.is_directory()) {
298  maya_cat.warning()
299  << "Shader " << shader->get_name()
300  << " references texture filename " << filename
301  << " which is a directory; clearing.\n";
302  _has_texture = false;
303  set_string_attribute(color, "fileTextureName", "");
304  }
305  }
306 
307  get_vec2_attribute(color, "coverage", _coverage);
308  get_vec2_attribute(color, "translateFrame", _translate_frame);
309  get_angle_attribute(color, "rotateFrame", _rotate_frame);
310 
311  //get_bool_attribute(color, "alphaIsLuminance", _alpha_is_luminance);
312 
313  get_bool_attribute(color, "mirror", _mirror);
314  get_bool_attribute(color, "stagger", _stagger);
315  get_bool_attribute(color, "wrapU", _wrap_u);
316  get_bool_attribute(color, "wrapV", _wrap_v);
317 
318  get_vec2_attribute(color, "repeatUV", _repeat_uv);
319  get_vec2_attribute(color, "offset", _offset);
320  get_angle_attribute(color, "rotateUV", _rotate_uv);
321 
322  if (!trans) {
323  if (maya_cat.is_debug()) {
324  maya_cat.debug() << "pushed a file texture" << endl;
325  }
326  shader->_color.push_back(this);
327  }
328 
329  } else if (color.hasFn(MFn::kProjection)) {
330  if (maya_cat.is_debug()) {
331  maya_cat.debug() << "reading a projection texture" << endl;
332  }
333  // This is a projected texture. We will have to step one level
334  // deeper to find the actual texture.
335  MFnDependencyNode projection_fn(color);
336  MPlug image_plug = projection_fn.findPlug("image");
337  if (!image_plug.isNull()) {
338  MPlugArray image_pa;
339  image_plug.connectedTo(image_pa, true, false);
340 
341  for (size_t i = 0; i < image_pa.length(); i++) {
342  find_textures_legacy(shader, image_pa[0].node());
343  }
344  }
345 
346  if (!get_mat4d_attribute(color, "placementMatrix", _projection_matrix)) {
347  _projection_matrix = LMatrix4d::ident_mat();
348  }
349 
350  // The uAngle and vAngle might be used for certain kinds of
351  // projections.
352  if (!get_angle_attribute(color, "uAngle", _u_angle)) {
353  _u_angle = 360.0;
354  }
355  if (!get_angle_attribute(color, "vAngle", _v_angle)) {
356  _v_angle = 180.0;
357  }
358 
359  string type;
360  if (get_enum_attribute(color, "projType", type)) {
361  set_projection_type(type);
362  }
363 
364  } else if (color.hasFn(MFn::kLayeredTexture)) {
365  if (maya_cat.is_debug()) {
366  maya_cat.debug() << "Found layered texture" << endl;
367  }
368 
369  int blendValue;
370  MStatus status;
371  MPlugArray color_pa;
372  MFnDependencyNode layered_fn(color);
373  layered_fn.getConnections(color_pa);
374  MPlug inputsPlug = layered_fn.findPlug("inputs", &status);
375  MPlug blendModePlug = layered_fn.findPlug("blendMode", &status);
376 
377  if (maya_cat.is_debug()) {
378  maya_cat.debug() << "number of connections: " << color_pa.length() << endl;
379  }
380  bool first = true;
381  BlendType bt = BT_modulate;
382  for (size_t i=0; i<color_pa.length(); ++i) {
383  MPlug pl = color_pa[i];
384  MPlugArray pla;
385  pl.connectedTo(pla, true, false);
386 
387  // First figure out the blend mode intended for this shadercolordef
388  int li = pl.logicalIndex();
389  if (li > -1) {
390  // found a blend mode
391  if (maya_cat.is_spam()) {
392  maya_cat.spam() << "*** Start doIt... ***" << endl;
393  maya_cat.spam() << "inputsPlug Name: " << inputsPlug.name() << endl;
394  }
395  status = blendModePlug.selectAncestorLogicalIndex(li,inputsPlug);
396  blendModePlug.getValue(blendValue);
397 
398  if (maya_cat.is_spam()) {
399  maya_cat.spam()
400  << blendModePlug.name() << ": has value " << blendValue << endl;
401  }
402 
403  MFnEnumAttribute blendModeEnum(blendModePlug);
404  MString blendName = blendModeEnum.fieldName(blendValue, &status);
405 
406  switch (blendValue) {
407  case 1:
408  bt = BT_decal;
409  get_bool_attribute(color, "interpolate", _interpolate);
410  maya_cat.info() << "interpolate: " << _interpolate << endl;
411  _keep_color = true;
412  break;
413  case 6:
414  bt = BT_modulate;
415  get_bool_attribute(color, "keepAlpha", _keep_alpha);
416  maya_cat.info() << "keepAlpha: " << _keep_alpha << endl;
417  break;
418  case 4:
419  bt = BT_add;
420  break;
421  }
422  maya_cat.info() << layered_fn.name() << ": blendMode used " << blendName << endl;
423  if (maya_cat.is_spam()) {
424  maya_cat.spam() << "*** END doIt... ***" << endl;
425  }
426 
427  // advance to the next plug, because that is where the shader info are
428  pl = color_pa[++i];
429  pl.connectedTo(pla, true, false);
430  }
431  for (size_t j=0; j<pla.length(); ++j) {
432  //maya_cat.debug() << pl.name() << " is(pl) " << pl.node().apiTypeStr() << endl;
433  //maya_cat.debug() << pla[j].name() << " is(pla) " << pla[j].node().apiTypeStr() << endl;
434  string pla_name = pla[j].name().asChar();
435  // sometimes, by default, maya gives a outAlpha on subsequent plugs, ignore that
436  if (pla_name.find("outAlpha") != string::npos) {
437  // top texture has an alpha channel, so make sure that this alpha is retained by egg file
438  if (maya_cat.is_debug()) {
439  maya_cat.debug() << pl.name().asChar() << ":has alpha channel" << pla_name << endl;
440  }
441  _has_alpha_channel = true;
442  continue;
443  }
444  if (!first) {
445  if (maya_cat.is_debug()) {
446  maya_cat.debug() << pl.name().asChar() << " next:connectedTo: " << pla_name << endl;
447  }
448  MayaShaderColorDef *color_p = new MayaShaderColorDef;
449  color_p->find_textures_legacy(shader, pla[j].node());
450  color_p->_blend_type = bt;
451  size_t loc = color_p->_texture_name.find('.',0);
452  if (loc != string::npos) {
453  color_p->_texture_name.resize(loc);
454  }
455  if (maya_cat.is_debug()) {
456  maya_cat.debug() << "uv_name : " << color_p->_texture_name << endl;
457  }
458  }
459  else {
460  if (maya_cat.is_debug()) {
461  maya_cat.debug() << pl.name().asChar() << " first:connectedTo: " << pla_name << endl;
462  }
463  find_textures_legacy(shader, pla[j].node());
464  _texture_name.assign(pla[j].name().asChar());
465  _blend_type = bt;
466  size_t loc = _texture_name.find('.',0);
467  if (loc != string::npos) {
468  _texture_name.resize(loc);
469  }
470  if (maya_cat.is_debug()) {
471  maya_cat.debug() << "uv_name : " << _texture_name << endl;
472  }
473  first = false;
474  }
475  }
476  }
477  } else {
478  // This shader wasn't understood.
479  if (maya_cat.is_debug()) {
480  maya_cat.info()
481  << "**Don't know how to interpret color attribute type "
482  << color.apiTypeStr() << "\n";
483 
484  } else {
485  // If we don't have a heavy verbose count, only report each type
486  // of unsupported shader once.
487  static pset<MFn::Type> bad_types;
488  if (bad_types.insert(color.apiType()).second) {
489  maya_cat.info()
490  << "**Don't know how to interpret color attribute type "
491  << color.apiTypeStr() << "\n";
492  }
493  }
494  }
495 }
496 
497 ////////////////////////////////////////////////////////////////////
498 // Function: MayaShaderColorDef::find_textures
499 // Access: Private
500 // Description: Search to find any file textures that lead into the
501 // given input plug. Any textures found will be added
502 // to the provided MayaShaderColorList.
503 ////////////////////////////////////////////////////////////////////
504 void MayaShaderColorDef::
505 find_textures_modern(const string &shadername, MayaShaderColorList &list, MPlug inplug, bool is_alpha) {
506 
507  MPlugArray outplugs;
508  inplug.connectedTo(outplugs, true, false);
509  if (outplugs.length() == 0) {
510  return;
511  }
512  if (outplugs.length() > 1) {
513  // Only one output plug should be connected to a given input plug.
514  maya_cat.warning()
515  << "Shader " << shadername << " has weird plug connections.\n";
516  return;
517  }
518  MPlug outplug = outplugs[0];
519  MObject source = outplug.node();
520  MFnDependencyNode sourceFn(source);
521 
522  if (source.hasFn(MFn::kFileTexture)) {
523 
524  string filename;
525  bool hasfn = get_string_attribute(source, "fileTextureName", filename);
526  if ((!hasfn) || (filename.empty())) {
527  maya_cat.warning()
528  << "Shader " << shadername << " references file texture "
529  << "with no file name, ignoring invalid file texture.\n";
530  return;
531  }
532  Filename fn = filename;
533  if (fn.is_directory()) {
534  maya_cat.warning()
535  << "Shader " << shadername << " references file name "
536  << filename << " which is a directory, ignoring it.\n";
537  return;
538  }
539 
541 
542  def->_color_object = new MObject(source);
543  def->_texture_filename = Filename::from_os_specific(filename);
544  def->_texture_name = sourceFn.name().asChar();
545 
546  get_vec2_attribute(source, "coverage", def->_coverage);
547  get_vec2_attribute(source, "translateFrame", def->_translate_frame);
548  get_angle_attribute(source, "rotateFrame", def->_rotate_frame);
549 
550  get_bool_attribute(source, "mirror", def->_mirror);
551  get_bool_attribute(source, "stagger", def->_stagger);
552  get_bool_attribute(source, "wrapU", def->_wrap_u);
553  get_bool_attribute(source, "wrapV", def->_wrap_v);
554 
555  get_vec2_attribute(source, "repeatUV", def->_repeat_uv);
556  get_vec2_attribute(source, "offset", def->_offset);
557  get_angle_attribute(source, "rotateUV", def->_rotate_uv);
558 
559  LRGBColor color_gain;
560  PN_stdfloat alpha_gain;
561  get_vec3_attribute(source, "colorGain", color_gain);
562  get_maya_attribute(source, "alphaGain", alpha_gain);
563  def->_color_gain[0] = color_gain[0];
564  def->_color_gain[1] = color_gain[1];
565  def->_color_gain[2] = color_gain[2];
566  def->_color_gain[3] = alpha_gain;
567 
568  def->_is_alpha = is_alpha;
569 
570  if (maya_cat.is_debug()) {
571  maya_cat.debug() << "pushed a file texture" << endl;
572  }
573  list.push_back(def);
574 
575  return;
576  }
577 
578  if (source.hasFn(MFn::kProjection)) {
579  // This is a projected texture. We will have to step one level
580  // deeper to find the actual texture.
581  size_t before = list.size();
582  MPlug image_plug = sourceFn.findPlug("image");
583  if (!image_plug.isNull()) {
584  MPlugArray image_pa;
585  image_plug.connectedTo(image_pa, true, false);
586 
587  for (size_t i = 0; i < image_pa.length(); i++) {
588  find_textures_modern(shadername, list, image_pa[0], is_alpha);
589  }
590  }
591 
592  // Now apply any inherited attributes to all textures found.
593 
594  for (size_t i=before; i<list.size(); i++) {
595  MayaShaderColorDef *def = list[i];
596 
597  if (!get_mat4d_attribute(source, "placementMatrix", def->_projection_matrix)) {
598  def->_projection_matrix = LMatrix4d::ident_mat();
599  }
600 
601  // The uAngle and vAngle might be used for certain kinds of
602  // projections.
603  if (!get_angle_attribute(source, "uAngle", def->_u_angle)) {
604  def->_u_angle = 360.0;
605  }
606  if (!get_angle_attribute(source, "vAngle", def->_v_angle)) {
607  def->_v_angle = 180.0;
608  }
609 
610  string type;
611  if (get_enum_attribute(source, "projType", type)) {
612  def->set_projection_type(type);
613  }
614  }
615  return;
616  }
617 
618  if (source.hasFn(MFn::kLayeredTexture)) {
619  if (maya_cat.is_debug()) {
620  maya_cat.debug() << "Found layered texture" << endl;
621  }
622 
623  MPlug inputsPlug = sourceFn.findPlug("inputs");
624  size_t nlayers = inputsPlug.numElements();
625  for (size_t layer=0; layer<nlayers; layer++) {
626  MPlug elt = inputsPlug.elementByPhysicalIndex(layer);
627  MPlug color;
628  MPlug blend;
629  for (size_t j=0; j<elt.numChildren(); j++) {
630  MPlug child = elt.child(j);
631  MFnAttribute att(child.attribute());
632  if (att.name() == "color") color = child;
633  if (att.name() == "blendMode") blend = child;
634  }
635  if (color.isNull() || blend.isNull()) {
636  maya_cat.warning() << "Invalid layered texture - bad inputs.\n";
637  return;
638  }
639  size_t before = list.size();
640  find_textures_modern(shadername, list, color, is_alpha);
641  int blendValue;
642  blend.getValue(blendValue);
643  for (size_t sub=before; sub<list.size(); sub++) {
644  MayaShaderColorDef *def = list[sub];
645  switch (blendValue) {
646  case 1: def->_blend_type = BT_decal; break;
647  case 6: def->_blend_type = BT_modulate; break;
648  case 4: def->_blend_type = BT_add; break;
649  }
650  }
651  }
652  return;
653  }
654 
655  if (source.apiType() == MFn::kReverse) {
656  MPlug input_plug = sourceFn.findPlug("input");
657  find_textures_modern(shadername, list, input_plug, is_alpha);
658  return;
659  }
660 
661  // This shader wasn't understood.
662  if (maya_cat.is_debug()) {
663  maya_cat.info()
664  << "**Don't know how to interpret color attribute type "
665  << source.apiTypeStr() << "\n";
666  } else {
667  // If we don't have a heavy verbose count, only report each type
668  // of unsupported shader once.
669  static pset<MFn::Type> bad_types;
670  if (bad_types.insert(source.apiType()).second) {
671  maya_cat.warning()
672  << "Don't know how to export a shader of type "
673  << source.apiTypeStr() << " " << sourceFn.type() << "\n";
674  }
675  }
676 }
677 
678 ////////////////////////////////////////////////////////////////////
679 // Function: MayaShaderColorDef::set_projection_type
680 // Access: Private
681 // Description: Sets up the shader to apply UV's according to the
682 // indicated projection type.
683 ////////////////////////////////////////////////////////////////////
684 void MayaShaderColorDef::
685 set_projection_type(const string &type) {
686  if (cmp_nocase(type, "planar") == 0) {
687  _projection_type = PT_planar;
688  _map_uvs = &MayaShaderColorDef::map_planar;
689 
690  // The Planar projection normally projects to a range (-1, 1) in
691  // both axes. Scale this into our UV range of (0, 1).
692  _projection_matrix = _projection_matrix * LMatrix4d(0.5, 0.0, 0.0, 0.0,
693  0.0, 0.5, 0.0, 0.0,
694  0.0, 0.0, 1.0, 0.0,
695  0.5, 0.5, 0.0, 1.0);
696 
697  } else if (cmp_nocase(type, "cylindrical") == 0) {
698  _projection_type = PT_cylindrical;
699  _map_uvs = &MayaShaderColorDef::map_cylindrical;
700 
701  // The cylindrical projection is orthographic in the Y axis; scale
702  // the range (-1, 1) in this axis into our UV range (0, 1).
703  _projection_matrix = _projection_matrix * LMatrix4d(1.0, 0.0, 0.0, 0.0,
704  0.0, 0.5, 0.0, 0.0,
705  0.0, 0.0, 1.0, 0.0,
706  0.0, 0.5, 0.0, 1.0);
707 
708  } else if (cmp_nocase(type, "spherical") == 0) {
709  _projection_type = PT_spherical;
710  _map_uvs = &MayaShaderColorDef::map_spherical;
711 
712  } else {
713  // Other projection types are currently unimplemented by the
714  // converter.
715  maya_cat.error()
716  << "Don't know how to handle type " << type << " projections.\n";
717  _projection_type = PT_off;
718  _map_uvs = NULL;
719  }
720 }
721 
722 ////////////////////////////////////////////////////////////////////
723 // Function: MayaShaderColorDef::map_planar
724 // Access: Private
725 // Description: Computes a UV based on the given point in space,
726 // using a planar projection.
727 ////////////////////////////////////////////////////////////////////
728 LPoint2d MayaShaderColorDef::
729 map_planar(const LPoint3d &pos, const LPoint3d &) const {
730  // A planar projection is about as easy as can be. We ignore the Z
731  // axis, and project the point into the XY plane. Done.
732  return LPoint2d(pos[0], pos[1]);
733 }
734 
735 ////////////////////////////////////////////////////////////////////
736 // Function: MayaShaderColorDef::map_spherical
737 // Access: Private
738 // Description: Computes a UV based on the given point in space,
739 // using a spherical projection.
740 ////////////////////////////////////////////////////////////////////
741 LPoint2d MayaShaderColorDef::
742 map_spherical(const LPoint3d &pos, const LPoint3d &centroid) const {
743  // To compute the x position on the frame, we only need to consider
744  // the angle of the vector about the Y axis. Project the vector
745  // into the XZ plane to do this.
746 
747  LVector2d xz(pos[0], pos[2]);
748  double xz_length = xz.length();
749 
750  if (xz_length < 0.01) {
751  // If we have a point on or near either pole, we've got problems.
752  // This point maps to the entire bottom edge of the image, so
753  // which U value should we choose? It does make a difference,
754  // especially if we have a number of polygons around the south
755  // pole that all share the common vertex.
756 
757  // We choose the U value based on the polygon's centroid.
758  xz.set(centroid[0], centroid[2]);
759  }
760 
761  // Now, if the polygon crosses the seam, we also have problems.
762  // Make sure that the u value is in the same half of the texture as
763  // the centroid's u value.
764  double u = rad_2_deg(atan2(xz[0], xz[1])) / (2.0 * _u_angle);
765  double c = rad_2_deg(atan2(centroid[0], centroid[2])) / (2.0 * _u_angle);
766 
767  if (u - c > 0.5) {
768  u -= floor(u - c + 0.5);
769  } else if (u - c < -0.5) {
770  u += floor(c - u + 0.5);
771  }
772 
773  // Now rotate the vector into the YZ plane, and the V value is based
774  // on the latitude: the angle about the X axis.
775  LVector2d yz(pos[1], xz_length);
776  double v = rad_2_deg(atan2(yz[0], yz[1])) / (2.0 * _v_angle);
777 
778  LPoint2d uv(u - 0.5, v - 0.5);
779 
780  nassertr(fabs(u - c) <= 0.5, uv);
781  return uv;
782 }
783 
784 ////////////////////////////////////////////////////////////////////
785 // Function: MayaShaderColorDef::map_cylindrical
786 // Access: Private
787 // Description: Computes a UV based on the given point in space,
788 // using a cylindrical projection.
789 ////////////////////////////////////////////////////////////////////
790 LPoint2d MayaShaderColorDef::
791 map_cylindrical(const LPoint3d &pos, const LPoint3d &centroid) const {
792  // This is almost identical to the spherical projection, except for
793  // the computation of V.
794 
795  LVector2d xz(pos[0], pos[2]);
796  double xz_length = xz.length();
797 
798  if (xz_length < 0.01) {
799  // A cylindrical mapping has the same singularity problem at the
800  // pole as a spherical mapping does: points at the pole do not map
801  // to a single point on the texture. (It's technically a slightly
802  // different problem: in a cylindrical mapping, points at the pole
803  // do not map to any point on the texture, while in a spherical
804  // mapping, points at the pole map to the top or bottom edge of
805  // the texture. But this is a technicality that doesn't really
806  // apply to us.) We still solve it the same way: if our point is
807  // at or near the pole, compute the angle based on the centroid of
808  // the polygon (which we assume is further from the pole).
809  xz.set(centroid[0], centroid[2]);
810  }
811 
812  // And cylinders do still have a seam at the back.
813  double u = rad_2_deg(atan2(xz[0], xz[1])) / _u_angle;
814  double c = rad_2_deg(atan2(centroid[0], centroid[2])) / _u_angle;
815 
816  if (u - c > 0.5) {
817  u -= floor(u - c + 0.5);
818  } else if (u - c < -0.5) {
819  u += floor(c - u + 0.5);
820  }
821 
822  // For a cylindrical mapping, the V value comes directly from Y.
823  // Easy.
824  LPoint2d uv(u - 0.5, pos[1]);
825 
826  nassertr(fabs(u - c) <= 0.5, uv);
827  return uv;
828 }
static const LPoint2d & zero()
Returns a zero-length point.
Definition: lpoint2.h:557
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:105
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
static LMatrix3d rotate_mat(double angle)
Returns a matrix that rotates by the given angle in degrees counterclockwise.
Definition: lmatrix.h:8346
This is a two-component vector offset.
Definition: lvector2.h:429
LTexCoordd project_uv(const LPoint3d &pos, const LPoint3d &ref_point) const
If the shader has a projection (has_projection() returns true), this computes the appropriate UV corr...
string get_panda_uvset_name()
Maya&#39;s default uvset name is "map1".
bool has_projection() const
Returns true if the shader has a projection in effect.
LMatrix3d compute_texture_matrix() const
Returns a texture matrix corresponding to the texture transforms indicated by the shader...
This is a two-component point in space.
Definition: lpoint2.h:424
static LMatrix3d translate_mat(const LVecBase2d &trans)
Returns a matrix that applies the indicated translation.
Definition: lmatrix.h:8320
string to_os_generic() const
This is similar to to_os_specific(), but it is designed to generate a filename that can be understood...
Definition: filename.cxx:1261
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
This is a 3-by-3 transform matrix.
Definition: lmatrix.h:4375
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 "color" channel for a particular...
static const LMatrix4d & ident_mat()
Returns an identity matrix.
Definition: lmatrix.h:5168
double length() const
Returns the length of the vector, by the Pythagorean theorem.
Definition: lvecBase2.h:1816
Corresponds to a single "shader" in Maya.
Definition: mayaShader.h:35
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1456
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:544
static LMatrix3d scale_mat(const LVecBase2d &scale)
Returns a matrix that applies the indicated scale in each of the two axes.
Definition: lmatrix.h:8359
This is our own Panda specialization on the default STL set.
Definition: pset.h:52
bool reset_maya_texture(const Filename &texture)
Changes the texture filename stored in the Maya file for this particular shader.
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