1 ////////////////////////////////////////////////////////////////////////////////
2 // material.cpp
3 // Author       : Francesco Giordana
4 // Sponsored by : Anygma N.V. (http://www.nazooka.com)
5 // Start Date   : January 13, 2005
6 // Copyright    : (C) 2006 by Francesco Giordana
7 // Email        : fra.giordana@tiscali.it
8 ////////////////////////////////////////////////////////////////////////////////
9 
10 /*********************************************************************************
11 *                                                                                *
12 *   This program is free software; you can redistribute it and/or modify         *
13 *   it under the terms of the GNU Lesser General Public License as published by  *
14 *   the Free Software Foundation; either version 2 of the License, or            *
15 *   (at your option) any later version.                                          *
16 *                                                                                *
17 **********************************************************************************/
18 
19 #include "material.h"
20 
21 namespace OgreMayaExporter
22 {
23     // Constructor
Material()24     Material::Material()
25     {
26         clear();
27     }
28 
29 
30     // Destructor
~Material()31     Material::~Material()
32     {
33     }
34 
35 
36     // Get material name
name()37     MString& Material::name()
38     {
39         return m_name;
40     }
41 
42 
43     // Clear data
clear()44     void Material::clear()
45     {
46         m_name = "";
47         m_type = MT_LAMBERT;
48         m_lightingOff = false;
49         m_isTransparent = false;
50         m_isTextured = false;
51         m_isMultiTextured = false;
52         m_ambient = MColor(0,0,0,0);
53         m_diffuse = MColor(0,0,0,0);
54         m_specular = MColor(0,0,0,0);
55         m_emissive = MColor(0,0,0,0);
56         m_textures.clear();
57     }
58 
59 
60     // Load material data
load(MFnDependencyNode * pShader,MStringArray & uvsets,ParamList & params)61     MStatus Material::load(MFnDependencyNode* pShader,MStringArray& uvsets,ParamList& params)
62     {
63         MStatus stat;
64         clear();
65         //read material name, adding the requested prefix
66         MString tmpStr = params.matPrefix;
67         if (tmpStr != "")
68             tmpStr += "/";
69         tmpStr += pShader->name();
70         MStringArray tmpStrArray;
71         tmpStr.split(':',tmpStrArray);
72         m_name = "";
73         for (int i=0; i<tmpStrArray.length(); i++)
74         {
75             m_name += tmpStrArray[i];
76             if (i < tmpStrArray.length()-1)
77                 m_name += "_";
78         }
79 
80         //check if we want to export with lighting off option
81         m_lightingOff = params.lightingOff;
82 
83         // GET MATERIAL DATA
84 
85         // Check material type
86         if (pShader->object().hasFn(MFn::kPhong))
87         {
88             stat = loadPhong(pShader);
89         }
90         else if (pShader->object().hasFn(MFn::kBlinn))
91         {
92             stat = loadBlinn(pShader);
93         }
94         else if (pShader->object().hasFn(MFn::kLambert))
95         {
96             stat = loadLambert(pShader);
97         }
98         else if (pShader->object().hasFn(MFn::kPluginHwShaderNode))
99         {
100             stat = loadCgFxShader(pShader);
101         }
102         else
103         {
104             stat = loadSurfaceShader(pShader);
105         }
106 
107         // Get textures data
108         MPlugArray colorSrcPlugs;
109         MPlugArray texSrcPlugs;
110         MPlugArray placetexSrcPlugs;
111         if (m_isTextured)
112         {
113             // Translate multiple textures if material is multitextured
114             if (m_isMultiTextured)
115             {
116                 // Get layered texture node
117                 MFnDependencyNode* pLayeredTexNode = NULL;
118                 if (m_type == MT_SURFACE_SHADER)
119                     pShader->findPlug("outColor").connectedTo(colorSrcPlugs,true,false);
120                 else
121                     pShader->findPlug("color").connectedTo(colorSrcPlugs,true,false);
122                 for (int i=0; i<colorSrcPlugs.length(); i++)
123                 {
124                     if (colorSrcPlugs[i].node().hasFn(MFn::kLayeredTexture))
125                     {
126                         pLayeredTexNode = new MFnDependencyNode(colorSrcPlugs[i].node());
127                         continue;
128                     }
129                 }
130 
131                 // Get inputs to layered texture
132                 MPlug inputsPlug = pLayeredTexNode->findPlug("inputs");
133 
134                 // Scan inputs and export textures
135                 for (int i=inputsPlug.numElements()-1; i>=0; i--)
136                 {
137                     MFnDependencyNode* pTextureNode = NULL;
138                     // Search for a connected texture
139                     inputsPlug[i].child(0).connectedTo(colorSrcPlugs,true,false);
140                     for (int j=0; j<colorSrcPlugs.length(); j++)
141                     {
142                         if (colorSrcPlugs[j].node().hasFn(MFn::kFileTexture))
143                         {
144                             pTextureNode = new MFnDependencyNode(colorSrcPlugs[j].node());
145                             continue;
146                         }
147                     }
148 
149                     // Translate the texture if it was found
150                     if (pTextureNode)
151                     {
152                         // Get blend mode
153                         TexOpType opType;
154                         short bm;
155                         inputsPlug[i].child(2).getValue(bm);
156                         switch(bm)
157                         {
158                         case 0:
159                             opType = TOT_REPLACE;
160                             break;
161                         case 1:
162                             opType = TOT_ALPHABLEND;
163                             break;
164                         case 4:
165                             opType = TOT_ADD;
166                             break;
167                         case 6:
168                             opType = TOT_MODULATE;
169                             break;
170                         default:
171                             opType = TOT_MODULATE;
172                         }
173 
174                         stat = loadTexture(pTextureNode,opType,uvsets,params);
175                         delete pTextureNode;
176                         if (MS::kSuccess != stat)
177                         {
178                             std::cout << "Error loading layered texture\n";
179                             std::cout.flush();
180                             delete pLayeredTexNode;
181                             return MS::kFailure;
182                         }
183                     }
184                 }
185                 if (pLayeredTexNode)
186                     delete pLayeredTexNode;
187             }
188             // Else translate the single texture
189             else
190             {
191                 // Get texture node
192                 MFnDependencyNode* pTextureNode = NULL;
193                 if (m_type == MT_SURFACE_SHADER)
194                     pShader->findPlug("outColor").connectedTo(colorSrcPlugs,true,false);
195                 else
196                     pShader->findPlug("color").connectedTo(colorSrcPlugs,true,false);
197                 for (int i=0; i<colorSrcPlugs.length(); i++)
198                 {
199                     if (colorSrcPlugs[i].node().hasFn(MFn::kFileTexture))
200                     {
201                         pTextureNode = new MFnDependencyNode(colorSrcPlugs[i].node());
202                         continue;
203                     }
204                 }
205                 if (pTextureNode)
206                 {
207                     TexOpType opType = TOT_MODULATE;
208                     stat = loadTexture(pTextureNode,opType,uvsets,params);
209                     delete pTextureNode;
210                     if (MS::kSuccess != stat)
211                     {
212                         std::cout << "Error loading texture\n";
213                         std::cout.flush();
214                         return MS::kFailure;
215                     }
216                 }
217             }
218         }
219 
220         return MS::kSuccess;
221     }
222 
223 
224     // Load a surface shader
loadSurfaceShader(MFnDependencyNode * pShader)225     MStatus Material::loadSurfaceShader(MFnDependencyNode *pShader)
226     {
227         m_type = MT_SURFACE_SHADER;
228         MPlugArray colorSrcPlugs;
229         // Check if material is textured
230         pShader->findPlug("outColor").connectedTo(colorSrcPlugs,true,false);
231         for (int i=0; i<colorSrcPlugs.length(); i++)
232         {
233             if (colorSrcPlugs[i].node().hasFn(MFn::kFileTexture))
234             {
235                 m_isTextured = true;
236                 continue;
237             }
238             else if (colorSrcPlugs[i].node().hasFn(MFn::kLayeredTexture))
239             {
240                 m_isTextured = true;
241                 m_isMultiTextured = true;
242                 continue;
243             }
244         }
245 
246         // Check if material is transparent
247         float trasp;
248         pShader->findPlug("outTransparencyR").getValue(trasp);
249         if (pShader->findPlug("outTransparency").isConnected() || trasp>0.0f)
250             m_isTransparent = true;
251 
252         // Get material colours
253         if (m_isTextured)
254             m_diffuse = MColor(1.0,1.0,1.0,1.0);
255         else
256         {
257             pShader->findPlug("outColorR").getValue(m_diffuse.r);
258             pShader->findPlug("outColorG").getValue(m_diffuse.g);
259             pShader->findPlug("outColorB").getValue(m_diffuse.b);
260             float trasp;
261             pShader->findPlug("outTransparencyR").getValue(trasp);
262             m_diffuse.a = 1.0 - trasp;
263         }
264         m_ambient = MColor(0,0,0,1);
265         m_emissive = MColor(0,0,0,1);
266         m_specular = MColor(0,0,0,1);
267         return MS::kSuccess;
268     }
269 
270     // Load a lambert shader
loadLambert(MFnDependencyNode * pShader)271     MStatus Material::loadLambert(MFnDependencyNode *pShader)
272     {
273         MPlugArray colorSrcPlugs;
274         m_type = MT_LAMBERT;
275         MFnLambertShader* pLambert = new MFnLambertShader(pShader->object());
276         // Check if material is textured
277         pLambert->findPlug("color").connectedTo(colorSrcPlugs,true,false);
278         for (int i=0; i<colorSrcPlugs.length(); i++)
279         {
280             if (colorSrcPlugs[i].node().hasFn(MFn::kFileTexture))
281             {
282                 m_isTextured = true;
283                 continue;
284             }
285             else if (colorSrcPlugs[i].node().hasFn(MFn::kLayeredTexture))
286             {
287                 m_isTextured = true;
288                 m_isMultiTextured = true;
289                 continue;
290             }
291         }
292 
293         // Check if material is transparent
294         if (pLambert->findPlug("transparency").isConnected() || pLambert->transparency().r>0.0f)
295             m_isTransparent = true;
296 
297         // Get material colours
298         //diffuse colour
299         if (m_isTextured)
300             m_diffuse = MColor(1.0,1.0,1.0,1.0);
301         else
302         {
303             m_diffuse = pLambert->color();
304             m_diffuse.a = 1.0 - pLambert->transparency().r;
305         }
306         //ambient colour
307         m_ambient = pLambert->ambientColor();
308         //emissive colour
309         m_emissive = pLambert->incandescence();
310         //specular colour
311         m_specular = MColor(0,0,0,1);
312         delete pLambert;
313         return MS::kSuccess;
314     }
315 
316     // Load a phong shader
loadPhong(MFnDependencyNode * pShader)317     MStatus Material::loadPhong(MFnDependencyNode *pShader)
318     {
319         MPlugArray colorSrcPlugs;
320         m_type = MT_PHONG;
321         MFnPhongShader* pPhong = new MFnPhongShader(pShader->object());
322         // Check if material is textured
323         pPhong->findPlug("color").connectedTo(colorSrcPlugs,true,false);
324         for (int i=0; i<colorSrcPlugs.length(); i++)
325         {
326             if (colorSrcPlugs[i].node().hasFn(MFn::kFileTexture))
327             {
328                 m_isTextured = true;
329                 continue;
330             }
331             else if (colorSrcPlugs[i].node().hasFn(MFn::kLayeredTexture))
332             {
333                 m_isTextured = true;
334                 m_isMultiTextured = true;
335                 continue;
336             }
337         }
338 
339         // Check if material is transparent
340         if (pPhong->findPlug("transparency").isConnected() || pPhong->transparency().r>0.0f)
341             m_isTransparent = true;
342 
343         // Get material colours
344         //diffuse colour
345         if (m_isTextured)
346             m_diffuse = MColor(1.0,1.0,1.0,1.0);
347         else
348         {
349             m_diffuse = pPhong->color();
350             m_diffuse.a = 1.0 - pPhong->transparency().r;
351         }
352         //ambient colour
353         m_ambient = pPhong->ambientColor();
354         //emissive colour
355         m_emissive = pPhong->incandescence();
356         //specular colour
357         m_specular = pPhong->specularColor();
358         m_specular.a = pPhong->cosPower()*1.28;
359         delete pPhong;
360         return MS::kSuccess;
361     }
362 
363     // load a blinn shader
loadBlinn(MFnDependencyNode * pShader)364     MStatus Material::loadBlinn(MFnDependencyNode *pShader)
365     {
366         MPlugArray colorSrcPlugs;
367         m_type = MT_BLINN;
368         MFnBlinnShader* pBlinn = new MFnBlinnShader(pShader->object());
369         // Check if material is textured
370         pBlinn->findPlug("color").connectedTo(colorSrcPlugs,true,false);
371         for (int i=0; i<colorSrcPlugs.length(); i++)
372         {
373             if (colorSrcPlugs[i].node().hasFn(MFn::kFileTexture))
374             {
375                 m_isTextured = true;
376                 continue;
377             }
378             else if (colorSrcPlugs[i].node().hasFn(MFn::kLayeredTexture))
379             {
380                 m_isTextured = true;
381                 m_isMultiTextured = true;
382                 continue;
383             }
384         }
385 
386         // Check if material is transparent
387         if (pBlinn->findPlug("transparency").isConnected() || pBlinn->transparency().r>0.0f)
388             m_isTransparent = true;
389 
390         // Get material colours
391         //diffuse colour
392         if (m_isTextured)
393             m_diffuse = MColor(1.0,1.0,1.0,1.0);
394         else
395         {
396             m_diffuse = pBlinn->color();
397             m_diffuse.a = 1.0 - pBlinn->transparency().r;
398         }
399         //ambient colour
400         m_ambient = pBlinn->ambientColor();
401         //emissive colour
402         m_emissive = pBlinn->incandescence();
403         //specular colour
404         m_specular = pBlinn->specularColor();
405         m_specular.a = 128.0-(128.0*pBlinn->eccentricity());
406         delete pBlinn;
407         return MS::kSuccess;
408     }
409 
410     // load a cgFx shader
loadCgFxShader(MFnDependencyNode * pShader)411     MStatus Material::loadCgFxShader(MFnDependencyNode *pShader)
412     {
413         m_type = MT_CGFX;
414         // Create a default white lambert
415         m_isTextured = false;
416         m_isMultiTextured = false;
417         m_diffuse = MColor(1.0,1.0,1.0,1.0);
418         m_specular = MColor(0,0,0,1);
419         m_emissive = MColor(0,0,0,1);
420         m_ambient = MColor(0,0,0,1);
421         return MS::kSuccess;
422     }
423 
424     // Load texture data from a texture node
loadTexture(MFnDependencyNode * pTexNode,TexOpType & opType,MStringArray & uvsets,ParamList & params)425     MStatus Material::loadTexture(MFnDependencyNode* pTexNode,TexOpType& opType,MStringArray& uvsets,ParamList& params)
426     {
427         Texture tex;
428         // Get texture filename
429         MString filename, absFilename;
430         MRenderUtil::exactFileTextureName(pTexNode->object(),absFilename);
431         filename = absFilename.substring(absFilename.rindex('/')+1,absFilename.length()-1);
432         MString command = "toNativePath(\"";
433         command += absFilename;
434         command += "\")";
435         MGlobal::executeCommand(command,absFilename);
436         tex.absFilename = absFilename;
437         tex.filename = filename;
438         tex.uvsetIndex = 0;
439         tex.uvsetName = "";
440         // Set texture operation type
441         tex.opType = opType;
442         // Get connections to uvCoord attribute of texture node
443         MPlugArray texSrcPlugs;
444         pTexNode->findPlug("uvCoord").connectedTo(texSrcPlugs,true,false);
445         // Get place2dtexture node (if connected)
446         MFnDependencyNode* pPlace2dTexNode = NULL;
447         for (int j=0; j<texSrcPlugs.length(); j++)
448         {
449             if (texSrcPlugs[j].node().hasFn(MFn::kPlace2dTexture))
450             {
451                 pPlace2dTexNode = new MFnDependencyNode(texSrcPlugs[j].node());
452                 continue;
453             }
454         }
455         // Get uvChooser node (if connected)
456         MFnDependencyNode* pUvChooserNode = NULL;
457         if (pPlace2dTexNode)
458         {
459             MPlugArray placetexSrcPlugs;
460             pPlace2dTexNode->findPlug("uvCoord").connectedTo(placetexSrcPlugs,true,false);
461             for (int j=0; j<placetexSrcPlugs.length(); j++)
462             {
463                 if (placetexSrcPlugs[j].node().hasFn(MFn::kUvChooser))
464                 {
465                     pUvChooserNode = new MFnDependencyNode(placetexSrcPlugs[j].node());
466                     continue;
467                 }
468             }
469         }
470         // Get uvset index
471         if (pUvChooserNode)
472         {
473             bool foundMesh = false;
474             bool foundUvset = false;
475             MPlug uvsetsPlug = pUvChooserNode->findPlug("uvSets");
476             MPlugArray uvsetsSrcPlugs;
477             for (int i=0; i<uvsetsPlug.evaluateNumElements() && !foundMesh; i++)
478             {
479                 uvsetsPlug[i].connectedTo(uvsetsSrcPlugs,true,false);
480                 for (int j=0; j<uvsetsSrcPlugs.length() && !foundMesh; j++)
481                 {
482                     if (uvsetsSrcPlugs[j].node().hasFn(MFn::kMesh))
483                     {
484                         uvsetsSrcPlugs[j].getValue(tex.uvsetName);
485                         for (int k=0; k<uvsets.length() && !foundUvset; k++)
486                         {
487                             if (uvsets[k] == tex.uvsetName)
488                             {
489                                 tex.uvsetIndex = k;
490                                 foundUvset = true;
491                             }
492                         }
493                     }
494                 }
495             }
496         }
497         // Get texture options from Place2dTexture node
498         if (pPlace2dTexNode)
499         {
500             // Get address mode
501             //U
502             bool wrapU, mirrorU;
503             pPlace2dTexNode->findPlug("wrapU").getValue(wrapU);
504             pPlace2dTexNode->findPlug("mirrorU").getValue(mirrorU);
505             if (mirrorU)
506                 tex.am_u = TAM_MIRROR;
507             else if (wrapU)
508                 tex.am_u = TAM_WRAP;
509             else
510                 tex.am_u = TAM_CLAMP;
511             // V
512             bool wrapV,mirrorV;
513             pPlace2dTexNode->findPlug("wrapV").getValue(wrapV);
514             pPlace2dTexNode->findPlug("mirrorV").getValue(mirrorV);
515             if (mirrorV)
516                 tex.am_v = TAM_MIRROR;
517             else if (wrapV)
518                 tex.am_v = TAM_WRAP;
519             else
520                 tex.am_v = TAM_CLAMP;
521             // Get texture scale
522             double covU,covV;
523             pPlace2dTexNode->findPlug("coverageU").getValue(covU);
524             pPlace2dTexNode->findPlug("coverageV").getValue(covV);
525             tex.scale_u = covU;
526             if (fabs(tex.scale_u) < PRECISION)
527                 tex.scale_u = 0;
528             tex.scale_v = covV;
529             if (fabs(tex.scale_v) < PRECISION)
530                 tex.scale_v = 0;
531             // Get texture scroll
532             double transU,transV;
533             pPlace2dTexNode->findPlug("translateFrameU").getValue(transU);
534             pPlace2dTexNode->findPlug("translateFrameV").getValue(transV);
535             tex.scroll_u = -0.5 * (covU-1.0)/covU - transU/covU;
536             if (fabs(tex.scroll_u) < PRECISION)
537                 tex.scroll_u = 0;
538             tex.scroll_v = 0.5 * (covV-1.0)/covV + transV/covV;
539             if (fabs(tex.scroll_v) < PRECISION)
540                 tex.scroll_v = 0;
541             // Get texture rotation
542             double rot;
543             pPlace2dTexNode->findPlug("rotateFrame").getValue(rot);
544             tex.rot = -rot;
545             if (fabs(rot) < PRECISION)
546                 tex.rot = 0;
547         }
548         // add texture to material texture list
549         m_textures.push_back(tex);
550         // free up memory
551         if (pUvChooserNode)
552             delete pUvChooserNode;
553         if (pPlace2dTexNode)
554             delete pPlace2dTexNode;
555 
556         return MS::kSuccess;
557     }
558 
559 
560     // Write material data to an Ogre material script file
writeOgreScript(ParamList & params)561     MStatus Material::writeOgreScript(ParamList &params)
562     {
563         //Start material description
564         params.outMaterial << "material " << m_name.asChar() << "\n";
565         params.outMaterial << "{\n";
566 
567         //Start technique description
568         params.outMaterial << "\ttechnique\n";
569         params.outMaterial << "\t{\n";
570 
571         //Start render pass description
572         params.outMaterial << "\t\tpass\n";
573         params.outMaterial << "\t\t{\n";
574         //set lighting off option if requested
575         if (m_lightingOff)
576             params.outMaterial << "\t\t\tlighting off\n\n";
577         //set phong shading if requested (default is gouraud)
578         if (m_type == MT_PHONG)
579             params.outMaterial << "\t\t\tshading phong\n";
580         //ambient colour
581         params.outMaterial << "\t\t\tambient " << m_ambient.r << " " << m_ambient.g << " " << m_ambient.b
582             << " " << m_ambient.a << "\n";
583         //diffuse colour
584         params.outMaterial << "\t\t\tdiffuse " << m_diffuse.r << " " << m_diffuse.g << " " << m_diffuse.b
585             << " " << m_diffuse.a << "\n";
586         //specular colour
587         params.outMaterial << "\t\t\tspecular " << m_specular.r << " " << m_specular.g << " " << m_specular.b
588             << " " << m_specular.a << "\n";
589         //emissive colour
590         params.outMaterial << "\t\t\temissive " << m_emissive.r << " " << m_emissive.g << " "
591             << m_emissive.b << "\n";
592         //if material is transparent set blend mode and turn off depth_writing
593         if (m_isTransparent)
594         {
595             params.outMaterial << "\n\t\t\tscene_blend alpha_blend\n";
596             params.outMaterial << "\t\t\tdepth_write off\n";
597         }
598         //write texture units
599         for (int i=0; i<m_textures.size(); i++)
600         {
601             //start texture unit description
602             params.outMaterial << "\n\t\t\ttexture_unit\n";
603             params.outMaterial << "\t\t\t{\n";
604             //write texture name
605             params.outMaterial << "\t\t\t\ttexture " << m_textures[i].filename.asChar() << "\n";
606             //write texture coordinate index
607             params.outMaterial << "\t\t\t\ttex_coord_set " << m_textures[i].uvsetIndex << "\n";
608             //write colour operation
609             switch (m_textures[i].opType)
610             {
611             case TOT_REPLACE:
612                 params.outMaterial << "\t\t\t\tcolour_op replace\n";
613                 break;
614             case TOT_ADD:
615                 params.outMaterial << "\t\t\t\tcolour_op add\n";
616                 break;
617             case TOT_MODULATE:
618                 params.outMaterial << "\t\t\t\tcolour_op modulate\n";
619                 break;
620             case TOT_ALPHABLEND:
621                 params.outMaterial << "\t\t\t\tcolour_op alpha_blend\n";
622                 break;
623             }
624             //write texture transforms
625             params.outMaterial << "\t\t\t\tscale " << m_textures[i].scale_u << " " << m_textures[i].scale_v << "\n";
626             params.outMaterial << "\t\t\t\tscroll " << m_textures[i].scroll_u << " " << m_textures[i].scroll_v << "\n";
627             params.outMaterial << "\t\t\t\trotate " << m_textures[i].rot << "\n";
628             //end texture unit desription
629             params.outMaterial << "\t\t\t}\n";
630         }
631 
632         //End render pass description
633         params.outMaterial << "\t\t}\n";
634 
635         //End technique description
636         params.outMaterial << "\t}\n";
637 
638         //End material description
639         params.outMaterial << "}\n";
640 
641         //Copy textures to output dir if required
642         if (params.copyTextures)
643             copyTextures(params);
644 
645         return MS::kSuccess;
646     }
647 
648 
649     // Copy textures to path specified by params
copyTextures(ParamList & params)650     MStatus Material::copyTextures(ParamList &params)
651     {
652         for (int i=0; i<m_textures.size(); i++)
653         {
654             // Copy file texture to output dir
655             MString command = "copy \"";
656             command += m_textures[i].absFilename;
657             command += "\" \"";
658             command += params.texOutputDir;
659             command += "\"";
660             system(command.asChar());
661         }
662         return MS::kSuccess;
663     }
664 
665 };  //end namespace