Dorian Fevrier's blog - Mot-clé - translationJe m’appelle FEVRIER Dorian, je suis infographiste 3D passionné par mon métier, l’informatique en général, l’internet, la programmation et l’évolution de tout ce petit monde. Vous trouverez sur ce blog des tutoriaux, mes coups de cœurs, avis, etc.2024-01-02T23:48:05+01:00FEVRIER Dorianurn:md5:695d9c73474c33ce3dab043823509c4bDotclearProject a mesh to another with Maya API (English Translation)urn:md5:0a8e2a12a431cfe2fc41231bce05af082011-07-31T20:28:00+02:002013-07-26T18:08:00+02:00NarannScript et codeapicodeenmayapythontranslationtuto<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_tn.png" alt="projection_mesh_api_tn.png" style="float:left; margin: 0 1em 1em 0;" title="projection_mesh_api_tn.png, fév. 2011" height="150" width="150" />This is the english translation of <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya">a tutorial</a> I've done that will allow you to project a mesh to another.</p>
<p>This is something that can be useful but most is pretty "fun" to do (some would say everything is relative :seSentCon: ) and it's a good way to learn fundamentals of matrice operations having a both simple and concrete example.</p>
<p>Amateur of the API, this tutorial is for you!</p> <center>:longBar:</center>
<blockquote><p>In advance, sorry for english. :baffed:</p></blockquote>
<p>Here is what we want to achieve:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_015.png" alt="projection_mesh_api_015.png" style="display:block; margin:0 auto;" title="projection_mesh_api_015.png, fév. 2011" height="378" width="511" /></p>
<p>A mesh projected on another.</p>
<p>And the scene from which we start:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_001.png" title="projection_mesh_api_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_001_m.jpg" alt="projection_mesh_api_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_001.png, fév. 2011" height="376" width="560" /></a></p>
<center>:longBar:</center>
<h5>Sommaire</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theory">Theory</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#startCode">Start code</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#attributesPreparation">Attributes preparation</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#prepareTheScene">Prepare the scene</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theComputeMethod">The compute method</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#matricesExplainedToCGArtists">Matrices explained to CG artists</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#backToTheCode">Back to the code!</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#goThroughEachVertexAndModifyIt">Go through each vertex and modify it</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#buildAMesh">Build a mesh</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#sourceCodeAndConclusion">Source code and conclusion</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#doThisWithoutPython">Update: Do this without Python</a></li>
</ul>
<center>:longBar:</center>
<h5>Theory <a name="theory"></a></h5>
<p>Once again, we begin with theory.</p>
<p>In facts, you'll see it's pretty simple because the harder part (the intersection) will be handle through <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">an API call</a>:</p>
<pre class="python python"><span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span>.<span style="color: black;">closestIntersection</span><span style="color: black;">(</span> ... <span style="color: black;">)</span></pre>
<p>This <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Method(computer_programming)">method</a> handle intersection of a ray (point+direction) on a mesh and return some infos (the most important: The position of the projected point).</p>
<p>Basically, we need three things:</p>
<ul>
<li>A point of origin (the one we want to project)</li>
<li>A direction (a vector)</li>
<li>A destination mesh</li>
</ul>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_002.png" title="projection_mesh_api_002.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_002_m.jpg" alt="projection_mesh_api_002.png" style="display:block; margin:0 auto;" title="projection_mesh_api_002.png, fév. 2011" height="410" width="560" /></a></p>
<p>The point of origin will be, of course, each vertex of the mesh to project (here, vertex numbers's are in yellow).</p>
<p>Direction will be the normal of the vertex to project (in green on the image. Okay, we do not see too much but put it a little good will damn! :cayMal: ).</p>
<p>And the destination mesh will obviously be the mesh that will receive the plan (in our case, a sphere).</p>
<p>So we recover, each time, a point and a normal (yellow crosses).</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_003.png" title="projection_mesh_api_003.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_003_m.jpg" alt="projection_mesh_api_003.png" style="display:block; margin:0 auto;" title="projection_mesh_api_003.png, fév. 2011" height="423" width="560" /></a></p>
<center><i>Forgive my "half-of-a-jpeg-dollar-moldy" diagram :pasClasse:</i></center>
<center>:longBar:</center>
<h5>Start code <a name="startCode"></a></h5>
<p>Here is the the base code:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
kPluginNodeTypeName = <span style="color: #483d8b;">"projectMesh"</span>
kPluginNodeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span> 0x80000 <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Node definition</span>
<span style="color: #ff7700;font-weight:bold;">class</span> projectMeshNode<span style="color: black;">(</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># constructor</span>
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #808080; font-style: italic;"># creator</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span> projectMeshNode<span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">return</span>
<span style="color: #808080; font-style: italic;"># initialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span> :
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span> kPluginNodeTypeName, kPluginNodeId, nodeCreator, nodeInitializer <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span>
<span style="color: #808080; font-style: italic;"># uninitialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span>:
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span> kPluginNodeId <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to unregister node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span></pre>
<p>If you already had wrote a Maya node in Python, this code shouldn't make you too afraid.</p>
<p>Quick to explain for others :baffed: .</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span></pre>
<p>Import the main modules.</p>
<ul>
<li>The <em>sys</em> module is used to create error message when Maya load/unload the plugin (see below).</li>
<li>The other two are used to call Maya API methods.</li>
</ul>
<p>Nothing complicated.</p>
<pre class="python python">kPluginNodeTypeName = <span style="color: #483d8b;">"projectMesh"</span>
kPluginNodeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span> 0x80000 <span style="color: black;">)</span></pre>
<ul>
<li><em>kPluginNodeTypeName</em> is a simple variable called below to give a name to you node type.</li>
<li><em>kPluginNodeId</em> is a value serving as an id for the node when it is written in binary files (mb). 0x80000 to 0xfffff are used by Maya samples. See (documentation|http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html] for more informations.</li>
</ul>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Node definition</span>
<span style="color: #ff7700;font-weight:bold;">class</span> projectMeshNode<span style="color: black;">(</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># constructor</span>
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span></pre>
<p>Here, the class is an instance of the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html">MPxNode</a> object which is himself a class done to create custom nodes (it's a fairly complex part I will not go througth in this tutorial as it deserves a full post :jdicajdirien: ).</p>
<p>The <em>__init__</em> method is the first executed when the class is created. It simply initialise the <em>MPxNode</em> class.</p>
<p>The <em>compute</em> method is the one we will work the most. It's a herited methode from <em>MPxNode</em>. The part of the code "which do something". :sourit:</p>
<p>If you're not familiar with classes and Python, this part of the code may seem complex, but don't worry, the only important part is the <em>compute</em> method.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># creator</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span> projectMeshNode<span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span></pre>
<p>A function, executed when initializing the plugin, which returns a pointer to the created class (and therefore, the node).</p>
<p>Python having no notion of pointer and Maya having need, in particular, to initialize the plugins, Autodesk has developed the OpenMayaMPx.asMPxPtr method (more informations <a href="http://autodesk.com/us/maya/2011help/files/Maya_Python_API_Using_the_Maya_Python_API.htm" hreflang="en">here</a>).</p>
<p>Once again, it's something basic, you put it, you do not think :bete: .</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">return</span></pre>
<p>This method is also called during the node creation and allows (among others) to initialize the node attributes. This will be the first one that we will fulfill. For now, it make nothing at all.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span> :
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span> kPluginNodeTypeName, kPluginNodeId, nodeCreator, nodeInitializer <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span>
<span style="color: #808080; font-style: italic;"># uninitialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span>:
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span> kPluginNodeId <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to unregister node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span></pre>
<p>Let me tell you, this is really a "copy-paste" I've done from examples. Basically, these functions are called when loading/unloading plugins. They are used to register/unregister the plugins from Maya sessions.</p>
<p>Their behavior is simple, I invite you to review this code snippet for yourself (it doesn't harm! :gniarkgniark: ).</p>
<p>At this point you should be able to create your python node and load it in Maya</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_004.png" alt="projection_mesh_api_004.png" style="display:block; margin:0 auto;" title="projection_mesh_api_004.png, fév. 2011" height="324" width="387" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_005.png" alt="projection_mesh_api_005.png" style="display:block; margin:0 auto;" title="projection_mesh_api_005.png, fév. 2011" height="465" width="476" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_006.png" alt="projection_mesh_api_006.png" style="display:block; margin:0 auto;" title="projection_mesh_api_006.png, fév. 2011" height="84" width="195" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_007.png" alt="projection_mesh_api_007.png" style="display:block; margin:0 auto;" title="projection_mesh_api_007.png, fév. 2011" height="75" width="433" /></p>
<p>And by creating it as follow:</p>
<pre class="mel mel">createNode projectMesh<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">// Result: projectMesh1 //</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_008.png" alt="projection_mesh_api_008.png" style="display:block; margin:0 auto;" title="projection_mesh_api_008.png, fév. 2011" height="171" width="474" /></p>
<p>To the extent that there is no attribute, this node does absolutely nothing! :sourit:</p>
<center>:longBar:</center>
<h5>Attributes preparation <a name="attributesPreparation"></a></h5>
<p>As promised, we will start by the <em>nodeInitializer()</em> method which initialize the node attributes.</p>
<p>We will need three attributes:</p>
<ul>
<li>Two inputs: The mesh projecting his vertex and the mesh which recieve them.</li>
<li>An output: The output, projected mesh.</li>
</ul>
<p>Here we go! :grenadelauncher:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
typedAttr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnTypedAttribute</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the input attributes</span>
projectMeshNode.<span style="color: black;">inputMeshSrc</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshSrc"</span>, <span style="color: #483d8b;">"inMeshSrc"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">inputMeshTarget</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshTarget"</span>, <span style="color: #483d8b;">"inMeshTrg"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the output attributes</span>
projectMeshNode.<span style="color: black;">outputMesh</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"outputMesh"</span>, <span style="color: #483d8b;">"outMesh"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setWritable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
typedAttr.<span style="color: black;">setStorable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Add the attributes to the node</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Set the attribute dependencies</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>The first line:</p>
<pre class="python python">typedAttr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnTypedAttribute</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Creates an "object" (known in the Maya API as "<a href="http://download.autodesk.com/us/maya/2009help/API/group___m_fn.html">Function Set</a>") that will help us to manipulate the attributes (especially build them):</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Setup the input attributes</span>
projectMeshNode.<span style="color: black;">inputMeshSrc</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshSrc"</span>, <span style="color: #483d8b;">"inMeshSrc"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">inputMeshTarget</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshTarget"</span>, <span style="color: #483d8b;">"inMeshTrg"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the output attributes</span>
projectMeshNode.<span style="color: black;">outputMesh</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"outputMesh"</span>, <span style="color: #483d8b;">"outMesh"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setWritable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
typedAttr.<span style="color: black;">setStorable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span></pre>
<p>We create attributes. Nothing complicated (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_typed_attribute.html#fc105a88372e84910bc56c74c973bc26">see documentation</a>).</p>
<p>Some details on used arguments:</p>
<ul>
<li>The full attribute name (long name).</li>
<li>The short attribute name (short name).</li>
<li>The attribute "type" (as <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html">API type</a>).</li>
</ul>
<p>The following methods each attribute declaration (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#ced78c59e2a6b0395810705bb2896bfb">setReadable</a>, <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#5b89d32b1d6beebfa7b21a58d1d8d6f2">setWritable</a>, <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#b0fe1b4159b4f4bcefd7c0cef9a7b389">setStorable</a>) add special things to the latest created attribute (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html">see documentation</a> for more precisions).</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Add the attributes to the node</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>As the comment said, this part add/connect attributes created above to the node.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Set the attribute dependencies</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>This part is very important! :papi:</p>
<p>It allows you to define "dependencies" between attributes.</p>
<p>In our case:</p>
<ul>
<li>If the <em>inputMeshSrc</em> attribute change, <em>outputMesh</em> attribute also change.</li>
<li>If the <em>inputMeshTarget</em> attribute change, <em>outputMesh</em> attribute also change.</li>
</ul>
<p>If these lines are not set, the <em>compute</em> method of the node we are creating will never be launched. The node will never be updated.</p>
<p>You can download the python node in the current state here:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projectMesh001.7z">>> projectMesh001.7z <<</a></p>
<center>:longBar:</center>
<h5>Prepare the scene <a name="prepareTheScene"></a></h5>
<p>Before we actually code the behavior of the node, we need to connect some geometry to our future node.</p>
<p>Create a scene that looks like this:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_001.png" title="projection_mesh_api_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_001_m.jpg" alt="projection_mesh_api_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_001.png, fév. 2011" height="376" width="560" /></a></p>
<p>Also create a third mesh, which will return the geometry of our future node (which will be the projected mesh).</p>
<p>In my case: A pSphere. But it can be anything. As it is a mesh node.</p>
<p>You can even create the mesh node by hand.</p>
<p>Place it to the center of the scene (0,0,0) so there have no offset between the projected mesh and his target.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_009.png" alt="projection_mesh_api_009.png" style="display:block; margin:0 auto;" title="projection_mesh_api_009.png, fév. 2011" height="549" width="733" /></p>
<p>To avoid having to create the connections each time, here are two small MEL codes that help you to test your node (your nodes must be well named).</p>
<p>To load all:</p>
<pre class="mel mel">loadPlugin<span style="color: #009900;">(</span> <span style="color: #ff0000;">"monRepertoire/projectMesh.py"</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
createNode projectMesh<span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f projectMesh1.<span style="color: #202020;">outputMesh</span> pSphereShape2.<span style="color: #202020;">inMesh</span><span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f pPlaneShape1.<span style="color: #202020;">worldMesh</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> projectMesh1.<span style="color: #202020;">inputMeshSrc</span><span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f pSphereShape1.<span style="color: #202020;">worldMesh</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> projectMesh1.<span style="color: #202020;">inputMeshTarget</span><span style="color: #339933;">;</span></pre>
<p>And unload all:</p>
<pre class="mel mel">delete projectMesh1<span style="color: #339933;">;</span>
flushUndo<span style="color: #339933;">;</span>
unloadPlugin<span style="color: #009900;">(</span> <span style="color: #ff0000;">"projectMesh"</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>And that's it! Now take a deep breath, we jump!</p>
<center>:longBar:</center>
<h5>The compute method <a name="theComputeMethod"></a></h5>
<p>The first thing to test is the presence of a connection on your outputMesh attribute. Actually, if your node is connected to anything, it should not be calculated:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">if</span> plug == <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span>:
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kUnknownParameter</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span></pre>
<p>Once we are sure connections are made, we get input attributes:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> plug == <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span>:
<span style="color: #808080; font-style: italic;"># get the inputMeshTarget (return MDataHandle)</span>
inMeshSrcHandle = data.<span style="color: black;">inputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
inMeshTargetHandle = data.<span style="color: black;">inputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span></pre>
<p>This is a "connection" to the block of data attributes. This is the first step to get the value (here it's a kMesh so it will be a little different) of an attribute.</p>
<blockquote><p>Python being an untyped language (both its main strength but also its main flaw ...), I tend to write in comments the Maya API type of data that I get.</p>
<p>
Otherwise, it's quick to not knowing at all what type is what variable (types in the Maya API is no lack :aupoil: ).</p>
<p>
However, everyone has their own method! If you have a over-developed cortex, if you want to play the "I dont care types are for noobs", if a code that you are the only person who can read it give you the feeling to be a strong male and if you want to justify your BAC +5 (in this day and age it's certainly not your salary that should do it). Do not hesitate, code as <del>TD</del> pork: No comments, one letter variables and whatnot like that... Your colleagues will return the favor. :sourit:</p>
<p>
But if you're more modest and would quickly learn the Maya API, I urge you to write Maya API types directly in comments of your code. Besides being clearer, it still requires to known/search, when writing variables, what type it is.</p></blockquote>
<p>After that, we check our two connected attributes are meshs:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#we check the API type we've got here (we need kMesh)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> inMeshSrcHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: #ff7700;font-weight:bold;">and</span> inMeshTargetHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"cool"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kInvalidParameter</span></pre>
<p>And we get them as such:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#we check the API type we've got here (we need kMesh)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> inMeshSrcHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: #ff7700;font-weight:bold;">and</span> inMeshTargetHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> :
<span style="color: #808080; font-style: italic;"># return a MObject</span>
meshSrc = inMeshSrcHandle.<span style="color: black;">asMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
meshTarget = inMeshTargetHandle.<span style="color: black;">asMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"cool"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kInvalidParameter</span></pre>
<p>I invite you to look at the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> documentation just to see what we can get from an attribute.</p>
<p>As mentioned in the comment, we get a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_object.html">MObject</a>. This object type is the "all-purpose-type" of Maya.</p>
<p>This MObject is just a transitionnal object. Actually, it is rarely used directly.</p>
<p>In Maya, to modify/malipulate objects, we often use <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_base.html">"Function Set"</a>. They follow the pattern: MFn*Type*.</p>
<p>Here, to manipulate meshs, we will get a function set of mesh: <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html">MFnMesh</a>.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># get the MFnMesh of the twice attr</span>
mFnMeshSrc = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> meshSrc <span style="color: black;">)</span>
mFnMeshTarget = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> meshTarget <span style="color: black;">)</span></pre>
<p>What interests us now is to have a list of all vertices of the "source mesh" (one which will be projected to the "target mesh") to create another array that will contain changed vertex positions:</p>
<pre class="python python">outMeshMPointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># create an array of vertex wich will contain the outputMesh vertex</span>
mFnMeshSrc.<span style="color: black;">getPoints</span><span style="color: black;">(</span>outMeshMPointArray<span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># get the point in the space</span></pre>
<p>The first line creates an array of type <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point_array.html">MPointArray</a>.</p>
<p>The second line fills it with the values of the "source mesh" vertex.</p>
<p>How to write is a bit confusing ("upside down" will say some :reflechi: ) but that's the way <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#54dc2a4415a365c7193936dad6643b47">getPoint</a> works, as many other functions of the Maya API.</p>
<p>Rather than return the result, it is stored in the variable given as argument.</p>
<p>We now have a MPointArray filled with his vertex positions.</p>
<p>The idea now is to change vertex positions to make them matching the projected position on the "target mesh".</p>
<p>But here... The vertex positions that you get in your MPointArray are in "object space". That mean, relative to the center of the object.</p>
<p>Nous allons nous heurter à un vrai problème. The big one! The ultimate: The matrices! *Voix qui résonne* :enerve:
We will face a real problem. The big one! The ultimate: The Matrix! *Voice echoing* :enerve:</p>
<center>:longBar:</center>
<h5>Matrices explained to CG artists <a name="matricesExplainedToCGArtists"></a></h5>
<p>When you are CG artists, we hear about it without really knowing what it is :bete: .</p>
<p>Add to that what we find on the net is very academic and "too mathematical" (It shows how to multiply a matrix without explaining why it is necessary to do so).</p>
<p>All this so that we don't necessarily see the connection with our work.</p>
<p>I'll modestly try to explain this from a "CG artist" point of view :mayaProf: .</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/photoMatrix.jpg" title="photoMatrix.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.photoMatrix_m.jpg" alt="photoMatrix.jpg" style="display:block; margin:0 auto;" title="photoMatrix.jpg, fév. 2011" height="416" width="560" /></a></p>
<center><small><i><a href="http://www.flickr.com/photos/trinity-of-one/20562069/">Photography</a> by <a href="http://www.flickr.com/photos/trinity-of-one/">My Melting Brain</a> under Creative Common <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/deed.fr">by-nc-sa</a>. Thanks to him! :sourit:</i></small></center>
<p>Create a cube.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_010.png" alt="projection_mesh_api_010.png" style="display:block; margin:0 auto;" title="projection_mesh_api_010.png, fév. 2011" height="284" width="463" /></p>
<p>You may have noticed, once your cube is created, that it has a "pivot point" with informations (position, rotation, scale, etc. ...). Well this point can be a "mathematical link" between the vertex points of your cube and "the world" (center coordinates of the "world" are 0,0,0).</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_011.png" alt="projection_mesh_api_011.png" style="display:block; margin:0 auto;" title="projection_mesh_api_011.png, fév. 2011" height="291" width="404" /></p>
<p>If this "transform" node didn't exist, your object would be at center of the scene. And to move it, it should move the all vertex positions of the cube.</p>
<p>The transform node acts like a "parent" of the vertex of your cube. In this way, the vertex positions of the cube doesn't move. For example, a vertex of a cube placed at 1,1,1 (relative to the pivot of the cube so) will remain at 1,1,1 regardless of the position of the transform.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_012.png" alt="projection_mesh_api_012.png" style="display:block; margin:0 auto;" title="projection_mesh_api_012.png, fév. 2011" height="246" width="305" /></p>
<center><i>Here, we move the pivot, not the cube vertices's that they don't change position relative to the pivot, the pivot that changes position relative to the world.</i></center>
<p>But some operations (in our case, get if the vertex is face to a different mesh) requires that the coordinates of all the entities are a common reference, the reference world.</p>
<p>Comic demonstration:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_001.png" alt="projection_mesh_api_bd_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_001.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Hi! I am vertex[0] and I'm on (1,1,1).</br>
- Hi! I am vertex[0] and I'm on (1,1,1).</br>
Two vertices at different coordinates in "world space"...</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_002.png" alt="projection_mesh_api_bd_002.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_002.png, fév. 2011" height="340" width="553" /></p>
<center><i>...but at the same place relative to their respective centers.</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_003.png" alt="projection_mesh_api_bd_003.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_003.png, fév. 2011" height="340" width="553" /></p>
<center><i>- (insult)</br>
- (insult)</br>
Not easy to make calculations.</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_004.png" alt="projection_mesh_api_bd_004.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_004.png, fév. 2011" height="340" width="553" /></p>
<center><i>You see the prob?</i></center>
<p>Whereas if we chose, as common reference point, the center of the world.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_005.png" alt="projection_mesh_api_bd_005.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_005.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Hi! I am vertex[0] and I'm on (4,2,3) relative to world.</br>
- Hi! I am vertex[0] and I'm on (3,1,2) relative to world.</i></center>
<p>It's much easier.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_006.png" alt="projection_mesh_api_bd_006.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_006.png, fév. 2011" height="340" width="553" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_007.png" alt="projection_mesh_api_bd_007.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_007.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Friend!</br>
- Friend!</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_008.png" alt="projection_mesh_api_bd_008.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_008.png, fév. 2011" height="340" width="553" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_009.png" alt="projection_mesh_api_bd_009.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_009.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Nevertheless, I am still higher than you!</br>
- Whait... What? o_O</i></center>
<p>Okay, now we know the vertex coordinates in their "object space", we must know how get them in "world space".</p>
<p>The basic principle that immediately comes to mind is: We add vertex positions (in object space) to its pivot point (him, in world space).</p>
<blockquote><p>Example:</p>
<p>
If pVertex the position of a vertex and cubePosition the position, in world space, of the pivot point of the cube:</p>
<p>
cubePositionX + pVertexInCubeX = pVertexInWorldX</p></blockquote>
<p>But surely you can imagine, it's more complicated... :siffle:</p>
<p>Indeed, in the case of rotations and scale, it is not enough of a few additions to solve the problem.</p>
<blockquote><p>Note: Whether a translation, a rotation, or scaling a mesh, all comes down to vertex moves in the world.</p></blockquote>
<p>The truth is that all this "parameters" can be put into a single "object" that we call a matrix. This matrix will help us to make calculations (as Maya saves us but if you're interested, here is an example of <a href="http://jeux.developpez.com/faq/math/?page=bases#Q4">calculates a rotation matrix</a>) to get vertex positions in world space.</p>
<p>As I said, Maya gives us easely access to this object through <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> (There are several matrix types, we will focus on this one).</p>
<p>So at this stage, we have two things:</p>
<ul>
<li>Vertex positions relative to the object (in "object space").</li>
<li>A matrix of the object (which is world relative, "world space").</li>
</ul>
<p>We need to "convert" vertex positions from "space object" to "world space". A "change of reference" (sorry guys, don't know the english word for that :pasClasse: ). So you get an absolute position ("world space").</p>
<p>And to get vertex position in the "world space", "simply" multiply vertex position matrix {x, y, z} by inclusion matrix of the object. (I wrote "simply" in quotes because multiplying a matrix is not as simple as 2x2... :redface: )</p>
<blockquote><p>Note: I would not do demonstration on "how to calculate a matrix", internet is full of examples and explanations.</p></blockquote>
<p>And here's the formula:</p>
<pre>
positionGlobale = positionLocale * inclusiveMatrix
</pre>
<p>It's almost like the Pythagorean theorem. Who cares how it works, until you know when to use it! :baffed:</p>
<p>Voila! I hope this little explanation you will shed some light on why the matrices are. :sourit:</p>
<center>:longBar:</center>
<h5>Back to the code! <a name="backToTheCode"></a></h5>
<p>Get inclusive matrix is simple.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># get MDagPath of the MMesh to get the matrix and multiply vertex to it. If I don't do that, all combined mesh will go to the origin</span>
inMeshSrcMDagPath = mFnMeshSrc.<span style="color: black;">dagPath</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># return MDagPath object</span>
inMeshSrcInclusiveMMatrix = inMeshSrcMDagPath.<span style="color: black;">inclusiveMatrix</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># return MMatrix</span></pre>
<p>The first line retrieves the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html">dagPath</a> of the source mesh. We can consider the dagPath as the equivalent of the transform node of an object in Maya. Where all the informations about transformations (positions, rotations, scales, etc ...) are stored.</p>
<p>The second line retrieves the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> of the source mesh as a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_matrix.html">MMatrix</a> type.</p>
<p>Now we can go through each vertex:</p>
<ul>
<li><a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point.html#1fdf65c85cf27f6ce4ddef0a72aac5d3">Multiply it by the matrix</a> to get his position in world space.</li>
<li>Get his normal.</li>
<li>Also <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html#d22edbae3de3e0742534acff6b4f6227">multiply it</a> bye the matrix.</li>
<li>Get the collision point, store it as the current point.</li>
</ul>
<center>:longBar:</center>
<h5>Go through each vertex and modify it <a name="goThroughEachVertexAndModifyIt"></a></h5>
<p>The beginning of the main loop looks like this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">(</span> outMeshMPointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span> :
inMeshMPointTmp = outMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span> <span style="color: #66cc66;">*</span> inMeshSrcInclusiveMMatrix <span style="color: #808080; font-style: italic;"># the MPoint of the meshSrc in the worldspace</span></pre>
<p>The loop is quite simple: "i" is incremented by 1 each "lap" to browse the vertex array (MPointArray).</p>
<p>The first thing we do is point multiplication (<em>outMeshMPointArray[i]</em>) by the matrix (<em>inMeshSrcInclusiveMMatrix</em>) to obtain a vertex (<em>inMesgPointTmp</em>) in world space ( with coordinates relatives to the world, 0,0,0).</p>
<p>Now we've (finally) the vertex positioned relative to the world, we will "intersect" it with the target mesh.</p>
<blockquote><p>As chrysl666 told <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#c3024">in his comment</a>, Maya give a simple way to get informations without have to extract matrix and multiply it yourself. Just call your points (and others stuffs) with:</p>
<p>
getPoints(OpenMaya.MSpace.kWorld).</p>
<p>
This will return points position directly in world space. Thanks to him for this information. :bravo:</p></blockquote>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/intersection.jpg" title="intersection.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.intersection_m.jpg" alt="intersection.jpg" style="display:block; margin:0 auto;" title="intersection.jpg, fév. 2011" height="420" width="560" /></a></p>
<center><small><i><a href="http://www.flickr.com/photos/mynamemattersnot/2470352700/">Photography</a> by <a href="http://www.flickr.com/photos/mynamemattersnot/">MyNameMattersNot</a> under Creative Common <a href="http://creativecommons.org/licenses/by-sa/2.0/deed.fr">by-sa</a>. Thanks to him!</i></small></center>
<p>Well, go look at arguments of the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">OpenMaya.MFnMesh.closestIntersection()</a> method we <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theorie">seen above</a>.
Bon, allez regarder les arguments de la méthode <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">OpenMaya.MFnMesh.closestIntersection()</a> que <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theory">nous avont vu plus haut</a>.</p>
<p>You see there are quite a few. :sourit:</p>
<p>Don't worry, we can ignore many of them. What interests us is the original vertex, his direction (in our case: normal) and the collision point (the hitPoints).</p>
<p>But more subtle, look at the type of the first argument (raySource) expected by the method.</p>
<p>It's a MFloatPoint!</p>
<p>But how convert <em>inMeshMPointTmp</em> (a MPoint) to a MFloatPoint? It's pretty easy in C++, you have to use doubles. After much research, I found a solution. I give it to you:</p>
<pre class="python python">raySource = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> inMeshMPointTmp.<span style="color: black;">x</span>, inMeshMPointTmp.<span style="color: black;">y</span>, inMeshMPointTmp.<span style="color: black;">z</span> <span style="color: black;">)</span></pre>
<p>It's not as complicated but if you don't know...</p>
<p>So we have our raySource.</p>
<p>Now the normal:</p>
<pre class="python python">rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MVector</span><span style="color: black;">(</span><span style="color: black;">)</span>
mFnMeshSrc.<span style="color: black;">getVertexNormal</span><span style="color: black;">(</span> i, <span style="color: #008000;">False</span>, rayDirection<span style="color: black;">)</span>
rayDirection <span style="color: #66cc66;">*</span>= inMeshSrcInclusiveMMatrix
rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatVector</span><span style="color: black;">(</span>rayDirection.<span style="color: black;">x</span>, rayDirection.<span style="color: black;">y</span>, rayDirection.<span style="color: black;">z</span><span style="color: black;">)</span></pre>
<p>At this point, you should understand:</p>
<ul>
<li>We create the MVector.</li>
<li>We store the current vertex (i) normal of the source mesh in it.</li>
<li>We multiply by the matrix ot get this vector in space world.</li>
<li>We "convert" it to MFloatVector.</li>
</ul>
<p>The hitPoint is a simple MFloatPoint:</p>
<pre class="python python">hitPoint = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>And the remaining arguments are:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># rest of the args</span>
hitFacePtr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MScriptUtil</span><span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">asIntPtr</span><span style="color: black;">(</span><span style="color: black;">)</span>
idsSorted = <span style="color: #008000;">False</span>
testBothDirections = <span style="color: #008000;">False</span>
faceIds = <span style="color: #008000;">None</span>
triIds = <span style="color: #008000;">None</span>
accelParams = <span style="color: #008000;">None</span>
hitRayParam = <span style="color: #008000;">None</span>
hitTriangle = <span style="color: #008000;">None</span>
hitBary1 = <span style="color: #008000;">None</span>
hitBary2 = <span style="color: #008000;">None</span>
maxParamPtr = <span style="color: #ff4500;">99999999</span>
<span style="color: #808080; font-style: italic;"># http://zoomy.net/2009/07/31/fastidious-python-shrub/</span>
hit = mFnMeshTarget.<span style="color: black;">closestIntersection</span><span style="color: black;">(</span>raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span>,
maxParamPtr,
testBothDirections,
accelParams,
hitPoint,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2<span style="color: black;">)</span></pre>
<p>Great thanks to <a href="http://zoomy.net/about/">Peter J. Richardson</a>! Sans <a href="http://zoomy.net/2009/07/31/fastidious-python-shrub/">son billet</a>, I would never have succeeded to code this stuff. This is why we need to "share what you know" on the internet! ;)</p>
<p>Once called <em>closestIntersection</em>, you get a hitPoint in MFloatPoint that you convert into MPoint:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> hit :
inMeshMPointTmp = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPoint</span><span style="color: black;">(</span> hitPoint.<span style="color: black;">x</span>, hitPoint.<span style="color: black;">y</span>, hitPoint.<span style="color: black;">z</span><span style="color: black;">)</span></pre>
<p>We replaces the current point by our new one:</p>
<pre class="python python">outMeshMPointArray.<span style="color: #008000;">set</span><span style="color: black;">(</span> inMeshMPointTmp, i <span style="color: black;">)</span></pre>
<p>And this is the end of the loop! :D</p>
<p>At this point you've got a MPointArray <em>outMeshMPointArray</em> with values of projected vertices on the target mesh.</p>
<p>So now we need to rebuild the mesh.</p>
<center>:longBar:</center>
<h5>Build a mesh <a name="buildAMesh"></a></h5>
<p>I will not go througt details on "how to create and arrange the variables in the case of the creation of a mesh".</p>
<p>Basically we need:</p>
<ul>
<li>The number of vertices.</li>
<li>Le number of ploygons (polygons + triangles if there is).</li>
<li>A array of points (All vertices one behind others with they XYZ coordinates).</li>
<li>An array listing the number of vertices by polygons (Example: 4,4,4,4,3,4,3,4,3,4,4,etc...).</li>
<li>An array of vertex indices (Example: 1,2,3,4,3,4,5,6,5,6,7,8, etc...).</li>
</ul>
<p>You will have understood, we already have the points array (the third point). Otherwise, we silly gets values from the original mesh.</p>
<p>Let's start! :hehe:</p>
<p>At first, we must create a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh_data.html">MFnMeshData</a> function set.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># create a mesh that we will feed!</span>
newDataCreator = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMeshData</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>This function set will allow us to create a MObject that we can "fill" with data of the future mesh.</p>
<pre class="python python">newOutputData = newDataCreator.<span style="color: black;">create</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Return MObject</span></pre>
<p>As I said above: We need to get all informations (except the vertex array) of the source mesh:</p>
<pre class="python python">outMeshNumVtx = mFnMeshSrc.<span style="color: black;">numVertices</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># outputMesh will have the same number of vtx and polygons</span>
outMeshNumPolygons = mFnMeshSrc.<span style="color: black;">numPolygons</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># create two array and feed them</span>
outMeshPolygonCountArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MIntArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
outMeshVtxArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MIntArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
mFnMeshSrc.<span style="color: black;">getVertices</span><span style="color: black;">(</span>outMeshPolygonCountArray, outMeshVtxArray<span style="color: black;">)</span></pre>
<p><a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#5f014e7c511cf86978a765a932ce04d0">mFnMeshSrc.getVertices()</a> filled two arrays with informations needed to create the mesh. See <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#construire_un_mesh">above</a>.</p>
<p>Once we have that, we create the mesh:</p>
<pre class="python python">meshFS = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
meshFS.<span style="color: black;">create</span><span style="color: black;">(</span>outMeshNumVtx, outMeshNumPolygons, outMeshMPointArray, outMeshPolygonCountArray, outMeshVtxArray, newOutputData<span style="color: black;">)</span></pre>
<p>The principle is quite simple:</p>
<ul>
<li>We create a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html">MFnMesh</a> function set.</li>
<li>We create the mesh giving all arguments re le mesh en donnant tout les arguments (collected above) via <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#6994886ddf6ea46baf399ff40d30bc32">meshFS.create()</a>.</li>
</ul>
<p>Once the mesh is created, we get the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> from the <em>outputMesh</em> connection to put the MObject we just fill <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html#959379b4d93358519ccc99696031756c">in it</a>: The mesh!</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Store them on the output plugs</span>
outputMeshHandle = data.<span style="color: black;">outputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
outputMeshHandle.<span style="color: black;">setMObject</span><span style="color: black;">(</span> newOutputData <span style="color: black;">)</span></pre>
<p>Once this is done, we say, via <a href="http://download.autodesk.com/us/maya/2010help/api/class_m_data_block.html#0d8faafb64e70cf0a579532bb033c98f">MDataBlock.setClean()</a>, to the dependency graph that the connection has been updated.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># tell to the dependency graph the connection is clean</span>
data.<span style="color: black;">setClean</span><span style="color: black;">(</span> plug <span style="color: black;">)</span></pre>
<p>It's over! :youplaBoum:</p>
<p>If you well followed the tutorial (and if I did not make errors ( :baffed: ), you should have a node that works correctly (place the plan so that it "target" the sphere):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_013.png" alt="projection_mesh_api_013.png" style="display:block; margin:0 auto;" title="projection_mesh_api_013.png, fév. 2011" height="390" width="635" /></p>
<p>Of course, if vertices aren't projected on the sphere, it return to their original positions:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_014.png" alt="projection_mesh_api_014.png" style="display:block; margin:0 auto;" title="projection_mesh_api_014.png, fév. 2011" height="406" width="487" /></p>
<center>:longBar:</center>
<h5>Source code and conclusion <a name="sourceCodeAndConclusion"></a></h5>
<p>At this point, you should have understood the principle of matrices (If this is not the case, don't hesitate to go deeply, you will see 3D in a different way! :sourit: ), be able to recover components of a mesh and create a mesh from scratch.</p>
<p>There is the source code: <a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projectMesh.7z">projectMesh.7z</a></p>
<p>And voila! I just finished another big tutorial. I hope it has been informative and that you can begin to do interesting things with Maya API. :banaeyouhou:</p>
<p>Such informations is lacking on the Internet on CG web site.</p>
<p>I encourage all seniors who would pass by and who would learn something interesting reading this post to do not hesitate to "give back": If you are competent in a field, share! I did. :hihi:</p>
<p>If I'm wrong somewhere, if a point does not seem clear or if there is an error, please tell me. :pasClasse:</p>
<p>See you soon!</p>
<center>:marioCours:</center>
<blockquote><p>Note: As you could read, my english is far from being perfect. Don't hesitate to leave a comment if you found a better way to explain some stuff.</p></blockquote>
<center>:longBar:</center>
<h5>Update: Do this without Python <a name="doThisWithoutPython"></a></h5>
<p>Well, it's not really the goal but since Kel Solar talk about <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#c205">in comments</a> I also give you a way to do this using the built in Maya. :seSentCon:</p>
<p>Select the target mesh:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_016.png" alt="projection_mesh_api_016.png" style="display:block; margin:0 auto;" title="projection_mesh_api_016.png, fév. 2011" height="453" width="603" /></p>
<p>Select the source mesh:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_017.png" alt="projection_mesh_api_017.png" style="display:block; margin:0 auto;" title="projection_mesh_api_017.png, fév. 2011" height="409" width="554" /></p>
<p>Open transfert attributes options:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_018.png" alt="projection_mesh_api_018.png" style="display:block; margin:0 auto;" title="projection_mesh_api_018.png, fév. 2011" height="232" width="259" /></p>
<p>Set options as follow:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_019.png" alt="projection_mesh_api_019.png" style="display:block; margin:0 auto;" title="projection_mesh_api_019.png, fév. 2011" height="359" width="551" /></p>
<p>Basically, we only transfer the vertex positions in world space by projecting them along their normal.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_020.png" alt="projection_mesh_api_020.png" style="display:block; margin:0 auto;" title="projection_mesh_api_020.png, fév. 2011" height="399" width="622" /></p>
<p>And that's it!</p>
<p>You can rotate the source mesh and it's updated! :laClasse:</p>
<p>Voila! That way, people who came to find a quick solution will not be frustrated! :sourit:</p>Creating Custom Locator with Maya's Python API (English Translation)urn:md5:c82400507ef5dded320bdb670f4eacfb2010-02-12T22:36:00+01:002013-07-26T22:32:05+02:00NarannScript et codeapicppenmayamelopenglpythonscripttranslation<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocator001.png" alt="pythonLocator001.png" style="float:left; margin: 0 1em 1em 0;" title="pythonLocator001.png, mar. 2009" height="150" width="150" />This post is a english translation of <a href="https://www.fevrierdorian.com/blog/post/2009/04/15/Cr%C3%A9er-un-Custom-Locator-avec-l-API-Python-de-Maya">this post</a>. I'd wrote it after post <a href="http://forums.cgsociety.org/showthread.php?t=845916" hreflang="en">this on CGTalk</a>. Great thanks to Daisy C. Lyle. She translate all the french version to english and make a great work! She save me hours doing this! :sourit: . Hope that this tutorial will be usefull.</p> <p>As <a href="https://www.fevrierdorian.com/blog/index.php?post/2009/03/31/Python%2C-Maya-et-sont-API...">I mentioned before</a>, I’m starting to use the Maya API Python binding. So I had a look at <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/mayaapi.html#2" hreflang="en">Rob Bateman’s</a> sources (which I found incomprehensible a year ago) and “translated” them (not without difficulty) into Python…So I’ve created a little script with a custom locator. It’s obviously not the simplest thing in the world to begin with (I did have some grounding thanks to <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/maya/mfn_traversing.htm" hreflang="en">other tutorials</a>, the Python “plug-ins” already incorporated into Maya and the OpenGL tutorials I’d done) but once the code is up and running, it’s quite fun to modify it to make your own locator…</p>
<p>So, on the menu we have:</p>
<ul>
<li>OpenGL (Well, how else are you going to draw your locator? :hehe: )</li>
<li>Color change as a function of the selection state of the locator.</li>
</ul>
<p>It’s not much but you’ll see it’s enough to be getting on with!</p>
<center>:longBar:</center>
<h5>Scripting is pointless, it’s for newbies!</h5>
<p>No! If you think that, then you haven’t understood anything. The two are closely related. If I chose the example of the locator, it’s not just because it’s “cool” but also because it’s an example of a thing it is only possible to do with the API…</p>
<p>But the API doesn’t do everything! :cayMal:</p>
<p>As you doubtless already know, the Maya interface is entirely written in MEL, which allows a scripter to quickly create a GUI for his or her scripts. Not all software packages offer this (did anyone say XSI?) and Maya is one of the few that boasts it.</p>
<p>Most Maya function calls are also scripted. This means you can “batch” your operations (when you have 50 scenes to open to change the value of an attribute, you won’t be laughing so hard! :hehe: )</p>
<p>I can only invite you to read <a href="http://www.creativecrash.com/forums/api/topics/new-to-the-api-then-read-this-33" hreflang="en">my mentor’s post</a> on the subject (displaying the C++ equivalent of a MEL code).</p>
<p>And finally: Python is a scripting language! Even if it can be “pre-compiled” by the interpreter, it’s still script.</p>
<p>That said, I think that this argument is a bit fruitless as even if you script using the Maya API Python binding, once the basics are understood you realize that the differences in syntax between a piece of C++ code and its equivalent in Python are minimal.</p>
<p>A little example of variable declaration:</p>
<center><I>C++</I></center>
<pre class="cpp cpp">MFnPlugin fnPlugin<span style="color: #008000;">(</span>obj<span style="color: #008000;">)</span><span style="color: #008080;">;</span></pre>
<center><I>Python</I></center>
<pre class="python python">fnPlugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span></pre>
<p>C++ is still the most user-friendly, I think (I’m not saying that because of this examples but in a more general way), which seems logical because that it's the historically used implementation, added to the fact that it remains a language fairly close to machine.</p>
<p>Then it all depends on whether or not you want to go fast. Compiled code (C++) will obviously go faster (about ten time) but I'm supposing that if you want to go fast, either you’re not good and you’re writing rotten code which leaves you no other solution than to resort to a lower-level language to speed it up, or you’re a “real” developer… If that’s the case, I don’t see what you’re doing on the blog of a Sunday coder! (Me! :aupoil: ).</p>
<p>What the Hell, one day I’m going to make a bench between MEL, pythonForMel, C++ and pythonForMayaApi :sourit: .</p>
<center>:longBar:</center>
<h5>Code basics</h5>
<p>Before we start messing around with the locator, we need the base code. Here we go!</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>Bang! Not acting so clever now are we? (I wasn’t acting clever the first time around either! :baffed: ).</p>
<p>Right, in fact it’s not very complicated. Explanation:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender</pre>
<p>Here we are importing the main modules. The first (“sys”) is the system module which we will use to write the plug-in initialization error messages (that’s all.)</p>
<p>The next three are Maya API Python modules. They have been divided into several modules in order to clarify their use:</p>
<ul>
<li>OpenMaya is the module for classes relating to node and command definitions and their “assembly” into plug-ins.</li>
<li>OpenMayaMPx is a Python-specific module. It contains the Maya’s proxy (or MPx class) bindings.</li>
<li>OpenMayaRender is the module for classes relating to render (hardware or software).</li>
</ul>
<pre class="python python">nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span></pre>
<p>This variable (I declare it at the beginning) will be used to give a name to the type of node you wish to create.</p>
<pre class="python python">nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span></pre>
<p>This variable (I also declare it at the beginning) will be used to give an ID (identifier) to our type of node.</p>
<pre class="python python">glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>This is where it gets complicated (especially for me.) This command is not referred to in the documentation (even though it appears in the example C++ codes.) This command makes it possible to retrieve a pointer to the class in charge of the render (hardware) of the viewports. It’s thanks to this that we are going to be able to “draw” our locator (using OpenGL commands.)</p>
<pre class="python python">glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The glFunctionTable() method returns a pointer to an object containing all the OpenGL instructions handled by Maya.</p>
<p>Now here we’re really getting stuck into OpenGL (well perhaps not quite, as you’re about to see :sourit: ):</p>
<center>:longBar:</center>
<h5>OpenGL, some informations</h5>
<p>What is OpenGL? (<a href="http://fr.wikipedia.org/wiki/OpenGL" hreflang="en">Wiki</a>) To summarize, it’s a way of communicating with the graphics card. It involves simple commands, e.g. to display points, lines, triangles, quads, textures, etc…</p>
<p>Before we go any further, we’re going to have to understand how this all works. If you want to “draw” locators, you have to know how to use the pen that is going to do it.</p>
<p>As a piece of code is worth more than a long lecture, here is how to “draw” a line in OpenGL (this is a C source. For Maya, we will see that there are two or three things you have to know first):</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>This technique is the “basic”, or “immediate mode” technique.</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span></pre>
<p>This command allows us to “go into primitive draw mode”(In a 3-D space.) We’re putting the pencil to the paper, if you like. The argument (GL_LINES) enables us to say how the following commands will be interpreted, inline here. Every two vertices, we write another line. One line per pair of vertices.</p>
<pre class="c c">glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span></pre>
<p>These two commands are the commands which “place” the vertices in space. The command is glVertex, the number is the number of arguments (here, three: X,Y and Z) and the last letter is the <a href="http://www.cplusplus.com/doc/tutorial/variables/" hreflang="en">data type</a> (here, “f” for float.)</p>
<p>Basically, we’re placing two vertices in space. And given that we have put ourselves into “line draw” mode (per vertex pair), we have just drawn a line.</p>
<pre class="c c">glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>And that’s just a way of “shutting down communications” with the graphics card and returning to the main program.</p>
<p>As you see, OpenGL isn’t as hard as all that! This was also how I understood that it’s the processor that calculates all the vertices’ positions in each image (well, that’s the old method; there are others, especially for static objects which are stored in the memory of the graphics card and called by a single instruction. Having said that, a large part of the work is done by the processor).</p>
<p>With this, you are almost ready to draw your own locator. I have just one point left to address.</p>
<center>:longBar:</center>
<h5>OpenGL, the Maya version! <a name="openGLVersionMaya"></a></h5>
<p>In fact, the code that I have shown you is a C program. I am now going to explain to you some of the few OpenGL peculiarities specific to Maya.</p>
<p>Maya, out of concern for interoperability (I suppose there are other reasons) has its own OpenGL implementation. So, when we wish to draw in the viewport, we don’t directly use the Windows or Mac implementation but in fact Maya’s own. (This can be done in C++, which makes it possible to dispense with the Maya wrapper, but you have to handle the interoperability in your code. It should also be possible to do this in Python but I haven’t tried). That doesn’t change much in the code but the constants (GL_LINES for instance) have another name.</p>
<p>As written in the Maya API documentation:</p>
<blockquote><p>The naming convention used in this file is to take the regular GL* definitions and add a "M" prefix</p></blockquote>
<p>Translation: “We put an ‘M’ in front of all the OpenGL constants”. :seSentCon:</p>
<p>So Maya won’t understand GL_LINES, only OpenMayaRender.MGL_LINES :baffed:</p>
<p>And here is how we write a line in OpenGL in Maya:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>So no big differences then ^^. Any experienced OpenGL user should be able to do it with their eyes closed!</p>
<center>:longBar:</center>
<h5>So shall we write this locator then?</h5>
<p>No! Not yet! There are still things to learn! After this little introduction to OpenGL, let’s go back to our Python script.</p>
<p>To be able to draw the locator, we have to derive the “draw” function (yes, because Maya allows you to derive functions…Not everyone can say as much, eh, XSI? :siffle: ). This function is used to…draw custom geometries using OpenGL functions.</p>
<p>Here is the basic code for the “draw” method:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The beginGL( ) and endGL( ) functions make it possible (well, this is my interpretation) to tell Maya that we are going to use OpenGL commands between them. According to the documentation, if they aren’t included in the right place, the program will exceed its allocated memory the program will therefore crash.</p>
<p>I’m deliberately going to forget a few functions for the moment, we’ll look at them later.</p>
<center>:longBar:</center>
<h5>Go Pawaaaah!</h5>
<p>So our code now looks like this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>You can download it here:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode001.7z">CustomLocatorNode001.7z</a></p>
<p>Load it (as a plug-in), and enter:</p>
<pre class="mel mel">createNode myCustomLocator<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocLineFirstLocator.png" alt="pythonLocLineFirstLocator.png" style="display:block; margin:0 auto;" title="pythonLocLineFirstLocator.png, avr. 2009" height="194" width="222" /></p>
<p>We’ve just made out first locator! :sourit: With this we can already do a few things… If you give it some elbow grease, you can get this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">random</span></pre>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode002.7z">CustomLocatorNode002.7z</a></p>
<p>You’ll have a wicked locator! ^^</p>
<p>Right, it’s all very well making lines but that’s not all there is to it… I suggest we do… a quad! :laClasse:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode003.7z">CustomLocatorNode003.7z</a></p>
<p style="font-style:italic; text-align:center;">Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuad.png" alt="pythonLocQuad.png" style="display:block; margin:0 auto;" title="pythonLocQuad.png, avr. 2009" height="276" width="405" /></p>
<p style="font-style:italic; text-align:center;">Paf le locator! <img class="smiley" alt=":baffed:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/baffed.gif"/></p>
<p>OK, it’s a bit flashy but we’re going to change that. :reflechi: We’re going to change the color and add transparency.</p>
<p>First, the color. As default, Maya uses the interface colors (blue when unselected, green when selected, pink when ‘templated’ etc….) To change the color, all you have to do is add the command glColor3f( )</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#on change la couleur</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode004.7z">CustomLocatorNode004.7z</a></p>
<p style="font-style:italic; text-align:center;">Re-Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColor.png" alt="pythonLocQuadColor.png" style="display:block; margin:0 auto;" title="pythonLocQuadColor.png, avr. 2009" height="342" width="507" /></p>
<p style="font-style:italic; text-align:center;">Re-Paf le locator! <img class="smiley" alt=":aupoil:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/aupoil.gif"/></p>
<p>Still just as flashy, huh? You’ll notice that only the line changes color as a function of the selection state. To ‘lighten up’ our locator a bit, we’re going to add transparency to our quad.</p>
<center>:longBar:</center>
<h5>The Alpha</h5>
<p>We are going to “enable” one of OpenGL's features: GL_BLEND (Don’t forget to disable it at the end):</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#We activate feature</span>
glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#Change color</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#Don't forget to unactivate in the end!</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>When this feature is enabled, OpenGL blends the given color with the resulting color (the background) according to a final factor (the Alpha.) If you run the code as it is, you won’t see any change. So we must add one last component to our color:</p>
<pre class="python python">glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#Change color and add alpha</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode005.7z">CustomLocatorNode005.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColorAlpha.png" alt="pythonLocQuadColorAlpha.png" style="display:block; margin:0 auto;" title="pythonLocQuadColorAlpha.png, avr. 2009" height="298" width="435" /></p>
<p style="font-style:italic; text-align:center;">Et voila! Better no? ;)</p>
<p>We are coming to the last part: the selection states.</p>
<center>:longBar:</center>
<h5>Selection</h5>
<p>In fact we can change aspects of our locator depending on its selection state. Here, we're going to change... The color! (very original... -_-).</p>
<p>First of all, there are several "states" of a "drawn" object (Listed in M3dView::DisplayStatus). I'm going to give you the two main ones, the ones we’re going to be using:</p>
<ul>
<li>kActive -> The active (selected) objects. (Be careful, this is tricky!)</li>
<li>kLead -> The last object selected.</li>
<li>kDormant -> The inactive objects.</li>
</ul>
<p>So, let’s explain the subtle difference::</p>
<p>In Maya, when you select an object, it becomes green. And when you add another object to the selection, this second object becomes green and the previous, white. In fact the green object is “in DisplayStatus” kLead, and the white object in kActive. Here’s a picture to explain (or remind you of?) the principle:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelectionState.png" alt="pythonLocSelectionState.png" style="display:block; margin:0 auto;" title="pythonLocSelectionState.png, avr. 2009" height="160" width="418" /></p>
<p>So we are going to be able to change the color as a function of the selection state.</p>
<p>As seen above, the derived function is draw, and the argument we use to know the state of the locator is "status". And nothing could be easier than using all this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaUI</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaUI <span style="color: #808080; font-style: italic;"># on top</span></pre>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kLead</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.3</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#rouge</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kActive</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#jaune</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kDormant</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#mauve</span></pre>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode006.7z">CustomLocatorNode006.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection001.png" alt="pythonLocSelection001.png" style="display:block; margin:0 auto;" title="pythonLocSelection001.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection002.png" alt="pythonLocSelection002.png" style="display:block; margin:0 auto;" title="pythonLocSelection002.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection003.png" alt="pythonLocSelection003.png" style="display:block; margin:0 auto;" title="pythonLocSelection003.png, avr. 2009" height="216" width="382" /></p>
<p>OK, it’s not that pretty but it works… :baffed:</p>
<p>I hope that this tutorial will shed some light on a few areas of the Python Maya API... Note however that I didn't choose the easiest thing to start with (if you know nothing about the API... Otherwise, it's quite easy I think.) But with this, you've already got a first toolkit that will enable you to take your first steps... :sourit:</p>
<p>Don’t hesitate to ask if you have questions on this tutorial (if the examples lack clarity or you have trouble making them work…)</p>
<p>See you soon!</p>
<p>Dorian</p>
<p>PS: As you could see, I'm not english so if I'd made redundant errors, don't hesitate to notice me! :sourit:</p>
<center>:longBar:</center>
<h5>Edit - Blog wich talk about this tutorial:</h5>
<ul>
<li><a href="http://marc.dubrois.free.fr/?p=179" hreflang="en">http://marc.dubrois.free.fr/?p=179</a></li>
</ul>