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