Dorian Fevrier's blog - Mot-clé - tutoJe 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>Récupérer rapidement la position des vertices d'un mesh Mayaurn:md5:912013d3913b678655c3562e58a746b32011-03-30T22:40:00+02:002013-07-26T18:11:05+02:00NarannScript et codeapicodefrmayapythonscripttutovertexxform<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_03_30_Recuperer_Position_Vertex_Rapidement/recuperer_Position_Vertex_Rapidement_tn.png" alt="recuperer_Position_Vertex_Rapidement_tn.png" style="float:left; margin: 0 1em 1em 0;" title="recuperer_Position_Vertex_Rapidement_tn.png, mar. 2011" height="150" width="150" />Si vous avez déjà été amené à récupérer la position dans l'espace de tous les vertices d'un objet, vous avez surement dû vous heurter à la commande <em>xform</em> et à sa lenteur légendaire. :baffed:</p>
<p>Dans ce billet, je vous propose un petit bout de script Python utilisant l'API Maya en Python qui permet de récupérer, plus rapidement, la liste de toutes les positions des vertices d'un objet.</p>
<p>Principalement didactique, ces codes, pourront intéresser les personnes qui souhaitent regarder un peu plus profondément comment utiliser l'API tout en ayant un cas concret d'application. :laClasse:</p> <h3>En utilisant xform</h3>
<p>Voici la version du script qui utilise la commande <a href="http://download.autodesk.com/us/maya/2011help/Commands/xform.html">xform</a>:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> getVtxPos<span style="color: black;">(</span> shapeNode <span style="color: black;">)</span> :
vtxWorldPosition = <span style="color: black;">[</span><span style="color: black;">]</span> <span style="color: #808080; font-style: italic;"># contiendra les positions dans l'espace de tout les vertex de l'objet</span>
vtxIndexList = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">getAttr</span><span style="color: black;">(</span> shapeNode+<span style="color: #483d8b;">".vrts"</span>, multiIndices=<span style="color: #008000;">True</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> vtxIndexList :
curPointPosition = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">xform</span><span style="color: black;">(</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>shapeNode<span style="color: black;">)</span>+<span style="color: #483d8b;">".pnts["</span>+<span style="color: #008000;">str</span><span style="color: black;">(</span>i<span style="color: black;">)</span>+<span style="color: #483d8b;">"]"</span>, query=<span style="color: #008000;">True</span>, translation=<span style="color: #008000;">True</span>, worldSpace=<span style="color: #008000;">True</span> <span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># [1.1269192869360154, 4.5408735275268555, 1.3387055339628269]</span>
vtxWorldPosition.<span style="color: black;">append</span><span style="color: black;">(</span> curPointPosition <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">return</span> vtxWorldPosition</pre>
<h3>La version avec l'API Maya</h3>
<p>Alternative à xform.</p>
<pre class="python python"><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;">def</span> particleFillSelection<span style="color: black;">(</span> <span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;"># get the active selection</span>
selection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MGlobal</span>.<span style="color: black;">getActiveSelectionList</span><span style="color: black;">(</span> selection <span style="color: black;">)</span>
iterSel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MItSelectionList</span><span style="color: black;">(</span>selection, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFn</span>.<span style="color: black;">kMesh</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># go througt selection</span>
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #ff7700;font-weight:bold;">not</span> iterSel.<span style="color: black;">isDone</span><span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;"># get dagPath</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
iterSel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># create empty point array</span>
inMeshMPointArray = <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 function set and get points in world space</span>
currentInMeshMFnMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span>dagPath<span style="color: black;">)</span>
currentInMeshMFnMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>inMeshMPointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># put each point to a list</span>
pointList = <span style="color: black;">[</span><span style="color: black;">]</span>
<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> inMeshMPointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span> :
pointList.<span style="color: black;">append</span><span style="color: black;">(</span> <span style="color: black;">[</span>inMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>, inMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span><span style="color: black;">[</span><span style="color: #ff4500;">1</span><span style="color: black;">]</span>, inMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span><span style="color: black;">[</span><span style="color: #ff4500;">2</span><span style="color: black;">]</span><span style="color: black;">]</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">return</span> pointList</pre>
<blockquote><p>Note j'ai pensé qu'en spécifiant la taille de la liste dès le départ on gagnerait encore du temps mais ça ne change rien. Il semblerait que <a href="http://stackoverflow.com/questions/311775/python-create-a-list-with-initial-capacity">Python gère ça bien</a>. Du coup, j'ai appris un nouveau dicton: <em>Premature optimization is the root of all evil</em>. :gniarkgniark:</p></blockquote>
<h3>Les temps</h3>
<p>Bon, c'est bien beau de dire que c'est plus rapide. Mais de combien? :reflexionIntense:</p>
<p>Voici les temps que j’obtiens pour une pSphere 50x50 smoothée 4 (soit 633602 vertices):</p>
<ul>
<li>Avec xform: 39 sec</li>
<li>Avec l'API Python de Maya: 4 sec</li>
</ul>
<p>10x plus rapide mine de rien. Ça me donnerait presque envie de le coder en C++, juste pour voir! :grenadelauncher:</p>
<h3>Conclusion</h3>
<p>Bon, et bien maintenant c'est à vous de voir quoi en tirer.</p>
<p>Vous pouvez essayer d'optimiser <a href="https://www.fevrierdorian.com/blog/post/2011/03/14/Remplir-un-mesh-de-spheres-dans-Maya-La-methode-d-un-senior">l’algorithme de Djelloul</a> et me faire part de vos résultats! :sourit:</p>
<p>Toujours est t'il que personnellement, je ne pourrais plus me passer de l'API si j'ai à récupérer la position des vertices d'un mesh c'est beaucoup plus rapide! Moyennant une complexité supérieur, je veux bien l'avouer, mais quand on a ce bout de code, on le garde et on le ressort au bon moment! :aupoil:</p>
<p>A bientôt!</p>
<center>:marioCours:</center>
Projeter un mesh sur un autre avec l'API Python de Mayaurn:md5:9653dcaa90d06317c1058f502c5c745c2011-02-20T23:56:00+01:002013-07-26T18:11:56+02:00NarannScript et code3dapiclosestIntersectioncodedéveloppementfrintersectionmayamelmeshnodepythonscripttuto<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" />Aujourd'hui je vous propose un tutorial qui vous permettra de projeter un mesh sur un autre.</p>
<p>C'est un truc qui peut être pratique mais surtout, c'est assez "fun" à faire (tout est relatif diront certains :seSentCon: ) et ça permet d'apprendre les fondamentales des changements de repères (les fameuses matrices) en ayant un exemple à la fois simple et concret.</p>
<p>Amateur de l'API, ce tuto est fait pour vous!</p> <center>:longBar:</center>
<p>Voici ce que nous souhaitons obtenir:</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>Un mesh projeté sur un autre.</p>
<p>Et la scène à partir de laquelle nous partons:</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/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#theorie">Théorie</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#codeDeBase">Code de base</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#preparationDesAttributs">Préparation des attributs</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#preparerSaScene">Préparer sa scene</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#laMethodeCompute">La méthode compute</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#lesMatricesExpliqueesAuxGraphistes">Les matrices expliquées aux graphistes</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#revenonsAuCode">Revenons au code!</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#parcourir_et_modifier_chaque_vertex">Parcourir et modifier chaque vertex</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#construire_un_mesh">Construire un mesh</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#conclusion_et_code_source">Conclusion et code source</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#la_methode_sans_python">Mise à jour: La méthode sans Python</a></li>
</ul>
<center>:longBar:</center>
<h5>Théorie <a name="theorie"></a></h5>
<p>Une fois de plus on commence par de la théorie.</p>
<p>Dans les faits, vous allez voir que c'est assez simple car le plus dur (la partie intersection) sera géré via <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">un appel à l'API Maya</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>Cette <a href="http://fr.wikipedia.org/wiki/Méthode_(informatique)">méthode</a> prend en charge l'intersection d'un rayon (point+direction) sur un mesh et renvoi quelques infos (dont la plus importante: La position du point projeté).</p>
<p>En gros, il nous faut trois choses:</p>
<ul>
<li>Un point d'origine (celui qu'on souhaite projeter).</li>
<li>Une direction (un vecteur).</li>
<li>Un mesh de destination.</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>Le point d'origine sera bien entendu chaque vertex du mesh à projeter (ici, les numéros des vertex étant en jaune).</p>
<p>La direction sera la normale du vertex à projeter (en vert sur l'image. D'accord on voit pas trop mais mettez y un peu de bonne volonté que diable! :cayMal: ).</p>
<p>Et le mesh de destination sera bien évidemment le mesh qui recevra le plan (dans notre cas, une sphere).</p>
<p>On récupère donc, à chaque fois, un point et une normale (les croix jaunes).</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>Pardonnez mon shéma à dix-francs-six-sous-convertion-jpeg-moisie :pasClasse:</i></center>
<center>:longBar:</center>
<h5>Code de base <a name="codeDeBase"></a></h5>
<p>Voici les bases du 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>Si vous avez déjà écrit un node Maya en Python, ce code ne doit pas vous faire trop peur.</p>
<p>J'explique vite fait pour les <del>boulets</del> autres :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 des principaux modules.</p>
<ul>
<li>Le module <em>sys</em> sert à créer les messages d'erreur lors du chargement/déchargement du plugin (voir plus loin).</li>
<li>Les deux autres modules servent à appeler les méthodes de l'API Maya.</li>
</ul>
<p>Rien de bien compliqué.</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> est une simple variable appelé plus loin pour donner un nom à notre type de node.</li>
<li><em>kPluginNodeId</em> est une valeur qui sert d'identifiant pour le node quand il est écrit dans dans les fichiers binaires (mb). 0x80000 à 0xfffff sont utilisé pour les examples Maya. Voir <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html">la documentation</a> pour plus d'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>Ici, la classe est une instance de l'objet <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html">MPxNode</a> qui est lui même une classe faite pour créer des nodes personnalisé (c'est une partie assez complexe que je n'aborderai pas dans ce tuto tant elle mérite un billet à part entière :jdicajdirien: ).</p>
<p>La méthode <em>__init__</em> est la première méthode lancée lors de la création de la classe. Elle initialise simplement la classe <em>MPxNode</em>.</p>
<p>La méthode <em>compute</em> est la méthode dans laquelle nous allons le plus travailler. C'est une méthode hérité de <em>MPxNode</em>. La partie du code "qui fait quelque chose". :sourit:</p>
<p>Si on ne connaît pas trop Python et les classes, cette partie du code peut sembler complexe mais ne vous inquiétez pas, c'est toujours la même qu'on utilise. La seule partie importante, c'est la méthode <em>compute</em>.</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>Une fonction lancée au moment de l'initialisation du plugin qui renvoi un pointeur (si si) vers la classe (et donc le node) crée.</p>
<p>Python n'ayant pas de notions de pointeur et Maya en ayant besoin, notamment, pour initialiser ces plugins, Autodesk a créé la méthode OpenMayaMPx.asMPxPtr (rechercher "asMPxPtr" dans l'aide Maya et prendre le premier résultat pour une explication plus précise).</p>
<p>Une fois de plus, c'est quelque chose de basique, on le met, on réfléchie pas :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>Cette méthode est elle aussi appelé lors de la création d'un node et permet (entre autres) d'initialiser les attributs du node. Ce sera la première que nous remplirons. Pour l'instant, elle en fait rien.</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>La je vais vous dire, c'est vraiment un "copier-coller" que je fais depuis les exemples fournit. En gros ces fonctions sont appelé lors du chargement/déchargement des plugins. Elles servent à "enregistrer"/"radier" les plugins des sessions Maya.</p>
<p>Leur comportement est simple, je vous invite à analyser ce bout de code par vous même (ça fait pas de mal! :gniarkgniark: ).</p>
<p>A ce stade, vous devriez pouvoir créer votre node python en le chargeant dans 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>Et en le créant comme suis:</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>Dans la mesure ou il n'y a aucun attribut, ce node ne fait absolument rien! :sourit:</p>
<center>:longBar:</center>
<h5>Préparation des attributs <a name="preparationDesAttributs"></a></h5>
<p>Comme promis on va commencer par la méthode <em>nodeInitializer()</em> qui initialiser les attributs du node.</p>
<p>Nous allons avoir besoin de trois attributs:</p>
<ul>
<li>Deux en entrée (input): Le mesh qui projette ses vertex et celui qui les reçoit.</li>
<li>Un en sortie (output): Le mesh de sortie, le mesh projeté.</li>
</ul>
<p>C'est partie! :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>La première ligne:</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>Crée un "objet" (appelé dans l'API Maya: "<a href="http://download.autodesk.com/us/maya/2009help/API/group___m_fn.html">Function Set</a>") qui va nous servir à manipuler les attributs (les créer surtout):</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>On créé les attributs. Rien de bien compliqué (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_typed_attribute.html#fc105a88372e84910bc56c74c973bc26">voir doc</a>).</p>
<p>Quelques précisions sur les arguments utilisés:</p>
<ul>
<li>Le nom entier de l'attribut (long name).</li>
<li>Le nom court de l'attribut (short name).</li>
<li>Le "type" (au sens <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html">API type</a>) de l'attribut.</li>
</ul>
<p>Les méthodes qui suivent chaque déclaration d'attribut (<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>) rajoutent des particularitées au dernier attribut créé (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html">voir la doc</a> pour plus de précisions).</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>Comme le commentaire l'indique, cette partie ajoute/connecte les attributs créés plus haut au 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>Cette partie est très importante! :papi:</p>
<p>Elle permet de définir des "dépendances" entre les attributs.</p>
<p>Dans notre cas:</p>
<ul>
<li>Si l'attribut <em>inputMeshSrc</em> change, l'attribut <em>outputMesh</em> changera aussi.</li>
<li>Si l'attribut <em>inputMeshTarget</em> change, l'attribut <em>outputMesh</em> changera aussi.</li>
</ul>
<p>Si ces lignes ne sont pas mises, la méthode <em>compute</em> du node que nous sommes en train de créer ne sera jamais lancé. Le node ne sera donc jamais mis à jour.</p>
<p>Vous pouvez télécharger le node python en l'état actuel ici:</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>Préparer sa scene <a name="preparerSaScene"></a></h5>
<p>Avant de réellement coder le comportement du node, il nous faut de la géométrie déjà présentes dans la scène à laquelle connecter notre futur node.</p>
<p>Créer une scène qui ressemble à ça:</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>Créez aussi un troisième mesh, celui qui renverra la géométrie de notre futur node (qui sera le mesh projeté).</p>
<p>Dans mon cas: Une pSphere. Mais ça peut être n'importe quoi.</p>
<p>Tant que c'est un node de mesh. Vous pouvez même créer le node de mesh à la main.</p>
<p>Placez le au centre de la scène (0,0,0) pour qu'il n'y ai pas de décalage entre le mesh projeté et sa cible.</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>Pour éviter d'avoir à créer les connections à chaque fois, voici deux petits codes MEL qui permettent de tester votre node (il faut que vos nodes soit bien nommés).</p>
<p>Pour charger le tout:</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>Et pour tout décharger:</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>Et voila le travail! Maintenant prenez une grosse inspiration, on saute!</p>
<center>:longBar:</center>
<h5>La méthode compute <a name="laMethodeCompute"></a></h5>
<p>La première chose à tester est la présence d'une connexion sur votre attribut outputMesh. En effet, si votre node n'est connecté à rien, il ne faut pas qu'il se calcule:</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>Une fois qu'on est sûr que les connections sont bonnes, on récupère les attributs d'entrés:</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>Ceci fait une "connection" vers le bloc de donnée des attributs. C'est la première étape pour récupérer la valeur (ici c'est un kMesh donc ce sera un peu différent) d'un attribut.</p>
<blockquote><p>Python étant un langage non typé (A la fois sa principale qualité mais aussi son principal défaut...), j'ai tendance à écrire en commentaire le type des données de l'API Maya que je récupère.</p>
<p>
Sinon, on a (très) vite fait de ne plus savoir du tout quelle variable correspond à quel type (surtout que des types dans l'API Maya, c'est pas ça qui manque :aupoil: ).</p>
<p>
Après, chacun sa méthode! Si vous avez un cortex sur développé, que vous voulez vous la jouer "Reunabranlé moi j'type queudal", qu'un code que vous êtes le seul à pouvoir lire vous met le kiki tout dur et que vous voulez justifier votre BAC+5 (par les temps qui courent ce n'est sûrement pas votre salaire qui doit s'en charger). N'hésitez pas, codez comme un <del>TD</del> porc: Pas de commentaires, des variables à une lettre et autres joyeusetés du genre... Vos collègues vous le rendront bien. :sourit:</p>
<p>
Mais si vous êtes plus modeste et souhaitez apprendre rapidement l'API Maya, je vous recommande vivement d'écrire les types de l'API Maya via des commentaires, directement dans votre code. En plus d'être plus clair, ça oblige à toujours savoir/chercher, quand on écrit des variables, à quel type elle correspond.</p></blockquote>
<p>Après ça, nous vérifions que nos deux attributs connectés sont bien des 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>Et nous les récupèrons en tant que tel:</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>Je vous invite à regarder la doc de <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> histoire de voir ce qu'on peut récupérer d'un attribut.</p>
<p>Comme précisé dans le commentaire, on récupère un <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_object.html">MObject</a>. Ce type d'objet un peu "le type à tout faire" dans Maya.</p>
<p>Ce MObject n'est qu'un objet de transition. En effet, il est rarement utilisé directement.</p>
<p>Dans Maya, pour modifier/manipuler des objets, on passe souvent par des <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_base.html">"Function Set"</a>. Ils ont la forme: MFn*Type*.</p>
<p>Ici, pour manipuler les mesh, on va récupérer un function set de 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>Ce qui nous intéresse maintenant c'est d'avoir une liste de tout les vertex du "mesh source" (celui qui va être projeté sur le "mesh cible") afin de créer un autre tableau de vertex qui contiendra leurs positions modifié:</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>La première ligne créée un tableau de type <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point_array.html">MPointArray</a>.</p>
<p>La seconde ligne le remplit avec les valeurs des vertex du "mesh source".</p>
<p>La façon de l'écrire est un peu déroutante ("à l'envers" diront certains :reflechi: ) mais c'est comme ça que fonctionne <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#54dc2a4415a365c7193936dad6643b47">getPoint</a> comme pas mal d'autres fonctions de l'API Maya.</p>
<p>Plutôt que de renvoyer le résultat, il est stocké dans la variable fournit en argument.</p>
<p>Nous avons maintenant un MPointArray remplit de vertex avec leurs positions.</p>
<p>L'idée est maintenant de modifier la position de ces vertex afin qu'elles correspondent à la position projetée sur le "mesh cible".</p>
<p>Mais voila... Les positions des vertex que vous avez récupéré dans votre MPointArray sont en "object space". C'est-à-dire, relatif au centre de l'objet.</p>
<p>Nous allons nous heurter à un vrai problème. The big one! The ultimate: The matrices! *Voix qui résonne* :enerve:</p>
<center>:longBar:</center>
<h5>Les matrices expliquées aux graphistes <a name="lesMatricesExpliqueesAuxGraphistes"></a></h5>
<p>Quand on est graphistes, on en entend des fois parler sans trop savoir ce que c'est :bete: .</p>
<p>Ajoutez à ça que ce qu'on trouve sur le net est très scolaire et "trop mathématique" (On montre comment multiplier une matrice sans expliquer pourquoi on est amené à le faire).</p>
<p>Tout ça au point qu'on ne voit pas forcément le lien avec notre boulot.</p>
<p>Je vais modestement tenter d'expliquer ça d'un point de vue "graphiste" :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/">Photo</a> par <a href="http://www.flickr.com/photos/trinity-of-one/">My Melting Brain</a> sous licence Créative <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/deed.fr">by-nc-sa</a>. Merci à lui! :sourit:</i></small></center>
<p>Créez un 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>Vous avez sûrement remarqué, une fois votre cube créé, qu'il a un "point de pivot" avec des informations (position, rotation, échelle, etc...). Et bien ce point permet de faire un "lien mathématique" entre les points de vertex de votre cube et "le monde" (les coordonnées centrales du "monde" sont 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>Dans l'idée, si ce node (le node de "transform") n'existait pas, votre objet serait au centre de la scène. Et pour le déplacer il faudrait déplacer les positions de tout les vertex du cube.</p>
<p>Le node de transform agit un peu comme un "parent" des vertex de votre cube. De cette façon, les positions des vertex du cube ne bouge pas. Par exemple, un vertex du cube placé à 1,1,1 (par rapport au pivot du cube donc) restera à 1,1,1 quel que soit la position du 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>Ici, on déplace le pivot, pas les vertex du cube qui eux, ne change pas de place par rapport au pivot, c'est le pivot qui change de place par rapport au monde.</i></center>
<p>Mais pour pouvoir faire certaines opérations (dans notre cas, savoir si le vertex est dirigé vers un autre mesh), il faut que les coordonnées de toute les entités qui entrent en jeu soient sur un repère commun, le repère monde.</p>
<p>Démonstration en bédé:</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>Deux vertex placés à différents endroits dans l'espace monde...</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>...mais au même endroit par rapport à leurs centres respectifs.</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>Pas pratique pour faire des calcules.</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>
<p>Alors que si on choisi, comme point de repère commun, le centre du monde.</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>
<p>C'est beaucoup plus facile.</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>
<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>
<p>Bon, maintenant qu'on connaît les coordonnées des vertex dans leurs "espaces objets", il faut savoir comment les récupérer en "espace monde".</p>
<p>Le principe de base qui vient tout de suite à l'esprit est: On additionne les positions (relatives à l'objet) des vertex à la position (relative au monde) de son point de pivot.</p>
<blockquote><p>Exemple:</p>
<p>
Si pVertex la position d'un vertex et positionDuCube la position, dans le monde, du pivot du cube:</p>
<p>
positionDuCubeX + pVertexDansLeCubeX = pVertexDansLeMondeX</p></blockquote>
<p>Mais vous vous en doutez surement, c'est plus compliqué... :siffle:</p>
<p>En effet, dans le cas des rotations et de l'échelle, il ne suffit pas de quelques additions pour résoudre le problème.</p>
<blockquote><p>Note: Que ce soit une transformation, une rotation, ou une mise à l'échelle d'un mesh. Tout se résume à un déplacement des vertex dans l'espace.</p></blockquote>
<p>La vérité est que tout ses "paramètres" peuvent être mis dans un seul et même "objet" que l'on appel une matrice. Cette matrice, va nous servir à faire des calcules (que Maya nous épargne mais si ça vous intéresse, voici un exemple de <a href="http://jeux.developpez.com/faq/math/?page=bases#Q4">calcule d'une matrice de rotation</a>) pour récupérer les positions des vertex dans l'espace monde.</p>
<p>Comme je disait, Maya nous donne très facilement accès à cet objet grâce à l'<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> (Il y a plusieurs types de matrices, nous nous focaliserons que sur celle là).</p>
<p>Du coup, à ce stade, nous avons deux choses:</p>
<ul>
<li>Les positions des vertex relativement à l'objet (en "object space").</li>
<li>Une matrice de l'objet (qui est relative au monde, en "world space").</li>
</ul>
<p>Il faut donc "convertir" les positions des vertex de "object space" vers "world space". On Parle d'un changement de repère. Vous obtenez donc une position dite absolue ("world space").</p>
<p>Et pour obtenir la position d'un vertex dans le "world space", il "suffit" de multiplier la matrice de position d'un vertex {x,y,z} par l'inclusive matrix de l'objet. (J'ai mis "suffit" entre guillemet car multiplier une matrice c'est pas aussi simple que faire 2x2... :redface: )</p>
<blockquote><p>Note: Je ne ferais pas de démonstration sur "comment calculer une matrice", le net regorgeant d'exemples et d'explications.</p></blockquote>
<p>Et voici la formule:</p>
<pre>
positionGlobale = positionLocale * inclusiveMatrix
</pre>
<p>C'est un peu comme le théorème de Pythagore. On s'en fout de comment ça marche, tant qu'on sait quand l'utiliser! :baffed:</p>
<p>Voila! J'espère que cette petite explication vous aura éclairé un peu sur le pourquoi des matrices. :sourit:</p>
<center>:longBar:</center>
<h5>Revenons au code! <a name="revenonsAuCode"></a></h5>
<p>Pour récupérer l'inclusive matrice, rien de plus 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>La première ligne permet de récupérer le <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html">dagPath</a> du mesh source. On peut considérer le dagPath comme étant l'équivalent du node de transform d'un objet dans Maya. Là ou toutes les informations sur les transformations (positions, rotations, échelles, etc...) sont stocké.</p>
<p>La seconde ligne récupère l'<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> du mesh source sous forme d'une <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_matrix.html">MMatrix</a>.</p>
<p>Maintenant, nous allons pouvoir parcourir chaque vertex:</p>
<ul>
<li>Le <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point.html#1fdf65c85cf27f6ce4ddef0a72aac5d3">multiplier par la matrice</a> afin d'avoir sa position dans le world space.</li>
<li>Récupérer sa normale.</li>
<li><a href="http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html#d22edbae3de3e0742534acff6b4f6227">La mutiplier</a> elle aussi par la matrice.</li>
<li>Récupérer le point de collision, le stocker à la place du point en court.</li>
</ul>
<center>:longBar:</center>
<h5>Parcourir et modifier chaque vertex <a name="parcourir_et_modifier_chaque_vertex"></a></h5>
<p>Le début de la boucle principale ressemble à ça:</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>La boucle est simple: "i" sera incrémenté de 1 à chaque "tour" pour parcourir le tableau de vertex (MPointArray).</p>
<p>La première chose qu'on fait est une multiplication du point (<em>outMeshMPointArray[i]</em>) par la matrice (<em>inMeshSrcInclusiveMMatrix</em>) pour obtenir un vertex (<em>inMesgPointTmp</em>) en world space (avec des coordonnées relatives au "monde").</p>
<p>Maintenant que nous avons (enfin) le vertex positionné par rapport au monde, on va "l'intersectionner" (si les gars de l'académie française voyait ça :pasClasse: ) avec le mesh cible.</p>
<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/">Photo</a> par <a href="http://www.flickr.com/photos/mynamemattersnot/">MyNameMattersNot</a> sous licence Créative <a href="http://creativecommons.org/licenses/by-sa/2.0/deed.fr">by-sa</a>. Merci à lui!</i></small></center>
<p>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/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#theorie">nous avont vu plus haut</a>.</p>
<p>Vous voyez qu'il y en a pas mal. :sourit:</p>
<p>Rassurez vous, nous pouvons en faire sauter la plupart. Ce qui nous intéresse c'est le vertex d'origine, sa direction (dans notre cas: la normale) et le point de "collision" (le hitPoint).</p>
<p>Mais plus subtile encore, regardez le type du premier argument attendu par la méthode (le raySource).</p>
<p>C'est un MFloatPoint!</p>
<p>Mais comment convertir <em>inMeshMPointTmp</em> (un MPoint) en MFloatPoint? En C++ c'est assez facile, il faut passer par des doubles. Après moult recherches, j'ai trouvé une solution. Je vous la donne de but en blanc:</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>C'est pas si compliqué mais si tu le sais pas...</p>
<p>Nous avons donc notre raySource.</p>
<p>Maintenant la direction:</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>Arrivé ici vous devriez comprendre:</p>
<ul>
<li>On créé le MVector.</li>
<li>On y stock la normale du vertex courant ("i") du "mesh source".</li>
<li>On multiplie par la matrice pour avoir ce vecteur relatif à l'espace monde.</li>
<li>On le "convertie" en MFloatVector.</li>
</ul>
<p>Le hitPoint quand à lui est un 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>Et le reste des arguments sont les suivants:</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>Un grand merci à <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>, je n'aurais jamais réussi à coder ce truc. C'est pour ça qu'il faut "partager ce qu'on sait" sur internet! ;)</p>
<p>Une fois le <em>closestIntersection</em> appelé, vous récupérez un hitPoint en MFloatPoint que vous reconvertissez en 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>On remplace le point courant par notre nouveau point:</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>Et c'est la fin de la boucle! :D</p>
<p>Arrivé ici vous avez un MPointArray <em>outMeshMPointArray</em> avec les valeurs des vertex projetés sur le mesh cible.</p>
<p>Il faut donc maintenant reconstruire le mesh.</p>
<center>:longBar:</center>
<h5>Construire un mesh <a name="construire_un_mesh"></a></h5>
<p>Je ne vais pas rentrer précisément dans les détails sur "comment créer et agencer les variables dans le cas de la création d'un mesh.</p>
<p>En gros il nous faut:</p>
<ul>
<li>Le nombre de vertex.</li>
<li>Le nombre de polygones (polygones + triangles si il y en a).</li>
<li>Un tableau de point (Tout les vertex à la queue leu leu avec leurs coordonnées XYZ).</li>
<li>Un tableau listant le nombre de vertex par polygones (Exemple: 4,4,4,4,3,4,3,4,3,4,4,etc...)</li>
<li>Un tableau d'index des vertex (Exemple: 1,2,3,4,3,4,5,6,5,6,7,8, etc...)</li>
</ul>
<p>Vous l'aurez compris, on déjà le tableau des points (le troisième point). Pour le reste, on le récupère bêtement sur le mesh d'origine.</p>
<p>Commençons! :hehe:</p>
<p>Dans un premier temps il faut créer un function set <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh_data.html">MFnMeshData</a>.</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>Ce function set va nous permettre de créer un MObject que nous allons pouvoir "remplir" des données du futur 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>Comme je vous l'ai dit plus haut: Il faut qu'on récupère toute les informations (hormis le tableau de vertex) du mesh source:</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> remplit les deux tableaux avec les informations nécessaires à la création du mesh voir <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#construire_un_mesh">plus haut</a></p>
<p>Une fois que nous avons tout ça, nous créons le 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>Le principe est assez simple:</p>
<ul>
<li>On créer un function set <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html">MFnMesh</a></li>
<li>On créer le mesh en donnant tout les arguments (récupéré plus haut) via <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#6994886ddf6ea46baf399ff40d30bc32">meshFS.create()</a>.</li>
</ul>
<p>Une fois que le mesh est créé, on récupère le <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> de la connexion "outputMesh " <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html#959379b4d93358519ccc99696031756c">pour y "mettre"</a> le MObject que l'on vient de remplir: Le 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>Une fois cela fait, on dit au dependency graph, via <a href="http://download.autodesk.com/us/maya/2010help/api/class_m_data_block.html#0d8faafb64e70cf0a579532bb033c98f">MDataBlock.setClean()</a>, que la connexion a été mise à jour.</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>Et c'est fini! :youplaBoum:</p>
<p>Si vous avez bien suivi le tuto (et si je ne me suis pas planté ( :baffed: ), vous devriez avoir un node qui marche correctement (placez le plan de sorte qu'il "vise" la sphère):</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>Bien sur, si les vertex ne sont pas projeté sur la sphère, il retourne à leur position d'origine:</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>Conclusion et code source <a name="conclusion_et_code_source"></a></h5>
<p>Arrivez ici, vous devriez avoir compris le principe des matrices (Si ce n'est pas le cas, n'hésitez pas à approfondir, vous verrez la 3D d'une autre façon! :sourit: ), être capable de récupérer les composants d'un mesh et de créer un mesh à partir de rien.</p>
<p>Voici le code source: <a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projectMesh.7z">projectMesh.7z</a></p>
<p>Et voilà! Je viens de finir un autre gros tuto. J'espère qu'il aura été instructif et que vous allez pouvoir commencer à faire des choses intéressantes avec l'API Maya. :banaeyouhou:</p>
<p>Ce genre d'informations manquent sur l'internet de l'infographie francophone. :franceHappy:</p>
<p>J'invite donc les seniors qui passeraient par là et qui tireraient quelque chose d'intéressant de ce qu'ils ont lu de ne pas hésiter à "rendre la pareille": Si vous êtes compétent dans un domaine, partagez! Je l'ai fait. :hihi:</p>
<p>N'hésitez pas à me dire si je me suis trompé quelque part, si un point ne vous semble pas clair ou si il y a une erreur. :pasClasse:</p>
<p>A bientôt!</p>
<center>:marioCours:</center>
<center>:longBar:</center>
<h5>Mise à jour: La méthode sans Python <a name="la_methode_sans_python"></a></h5>
<p>Bon, ce n'est pas vraiment le but mais puisque Kel Solar en parle <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#c205">dans les commentaires</a> je vous donne un moyen de faire ça en utilisant la méthode intégré dans Maya. :seSentCon:</p>
<p>Sélectionner le mesh cible:</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>Sélectionner le mesh source:</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>Ouvrez les options du transfert d'attributs:</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>Settez les options comme suis:</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>En gros, on ne transfert que la position des vertex dans l'espace monde en les projetant le long de leur normale.</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>Et voilà le travail!</p>
<p>Vous pouvez tourner le mesh source et c'est actualisé directement! :laClasse:</p>
<p>Voilà! Comme ça, les personnes qui sont venu pour trouver une solution rapide ne seront pas frustré! :sourit:</p>Une introduction à l'OpenGL "Moderne" - Chapitre 2.1: Les Buffers Et Les Texturesurn:md5:a3bdc41a7f120e318fe47092623cfc242010-10-31T23:49:00+01:002013-07-26T18:12:21+02:00NarannScript et codebuffercodefropenglshadertexturetuto<p><img src="https://www.fevrierdorian.com/blog/public/logos/OpenGL_logo_tn150.png" alt="OpenGL_logo_tn150.png" style="float:left; margin: 0 1em 1em 0;" title="OpenGL_logo_tn150.png, oct. 2010" height="150" width="150" />Ça y est, on attaque du vrais code! Ne soyez pas trop impatient tout de même. En effet, cette partie est la première d'une série de trois. :seSentCon:</p>
<p>Ici vous allez commencer à écrire du code qui sera commenter, ligne à ligne, pour vous expliquer ce qui se passe dans votre bécane!</p>
<p>Prenez un peu de temps devant vous on attaque du gros! :grenadelauncher:</p> <h4>Une introduction à l'OpenGL "Moderne" - Chapitre 2.1: Les Buffers Et Les Textures</h4>
<h5>Sommaire</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#le_pipeline_revisite">Le pipeline revisité</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#le_typage_c_dans_opengl">Le typage C dans OpenGL</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#stocker_nos_donnees">Stocker nos données</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#le_modele_objet_dopengl">Le modèle objet d'OpenGL</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#les_buffers_objects">Les "buffer objects"</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#les_textures_objects">Les "texture objects"</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#le_sampling_et_les_parametres_des_textures">Le sampling et les paramètres des textures</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#allouer_les_textures">Allouer les textures</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/31/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2.1%3A-Les-Buffers-Et-Les-Textures#partie_suivante_les_shaders">Partie suivante: Les shaders</a></li>
</ul>
<center>:longBar:</center>
<p>Dans <a href="https://www.fevrierdorian.com/blog/post/2010/10/23/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2-Hello-World-Le-Slideshow">la partie précédente</a>, nous avions une fenêtre ouverte attendant les instructions qui afficheront notre programme "Hello World".</p>
<p>Mais avant de dessiner quoi que ce soit, nous allons avoir besoin d'envoyer des données à OpenGL en créant des "<strong>objets</strong>" de différents types.</p>
<p>Voyons ensemble les "objets" que nous allons avoir besoin de configurer.</p>
<center>:longBar:</center>
<h5>Le pipeline revisité <a name="le_pipeline_revisite"></a></h5>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-pipeline-01.png" alt="gl2-pipeline-01.png" style="display:block; margin:0 auto;" title="gl2-pipeline-01.png, oct. 2010" height="656" width="504" /></p>
<p>Revoir le pipeline graphique (cf <a href="https://www.fevrierdorian.com/blog/post/2010/10/04/Une-introduction-%C3%A0-l-OpenGL-Moderne-Chapitre-1%3A-Le-Pipeline-Graphique">chapitre précédent</a>), en gardant à l'esprit notre programme "hello world", va nous permettre de définir clairement quels sont les "objets" dont nous allons avoir besoins.</p>
<p>En commençant par l'entrée, notre vertex array contiendra quatre vertices, que le vertex shader assignera aux coins de la fenêtre.</p>
<p>L'element array assemblera ces quatres vertices en deux triangles, se qui nous donnera un rectangle qui viendra couvrir notre fenêtre.</p>
<p>Nous fabriquerons des objets de type buffer (<strong>buffer objects</strong>) qui contiendront ces arrays dans la mémoire du GPU.</p>
<p>Notre "uniform state" (l'ensemble de paramètres/variables qui ne change pas durant le rendu) sera composé de deux images "Hello" et d'un "fade factor" pour les mélanger.</p>
<p>Chacune de ses images aura sa propre <strong>texture object</strong>.</p>
<p>En plus de mapper nos vertices aux coins de l'écran, le vertex shader assignera un set de coordonnées de texture à chaque vertex, mappant les vertices aux coins (de la texture) correspondant.</p>
<p>Le rasterizer interpolera ensuite ces coordonnées de texture le long de la surface du rectangle (<a href="https://www.fevrierdorian.com/blog/post/2010/10/04/Une-introduction-%C3%A0-l-OpenGL-Moderne-Chapitre-1%3A-Le-Pipeline-Graphique#la_rasterisation_ou_pixelisation">Rappelez vous</a>) . Une fois cela fait, notre fragment shader "samplera" les deux textures et les mélangera en utilisant le "fade factor".</p>
<p>Pour "connecter" les shaders à OpenGL, nous allons créer un "objet" de type <strong>program</strong> qui liera le <strong>vextex shader</strong> et le <strong>fragment shader</strong>.</p>
<p>Dans cet article, nous allons configurer le buffer et les textures. La prochaine fois, nous travaillerons sur les shaders.</p>
<center>:longBar:</center>
<h5>Le typage C dans OpenGL <a name="le_typage_c_dans_opengl"></a></h5>
<p>OpenGL dispose de son propre set de typedef qui sont en fait les équivalents GL* des typedefs C:</p>
<ul>
<li>GLubyte</li>
<li>GLbyte</li>
<li>GLushort</li>
<li>GLshort</li>
<li>GLuint</li>
<li>GLint</li>
<li>GLfloat</li>
<li>GLdouble</li>
</ul>
<p>Chacun correspondant à son type C comme vous vous en doutez.</p>
<p>En plus de ces types de base, OpenGL fournit des typedefs additionnels avec un sens plus sémantique:</p>
<ul>
<li>GLchar*, utilisé par les fonctions qui gèrent les strings. C'est un pointeur vers une string ASCII "null-terminated".</li>
<li>GLclampf et GLclampd, typdedefs pour GLfloat et GLdouble utilisé quand les valeurs attendues doivent être comprise entre zéro et un.</li>
<li>GLsizei, un typedef integer utilisé pour récupérer/conserver la taille d'un memory buffer, équivalent à size_t de la bibliothèque standard C.</li>
<li>GLboolean, un typedef pour GLbyte prévu pour contenir un GL_TRUE ou GL_FALSE, similaire au bool du C++ ou C99</li>
<li>GLenum, un typedef de GLuint prévu pour contenir une constante GL_* prédéfini.</li>
<li>GLbitfield, un autre typedef de GLuint prévu pour contenir l'opérateur OR d'un masque ou plus de GL_*_BIT</li>
</ul>
<center>:longBar:</center>
<h5>Stocker nos données <a name="stocker_nos_donnees"></a></h5>
<pre class="c c"><span style="color: #993333;">static</span> <span style="color: #993333;">struct</span> <span style="color: #009900;">{</span>
GLuint vertex_buffer<span style="color: #339933;">,</span> element_buffer<span style="color: #339933;">;</span>
GLuint textures<span style="color: #009900;">[</span><span style="color: #0000dd;">2</span><span style="color: #009900;">]</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* fields for shader objects ... */</span>
<span style="color: #009900;">}</span> g_resources<span style="color: #339933;">;</span></pre>
<p>Une structure globale comme g_ressources est la manière la plus simple de partager des données entre notre code d'initialisation et notre contexte GLUT.</p>
<p>OpenGL utilise des valeurs GLunint pour la manipulation des objets.</p>
<p>Notre structure g_ressources contient deux variables GLuint que nous utiliserons pour conserver le nom de nos vertex buffer et element buffer. Ainsi qu'un array de deux GLuint pour nos deux textures.</p>
<p>Nous continuerons d'ajouter des variables à notre structure au fil des shaders que nous coderons dans la prochaine partie.</p>
<center>:longBar:</center>
<h5>Le modèle objet d'OpenGL <a name="le_modele_objet_dopengl"></a></h5>
<p>La convention d'OpenGL pour manipuler les objets et un peut inhabituel.</p>
<p>Vous créez des objets en générant un ou plusieurs <strong>noms d'objet</strong>. Pour cela, vous utilisez une fonction glGen*s (exemple: glGenBuffers, glGenTextures, etc...).</p>
<p>Comme mentionné plus haut, ses noms sont des variables de type GLuint. N'importe quelles données fournit ou associé à l'objet est géré en interne par OpenGL.</p>
<p>Cette façon de faire est assez classique.</p>
<p>En revanche, la façon d'utiliser ses noms est inhabituelle:</p>
<p>Pour manipuler un objet, vous devez d'abord lier son nom à une <strong>cible</strong> OpenGL définie (on appelle ça le target binding), en appelant la fonction glBind* correspondante (exemple: glBindBuffer, glBindTexture, etc...).</p>
<p>Ensuite, vous passez cette cible à une fonction OpenGL en tant qu'argument, ce qui set ses propriétés ou charge des données dans l'objet attaché.</p>
<p>Les targets bindings affectent également les fonctions OpenGL qui ne prennent pas explicitement les dites cibles en argument, comme nous le verrons lorsque nous parlerons du rendu.</p>
<p>Pour l'instant, voyons comment tout ça se comporte quand nous construisons des buffer objects:</p>
<center>:longBar:</center>
<h5>Les "buffer objects" <a name="les_buffers_objects"></a></h5>
<pre class="c c"><span style="color: #993333;">static</span> GLuint make_buffer<span style="color: #009900;">(</span>
GLenum target<span style="color: #339933;">,</span>
<span style="color: #993333;">const</span> <span style="color: #993333;">void</span> <span style="color: #339933;">*</span>buffer_data<span style="color: #339933;">,</span>
GLsizei buffer_size
<span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
GLuint buffer<span style="color: #339933;">;</span>
glGenBuffers<span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> <span style="color: #339933;">&</span>buffer<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glBindBuffer<span style="color: #009900;">(</span>target<span style="color: #339933;">,</span> buffer<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glBufferData<span style="color: #009900;">(</span>target<span style="color: #339933;">,</span> buffer_size<span style="color: #339933;">,</span> buffer_data<span style="color: #339933;">,</span> GL_STATIC_DRAW<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">return</span> buffer<span style="color: #339933;">;</span>
<span style="color: #009900;">}</span></pre>
<p>Les buffers sont des pointeurs vers de la mémoire OpenGL.</p>
<p>En d'autres termes, ils sont utilisés pour stocker des vertex arrays (en utilisant la cible GL_ARRAY_BUFFER) et des element array(en utilisant la cible GL_ELEMENT_ARRAY_BUFFER)</p>
<p>Quand vous allouez un buffer avec glBufferData, vous précisez un "<strong>usage hint</strong>" (une "intention d'usage" en français, GL_STATIC_DRAW dans notre cas), qui indique combien de fois vous avez l'intention d'accéder et de modifier les données du buffer, et OpenGL choisi, entre la mémoire CPU ou celle du GPU, quel est le meilleur endroit pour stocker ces informations.</p>
<p>Le "hint" (l'intention) ne contraint en rien la façon dont le buffer est utilisé, mais utiliser les buffers d'une façon différente de celle précisé (via le "hint"), entraînera une perte de performance.</p>
<p>Pour notre programme, nous avons des vertex arrays et element arrays constant qui ne change jamais, donc nous donnons à glBufferData l'argument (le hint) GL_STATIC_DRAW.</p>
<p>Le mot STATIC indique que nous ne comptons jamais changer les données.</p>
<p>Les buffers peuvent également être "hinted" en:</p>
<ul>
<li>DYNAMIC: Qui indique que nous comptons écrire dans le buffer fréquemment.</li>
<li>STREAM: Qui indique que nous comptons régulièrement remplacer entièrement le contenu du buffer.</li>
</ul>
<p>Le mot DRAW indique que nous comptons lire le buffer uniquement depuis le GPU.</p>
<p>Les alternatives à DRAW sont:</p>
<ul>
<li>READ, qui défini un buffer qui sera principalement lu par le CPU.</li>
<li>COPY, qui indique que le buffer sera un "intermédiaire" entre le CPU et le GPU et qu'aucune préférence n'est défini.</li>
</ul>
<p>Les buffers de vertex array et d'element array utiliseront principalement le "hint" (l'intention) GL_*_DRAW.</p>
<p>La fonction glBufferData considère vos variables de la même façon que memcpy: Juste un bête flux d'octets.</p>
<p>Nous ne préciserons la structure de nos arrays à OpenGL qu'une fois qu'on s'en servira pour le rendu.</p>
<p>Ceci permet aux buffers de stocker des vertex attributes (et autres données) dans n'importe quel format, ou bien d'envoyer ces données d'une façon différente aux render jobs.</p>
<pre class="c c"><span style="color: #993333;">static</span> <span style="color: #993333;">const</span> GLfloat g_vertex_buffer_data<span style="color: #009900;">[</span><span style="color: #009900;">]</span> <span style="color: #339933;">=</span> <span style="color: #009900;">{</span>
<span style="color: #339933;">-</span><span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span>
<span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span>
<span style="color: #339933;">-</span><span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span> <span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span>
<span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span> <span style="color:#800080;">1.0f</span>
<span style="color: #009900;">}</span><span style="color: #339933;">;</span>
<span style="color: #993333;">static</span> <span style="color: #993333;">const</span> GLushort g_element_buffer_data<span style="color: #009900;">[</span><span style="color: #009900;">]</span> <span style="color: #339933;">=</span> <span style="color: #009900;">{</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">2</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">3</span> <span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<p>Ici, nous ne faisont que définir les coins de notre rectangle en tant que "set" de quatre vecteurs, composé de deux composant chacun.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-vertex-array-01.png" alt="gl2-vertex-array-01.png" style="float:right; margin: 0 0 1em 1em;" title="gl2-vertex-array-01.png, oct. 2010" height="88" width="209" />Notre element array est également simple, un tableau de GLushorts servant à définir l'ordre des index des vertex de manière à ce qu'ils puissent être assemblé en rectangle sous la forme de "triangle strip" (voir <a href="https://www.fevrierdorian.com/blog/post/2010/10/04/Une-introduction-%C3%A0-l-OpenGL-Moderne-Chapitre-1%3A-Le-Pipeline-Graphique#assemblage_des_triangles">chapitre précédent</a>).</p>
<p>Sur les implémentations OpenGL standard, un element array peut être de type GLubyte (8bits), GLushort (16bits) ou GLuint (32bits); Pour OpenGL ES, seul GLubyte et GLushort peuvent être utilisés.</p>
<p>Nous allons maintenant remplir notre fonction make_ressources d'appels à make_buffer qui alloueront et remplieront nos buffers:</p>
<pre class="c c"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> make_resources<span style="color: #009900;">(</span><span style="color: #993333;">void</span><span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
g_resources.<span style="color: #202020;">vertex_buffer</span> <span style="color: #339933;">=</span> make_buffer<span style="color: #009900;">(</span>
GL_ARRAY_BUFFER<span style="color: #339933;">,</span>
g_vertex_buffer_data<span style="color: #339933;">,</span>
<span style="color: #993333;">sizeof</span><span style="color: #009900;">(</span>g_vertex_buffer_data<span style="color: #009900;">)</span>
<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
g_resources.<span style="color: #202020;">element_buffer</span> <span style="color: #339933;">=</span> make_buffer<span style="color: #009900;">(</span>
GL_ELEMENT_ARRAY_BUFFER<span style="color: #339933;">,</span>
g_element_buffer_data<span style="color: #339933;">,</span>
<span style="color: #993333;">sizeof</span><span style="color: #009900;">(</span>g_element_buffer_data<span style="color: #009900;">)</span>
<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* make textures and shaders ... */</span>
<span style="color: #009900;">}</span></pre>
<center>:longBar:</center>
<h5>Les "texture objects" <a name="les_textures_objects"></a></h5>
<pre class="c c"><span style="color: #993333;">static</span> GLuint make_texture<span style="color: #009900;">(</span><span style="color: #993333;">const</span> <span style="color: #993333;">char</span> <span style="color: #339933;">*</span>filename<span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
GLuint texture<span style="color: #339933;">;</span>
<span style="color: #993333;">int</span> width<span style="color: #339933;">,</span> height<span style="color: #339933;">;</span>
<span style="color: #993333;">void</span> <span style="color: #339933;">*</span>pixels <span style="color: #339933;">=</span> read_tga<span style="color: #009900;">(</span>filename<span style="color: #339933;">,</span> <span style="color: #339933;">&</span>width<span style="color: #339933;">,</span> <span style="color: #339933;">&</span>height<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">(</span><span style="color: #339933;">!</span>pixels<span style="color: #009900;">)</span>
<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span></pre>
<p>Comme mentionné dans <a href="https://www.fevrierdorian.com/blog/post/2010/10/23/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2-Hello-World-Le-Slideshow">la partie précédente</a>, j'utilise <a href="https://secure.wikimedia.org/wikipedia/fr/wiki/Truevision_Targa" hreflang="fr">le format TGA</a> pour stocker nos images "hello world".</p>
<p>Je ne perdrais pas de temps à expliquer le code de parsing; Il est dans <a href="http://github.com/jckarter/hello-gl/blob/master/util.c" hreflang="en">util.c</a> dans le repo Github si vous voulez le voir.</p>
<p>Les données des pixels d'un TGA sont stocké sous forme de tableaux simples, non compressé, de pixels. Chaque pixel ayant trois valeurs: <a href="https://secure.wikimedia.org/wikipedia/fr/wiki/Rouge_vert_bleu" hreflang="fr">RGB</a> (En fait, stocké dans l'ordre BGR). Chacune de ses valeur est composé d'un octets (2^8 = 256 valeurs possible). L'ordre des pixels commence du bas à gauche de l'image et avance vers la droite, en montant.</p>
<p>Ce format est parfait pour être utilisé conjointement avec les textures OpenGL, comme nous allons le voir tout de suite.</p>
<p>Si la lecture de l'image échoue, on renvoie zéro, qui est le code d'un "objet null" et ne sera jamais utilisé par un vrai objet OpenGL.</p>
<pre class="c c">glGenTextures<span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">,</span> <span style="color: #339933;">&</span>texture<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glBindTexture<span style="color: #009900;">(</span>GL_TEXTURE_2D<span style="color: #339933;">,</span> texture<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>Les texture objects sont des pointeurs sur des tableaux de la mémoire GPU spécialisé dans le stockage des textures.</p>
<p>OpenGL supporte plusieurs types de textures, chacun dispose de son propre flag:</p>
<ul>
<li>1d (GL_TEXTURE_1D)</li>
<li>2d (GL_TEXTURE_2D)</li>
<li>3d (GL_TEXTURE_3D)</li>
</ul>
<p>Il y a d'autres types de textures, plus spécialisé, dont nous parlerons peut être plus tard.</p>
<p>Les textures 2d sont de loin les plus couramment utilisé.</p>
<p>Ici, nous générons une GL_TEXTURE_2D pour une de nos images.</p>
<p>Les texture objects n'ont pas grand chose à voir avec les buffer objects dans la mesure ou le GPU gère la mémoire des textures d'une manière très différente de celle des buffers.</p>
<center>:longBar:</center>
<h5>Le sampling et les paramètres des textures <a name="le_sampling_et_les_parametres_des_textures"></a></h5>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-texcoords-01.png" alt="gl2-texcoords-01.png" style="float:right; margin: 0 0 1em 1em;" title="gl2-texcoords-01.png, oct. 2010" height="272" width="189" />Bien que les éléments des vertex arrays soit utilisé par le vertex shader un par un (et il n'y a aucun moyen, pour le vertex shader, d’accéder à d'autres éléments que ceux dont il dispose), les vertex shaders et fragments shaders peuvent accéder au contenu d'une texture quand il le souhaite.</p>
<p>Les shaders "<strong>samplent</strong>" (échantillonnent/prennent une partie de) la texture à un point de <strong>coordonné de la texture</strong>.</p>
<p>Les éléments du texture array sont répartis uniformément dans le <strong>texture space</strong> ("espace texture" en français) qui est en fait un carré couvrant les coordonnées (0, 0) (1, 1) (Ou une ligne (0, 1) pour une texture 1d, ou un cube (0, 0, 0) (1, 1, 1) pour une texture 3d).</p>
<p>Pour les distinguer des coordonnées x, y, z de l'object space, OpenGL nomme ses axes du texture space s, t, et r.</p>
<p>Le carré du texture space est divisé uniformément, le long de ses axes, en cellules rectangulaires, correspondant à la largeur et la hauteur de la texture array.</p>
<p>La cellule (0, 0) renvoie au premier élément du texture array, et les autres éléments sont répartis dans les cellules suivantes, le long de l'axe s et t.</p>
<p>"Sampler" la texture au centre d'une de ces cellules renvoie l'élément du texture array correspondant.</p>
<p>Note that the t axis can be thought of as increasing either upward or downward (or in any direction, really), depending on the representation of the underlying array.</p>
<blockquote><p>Je n'ai pas réussi à traduire. Si vous avez une idée n'hésitez pas :baffed:</p></blockquote>
<p>The other axes of texture space are similarly arbitrary.</p>
<blockquote><p>Idem... Désolé... :pasClasse:</p></blockquote>
<p>Dans la mesure ou les images TGA stockent leurs pixels de gauche à droite et de bas en haut, c'est de cette façon que je représenterai les axes.</p>
<pre class="c c">glTexParameteri<span style="color: #009900;">(</span>GL_TEXTURE_2D<span style="color: #339933;">,</span> GL_TEXTURE_MIN_FILTER<span style="color: #339933;">,</span> GL_LINEAR<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glTexParameteri<span style="color: #009900;">(</span>GL_TEXTURE_2D<span style="color: #339933;">,</span> GL_TEXTURE_MAG_FILTER<span style="color: #339933;">,</span> GL_LINEAR<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glTexParameteri<span style="color: #009900;">(</span>GL_TEXTURE_2D<span style="color: #339933;">,</span> GL_TEXTURE_WRAP_S<span style="color: #339933;">,</span> GL_CLAMP_TO_EDGE<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glTexParameteri<span style="color: #009900;">(</span>GL_TEXTURE_2D<span style="color: #339933;">,</span> GL_TEXTURE_WRAP_T<span style="color: #339933;">,</span> GL_CLAMP_TO_EDGE<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>La manière dont le sampling se comporte quand une texture est samplé:</p>
<ul>
<li>entre les centres des cellules voisines</li>
<li>à l’extérieur du texture space (aux coordonnées (-0.1, -0.1) par exemple)</li>
</ul>
<p>est contrôlé par des <strong>paramètres de texture</strong> qui sont setté par la fonction glTexParameteri.</p>
<p>Les paramètres GL_TEXTURE_MIN_FILTER et GL_TEXTURE_MAG_FILTER contrôlent respectivement comment les points samplés "entre deux" sont traités quand la texture est samplé à une résolution inférieure ou supérieur que la résolution de l'écran.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-texture-filter-01.png" alt="gl2-texture-filter-01.png" style="float:right; margin: 0 0 1em 1em;" title="gl2-texture-filter-01.png, oct. 2010" height="73" width="134" />Nous settons ces paramètres sur GL_LINEAR pour dire au GPU d'utiliser une <strong>interpolation linéaire</strong> (ce qui mélange les quatre éléments les plus proches du point samplé).</p>
<p>Si l'utilisateur redimensionne notre fenêtre, la texture s'étirera en douceur.</p>
<p>Setter le filtre à GL_NEAREST dirait au GPU de renvoyer le texture element le plus proche du point samplé. Ce qui pixeliserai notre texture (effet Playstation).</p>
<p>Les paramètres GL_TEXTURE_WRAP_S et GL_TEXTURE_WRAP_T contrôlent de quelle façon les bords extérieurs de la texture sont considérés.</p>
<p>Dans la mesure ou nous ne prévoyions pas de d'aller chercher des informations à l’extérieur des champs S et T (0-1), nous utilisons GL_CLAMP_TO_EDGE, qui ramène les coordonnées en dessous de zéro à zéro et au-dessus de un à un. (Comme si les pixels du bord se repetaient)</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/5_627_d022a97d643b682.jpg" alt="5_627_d022a97d643b682.jpg" style="display:block; margin:0 auto;" title="5_627_d022a97d643b682.jpg, oct. 2010" height="342" width="377" /></p>
<center><i>Un exemple d'utilisation de GL_CLAMP_TO_EDGE. Sur cette image, on voit que les pixels des bords de la texture sont répétés</i></center>
<p>Si nous avions appliqué la valeur GL_WRAP à un ou aux deux axes, c'est la texture entière qui aurait été répété le long des axes du bord.</p>
<p>Si on pouvait résumer, je dirais que le "texture sampling" (l'échantillionnage de texture) ressemble à un tableau d'indexation 2d dans lequel on peut récupérer des valeurs entre les entrées.</p>
<p>Tout cela prendra plus de sens si on regarde de quelle manière le fragment shader sample la texture:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-texture-rasterization-01.png" alt="gl2-texture-rasterization-01.png" style="display:block; margin:0 auto;" title="gl2-texture-rasterization-01.png, oct. 2010" height="246" width="400" /></p>
<p>Dans notre vertex shader, nous allons assigner les coins de nos coordonnées de texture aux vertex du rectangle.</p>
<ul>
<li>Quand la taille rasterisé du rectangle est la même que celle de la texture (Ce qui arrive quand votre fenêtre est de la même résolution que l'image), le centre des fragments (les petites croix sur le schéma) correspond au centre des cellules des textures (les petits cercles). Ainsi, le fragment shader va sampler les images au pixel près, comme vous pouvez le voir sur le schéma de gauche.</li>
</ul>
<ul>
<li>Si la taille rasterisé du rectangle n'est pas strictement égale à la taille de la texture (99,9% des cas), chaque fragment samplera entre plusieurs cellules et le filtrage linéaire va s'assurer que nous ayons un dégradé lisse entre les textures elements (pixels), comme le montre le schéma de droite.</li>
</ul>
<center>:longBar:</center>
<h5>Allouer les textures <a name="allouer_les_textures"></a></h5>
<pre class="c c">glTexImage2D<span style="color: #009900;">(</span>
GL_TEXTURE_2D<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #808080; font-style: italic;">/* target, level of detail */</span>
GL_RGB8<span style="color: #339933;">,</span> <span style="color: #808080; font-style: italic;">/* internal format */</span>
width<span style="color: #339933;">,</span> height<span style="color: #339933;">,</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">,</span> <span style="color: #808080; font-style: italic;">/* width, height, border */</span>
GL_BGR<span style="color: #339933;">,</span> GL_UNSIGNED_BYTE<span style="color: #339933;">,</span> <span style="color: #808080; font-style: italic;">/* external format, type */</span>
pixels <span style="color: #808080; font-style: italic;">/* pixels */</span>
<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
free<span style="color: #009900;">(</span>pixels<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">return</span> texture<span style="color: #339933;">;</span>
<span style="color: #009900;">}</span></pre>
<p>La fonction glTexImage2D (ou *1D/*3D) alloue de la mémoire pour une texture.</p>
<p>Les textures peuvent avoir plusieurs <strong>niveaux de détails</strong> (Voir <a href="http://www.erenumerique.fr/ati_le_filtrage_de_texture_controverse-art-639-2.html" hreflang="fr">le mipmapping</a>) quand elles sont samplées à une faible résolution. Mais dans notre cas, nous fournirons uniquement le niveau de base: Zéro.</p>
<p>A l'inverse de glBufferData, la fonction glTexImage2D nécessite que toutes les informations de format soit défini pour allouer la mémoire.</p>
<p>Le "<strong>format interne</strong>" précise au GPU le nombre et la précision des composants de couleur à stocker par texture element.</p>
<p>OpenGL supporte toutes sortes de format d'image; Je ne ferai allusion qu'à ceux dont on va se servir.</p>
<p>Nos images TGA sont de type RGB 24bits. En d'autres mots elles contiennent trois composants (de 8bits chacun) par pixel.</p>
<p>Ce qui correspond au format interne GL_RGB8.</p>
<p>Le <strong>width</strong> et le <strong>height</strong> permettent de définir le nombre de texture elements (pixels) le long des axes S et T. (L'argument "border" n'est plus utilisé et doit toujours être à zéro).</p>
<p>Le "<strong>format externe</strong>" ainsi que le <strong>type</strong> définissent l'ordre des composants et le type de pixel fournit par l’argument "<strong>pixels</strong>" (qui lui, pointe sur une texture de taille width x height et du même format que celui donné).</p>
<p>Le format TGA stock les composants de ces pixels (de type unsigned byte) au format BGR. Donc nous utilisons GL_BGR pour le format externe et GL_UNSIGNED_BYTE pour le type des composants de l'image.</p>
<p>Maintenant que nous avons fini avec la fonction make_texture, ajoutons la à notre fonction make_resources pour créer des textures objects:</p>
<pre class="c c"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> make_resources<span style="color: #009900;">(</span><span style="color: #993333;">void</span><span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
<span style="color: #808080; font-style: italic;">/* ... make buffers */</span>
g_resources.<span style="color: #202020;">textures</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> <span style="color: #339933;">=</span> make_texture<span style="color: #009900;">(</span><span style="color: #ff0000;">"hello1.tga"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
g_resources.<span style="color: #202020;">textures</span><span style="color: #009900;">[</span><span style="color: #0000dd;">1</span><span style="color: #009900;">]</span> <span style="color: #339933;">=</span> make_texture<span style="color: #009900;">(</span><span style="color: #ff0000;">"hello2.tga"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">(</span>g_resources.<span style="color: #202020;">textures</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span> <span style="color: #339933;">||</span> g_resources.<span style="color: #202020;">textures</span><span style="color: #009900;">[</span><span style="color: #0000dd;">1</span><span style="color: #009900;">]</span> <span style="color: #339933;">==</span> <span style="color: #0000dd;">0</span><span style="color: #009900;">)</span>
<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #808080; font-style: italic;">/* make shaders ... */</span>
<span style="color: #009900;">}</span></pre>
<center>:longBar:</center>
<h5>Partie suivante: Les shaders <a name="partie_suivante_les_shaders"></a></h5>
<p>Nous avons maintenant nos vertex et images prêts à partir pour le pipeline graphique.</p>
<p>La prochaine étape de ce tutorial va être d'écrire les shaders qui manipuleront ses données au travers du GPU et les afficheront à l'écran.</p>
<p>C'est ce que nous verrons dans la prochaine partie de ce chapitre.</p>
<blockquote><p>Vous pouvez retrouver l'article original sur <a href="http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Chapter-2.1:-Buffers-and-Textures.html" hreflang="en">la page de Joe Groff</a>, une fois de plus: Merci à lui!</p></blockquote>Une introduction à l'OpenGL "Moderne" - Chapitre 2: Hello World: Le Slideshowurn:md5:fe5bc3787b579d2eba6e17db1cb5711d2010-10-23T15:34:00+02:002013-07-26T18:12:32+02:00NarannScript et codecodecompilationfrglewglutmoderneopengltraductiontuto<p><img src="https://www.fevrierdorian.com/blog/public/logos/OpenGL_logo_tn150.png" alt="OpenGL_logo_tn150.png" style="float:left; margin: 0 1em 1em 0;" title="OpenGL_logo_tn150.png, oct. 2010" height="150" width="150" />Voici la traduction du <a href="http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Chapter-2:-Hello-World:-The-Slideshow.html" hreflang="en">deuxième chapitre</a> du tutorial de <a href="http://twitter.com/jckarter" hreflang="en">Joe Groff</a>. :hehe:</p>
<p>Ici, vous allez écrire l'architecture global de votre programme, qui concerne surtout le système de fenêtrage.</p>
<p>Prenez le temps de configurer votre environnement de développement, il ne faut pas que la compilation soit une procédure rébarbative, vous allez le faire un grand nombre de fois...</p>
<p>Ce n'est pas la partie la plus intéressante mais rassurez vous, ce tutorial est assez court et vous aurez bien assez le temps de vous casser les dents sur les suivants! :baffed:</p> <h4>Une introduction à l'OpenGL "Moderne" - Chapitre 2: Hello World: Le Slideshow</h4>
<h5>Sommaire</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/23/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2-Hello-World-Le-Slideshow#les_fichiers_dentete_opengl">Les fichiers d'entete OpenGL</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/23/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2-Hello-World-Le-Slideshow#mise_en_place_de_notre_fenetre_avec_glut">Mise en place de notre fenêtre avec GLUT</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/23/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2-Hello-World-Le-Slideshow#compiler_et_lancer_notre_programme">Compiler et lancer notre programme</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/23/Une-introduction-a-l-OpenGL-Moderne-Chapitre-2-Hello-World-Le-Slideshow#chapitre_suivant">Chapitre suivant</a></li>
</ul>
<p>Mise à jour: J'ai résolu les soucis que certaines personnes ont pu rencontrer, tout particulièrement ceux concernant la compilation sur Unix et Visual C++. Ce chapitre à également <a href="http://www.reddit.com/r/programming/comments/biw8t/an_intro_to_modern_opengl_chapter_2_hello_world/" hreflang="en">sa page Reddit</a>.</p>
<center>:longBar:</center>
<p><a href="https://www.fevrierdorian.com/blog/post/2010/10/04/Une-introduction-%C3%A0-l-OpenGL-Moderne-Chapitre-1%3A-Le-Pipeline-Graphique">Dans le chapitre précédent</a>, je vous présentais un schéma <a href="https://www.fevrierdorian.com/blog/post/2010/10/04/Une-introduction-%C3%A0-l-OpenGL-Moderne-Chapitre-1%3A-Le-Pipeline-Graphique#le_pipeline_graphique">du pipeline graphique</a>. C'est l'heure de le mettre en pratique. Avant que nous essayions rendre une scène 3d, je vais faire comme dans tout bon tutorial, à savoir, une simple application "hello world" en deux dimension, afin de présenter les bases de l'API OpenGL.</p>
<p>Nous allons prendre cette image:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-hello-1.png" alt="gl2-hello-1.png" style="display:block; margin:0 auto;" title="gl2-hello-1.png, oct. 2010" height="300" width="400" /></p>
<p>Nous allons ensuite la dessiner dans une fenêtre.</p>
<p>Mais les images qui ne bougent pas, c'est un peu ennuyeux. C'est pourquoi nous allons rendre l'exercice un peu plus intéressant en faisant des fondus enchainés avec cette image:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-hello-2.png" alt="gl2-hello-2.png" style="display:block; margin:0 auto;" title="gl2-hello-2.png, oct. 2010" height="300" width="400" /></p>
<p>Ce n'est certes pas très passionnant comme programme, mais au delà de sa simplicité, celui ci nous exercera à quasiment toutes les parties d'OpenGL qu'un programme plus complexe nécessiterai.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/github-logo_128.png" alt="github-logo_128.png" style="display:block; margin:0 auto;" title="github-logo_128.png, oct. 2010" height="128" width="128" /></p>
<center><i>Octocat: La mascotte de Github</i></center>
<p>Le code source complet est en ligne sur Github, <a href="http://github.com/jckarter/hello-gl" hreflang="en">ici</a>. Composé de 380 lignes de C et de deux shaders d'une douzaine de ligne chacun, ce code (qui ne fait que dessiner une image sur l'écran) peut sembler exagéré. Cela dis, une grande partie de ce code pose les bases des démos à venir.</p>
<p>Le fichier <a href="http://github.com/jckarter/hello-gl/blob/master/hello-gl.c" hreflang="en">hello-gl.c</a> contient tout ce qui concerne le rendu OpenGL, tandis que <a href="http://github.com/jckarter/hello-gl/blob/master/util.c" hreflang="en">util.c</a> contient les fonctions qui vont nous permettre de lire des images TGA.</p>
<p>J'ai inclus les deux images, hello1.tga et hello2.tga, dans ce format, car il est facile à "décoder" sans avoir à utiliser une librairie externe.</p>
<p>Les codes des shaders sont dans deux fichiers:</p>
<ul>
<li><a href="http://github.com/jckarter/hello-gl/blob/master/hello-gl.v.glsl" hreflang="en">hello-gl.v.glsl</a> pour le vertex shader.</li>
<li><a href="http://github.com/jckarter/hello-gl/blob/master/hello-gl.f.glsl" hreflang="en">hello-gl.f.glsl</a> pour le fragment shader.</li>
</ul>
<p>Dans ce chapitre, j'expliquerai comment les différentes parties du programme hello-gl utilise l'API OpenGL pour envoyer des données dans le pipeline graphique et comment il le fait fonctionner.</p>
<p>Je donnerai également un bref aperçu du <a href="https://secure.wikimedia.org/wikipedia/en/wiki/GLSL" hreflang="en">langage GLSL</a> quand nous verront shaders.</p>
<p>C'est beaucoup de choses à faire dans un seul billet de blog, c'est pourquoi je diviserai ce chapitre en quatre parties.</p>
<p>Dans cette première partie, nous allons ouvrir une fenêtre avec <a href="http://www.opengl.org/resources/libraries/glut/" hreflang="en">GLUT</a>. Dans la seconde partie, nous allons implémenter le buffer et les textures qui contiendront respectivement les vertices bruts et les images de notre programme.</p>
<p>Après ça, nous écriront le code des shaders qui traiteront ces données pour en faire l'image final, et nous les implémenterons dans notre application OpenGL.</p>
<p>Dans le dernier article, nous parlerons des appels aux fonctions OpenGL qui servent à rendre la scène sur l'écran.</p>
<p>Maintenant que vous avez le plan en tête, commençons à coder!</p>
<p>Nous allons commencer par configurer GLUT pour avoir une fenêtre vide sur l'écran.</p>
<center>:longBar:</center>
<h5>Les fichiers d'entete OpenGL <a name="les_fichiers_dentete_opengl"></a></h5>
<pre class="c c"><span style="color: #339933;">#include <stdlib.h></span>
<span style="color: #339933;">#include <GL/glew.h></span>
<span style="color: #339933;">#ifdef __APPLE__</span>
<span style="color: #339933;"># include <GLUT/glut.h></span>
<span style="color: #339933;">#else</span>
<span style="color: #339933;"># include <GL/glut.h></span>
<span style="color: #339933;">#endif</span></pre>
<p>Chaque plateforme a ses headers OpenGL à différents endroits, mais avec GLEW, vous n'avez pas à vous soucier de ça.</p>
<p>Le fait d'inclure GL/glew.h vous permet de récupérer tout les headers OpenGL, sans que vous ayez à aller les récupérer vous même.</p>
<p>Malheureusement, ce n'est pas le cas de GLUT, et pour l'inclure, vous devez passer par quelques tricks multi-plateforme (les ifdef).</p>
<p>Son header est généralement dans GL/glut.h, mais le framework GLUT livré avec MacOS X utilise les conventions de header Apple. Ducoup, le header de GLUT est dans GLUT/glut.h.</p>
<p>Il y a également un bug dans la façon dont les versions récentes des headers C standards de Visual Studio interagissent avec glut.h, ce qui requière que stdlib.h soit déclaré avant glut.h.</p>
<center>:longBar:</center>
<h5>Mise en place de notre fenêtre avec GLUT <a name="mise_en_place_de_notre_fenetre_avec_glut"></a></h5>
<pre class="c c"><span style="color: #993333;">int</span> main<span style="color: #009900;">(</span><span style="color: #993333;">int</span> argc<span style="color: #339933;">,</span> <span style="color: #993333;">char</span><span style="color: #339933;">**</span> argv<span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
glutInit<span style="color: #009900;">(</span><span style="color: #339933;">&</span>argc<span style="color: #339933;">,</span> argv<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glutInitDisplayMode<span style="color: #009900;">(</span>GLUT_RGB <span style="color: #339933;">|</span> GLUT_DOUBLE<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glutInitWindowSize<span style="color: #009900;">(</span><span style="color: #0000dd;">400</span><span style="color: #339933;">,</span> <span style="color: #0000dd;">300</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glutCreateWindow<span style="color: #009900;">(</span><span style="color: #ff0000;">"Hello World"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glutDisplayFunc<span style="color: #009900;">(</span><span style="color: #339933;">&</span>render<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glutIdleFunc<span style="color: #009900;">(</span><span style="color: #339933;">&</span>update_fade_factor<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>GLUT fournit une interface au système de fenêtrage (window system) limité mais simple et portable.</p>
<p>Après avoir initialisé GLUT en appelant la fonction glutInit, nous utilisons glutInitDisplayMode pour spécifier les buffers dont notre framebuffer par défaut disposera.</p>
<p>Dans notre cas, un buffer de couleur (GLUT_RGB) avec double buffering (GLUT_DOUBLE) est suffisant.</p>
<p><a href="http://en.wikipedia.org/wiki/Double_buffering#Double_buffering_in_computer_graphics" hreflang="en">La technique du double buffering</a> permet de stocker deux color buffers dans le framebuffer et d'alterner à chaque frame entre le buffer affiché sur l'écran et celui qui est en court de rendu, ce qui permet d'avoir une animation fluide.</p>
<p>Si nous avons besoin d'un buffer de profondeur (depth buffer) ou d'un buffer de masque (stencil buffer), nous pouvons l'appeler ici.</p>
<p>Ensuite, nous nous servons de glutInitWindowSize pour initialiser la taille de notre fenêtre à 400x300 (la taille de nos images) et nous créons notre fenêtre avec glutCreateWindow.</p>
<p>Pour finir, nous faisons appel à deux fonctions qui "reçoivent" les events (callbacks) de la fenêtre:</p>
<ul>
<li>glutDisplayFunc qui calcul notre image quand la fenêtre lui demande de s'afficher.</li>
<li>glutIdleFunc qui met continuellement à jour le "fade factor" entre les deux images.</li>
</ul>
<pre class="c c">glewInit<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span> <span style="color: #009900;">(</span><span style="color: #339933;">!</span>GLEW_VERSION_2_0<span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
fprintf<span style="color: #009900;">(</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">"OpenGL 2.0 not available<span style="color: #000099; font-weight: bold;">\n</span>"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span></pre>
<p>Une fois que GLUT a créé notre fenêtre, nous préparons OpenGL afin de pouvoir y faire des appels.</p>
<p>La première chose que nous faisons est d'initialiser GLEW. Lorsque glewInit est appelé, il définit des appels au fonctions OpenGL en se basant sur la version ainsi que les extensions supportées par votre matériel.</p>
<p>Nous vérifions que GLEW_VERSION_2_0 est définit pour être certain que le matériel qui exécuté le programme a au moins OpenGL 2.0.</p>
<p>Une fois que la version est vérifié, l'utilisation de GLEW devient quasiment invisible, et nous n'aurons plus besoin d'interagir avec lui.</p>
<pre class="c c"><span style="color: #b1b100;">if</span> <span style="color: #009900;">(</span><span style="color: #339933;">!</span>make_resources<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #009900;">)</span> <span style="color: #009900;">{</span>
fprintf<span style="color: #009900;">(</span>stderr<span style="color: #339933;">,</span> <span style="color: #ff0000;">"Failed to load resources<span style="color: #000099; font-weight: bold;">\n</span>"</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span>
glutMainLoop<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span></pre>
<p>GLEW initialisé, nous appelons notre fonction make_resources qui set nos ressources OpenGL.</p>
<p>Nous verront cette fonction durant les prochaines parties de ce chapitre.</p>
<p>Si nos ressources sont correctement chargé, glutMainLoop prend le relais.</p>
<p>Il affiche la fenêtre, commence par recevoir les events de l'UI, et appel les fonctions que l'on a setté en réponse à ses events (les callbacks).</p>
<p>Il fermera aussi le programme pour nous quand l'utilisateur le quittera.</p>
<p>Le return 0 supprime simplement les avertissements du compilateur et il n'est, de toute façon, jamais réellement atteint.</p>
<center>:longBar:</center>
<h5>Compiler et lancer notre programme <a name="compiler_et_lancer_notre_programme"></a></h5>
<p>Arrivé ici, nous pouvons écrire le code des callbacks de GLUT, de la fonction make_resources, et faire fonctionner notre (au combien inutile) programme.</p>
<pre class="c c"><span style="color: #993333;">static</span> <span style="color: #993333;">int</span> make_resources<span style="color: #009900;">(</span><span style="color: #993333;">void</span><span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
<span style="color: #b1b100;">return</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span></pre>
<pre class="c c"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> update_fade_factor<span style="color: #009900;">(</span><span style="color: #993333;">void</span><span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
<span style="color: #009900;">}</span></pre>
<pre class="c c"><span style="color: #993333;">static</span> <span style="color: #993333;">void</span> render<span style="color: #009900;">(</span><span style="color: #993333;">void</span><span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
glClearColor<span style="color: #009900;">(</span><span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span> <span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span> <span style="color:#800080;">1.0f</span><span style="color: #339933;">,</span> <span style="color:#800080;">1.0f</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glClear<span style="color: #009900;">(</span>GL_COLOR_BUFFER_BIT<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
glutSwapBuffers<span style="color: #009900;">(</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span></pre>
<p>glClearColor set une couleur de fond <a href="https://secure.wikimedia.org/wikipedia/fr/wiki/RGBA" hreflang="fr">RGBA</a> (dans notre cas, blanche) que glClear utilise pour remplir le buffer de color du framebuffer.</p>
<p>glutSwapBuffers amène ensuite notre color buffer à l'écran.</p>
<p>Avec tout ça en place, nous pouvons maintenant compiler et lancer notre programme.</p>
<p>Cette version est dans le reposit de Github et s'appelle <a href="http://github.com/jckarter/hello-gl/blob/master/hello-gl-dummy.c" hreflang="en">hello-gl-dummy.c</a>.</p>
<p>La commande qui compile le programme et le link aux libs OpenGL, GLUT et GLEW peut varier suivant les plateformes.</p>
<p>Sur la plupart des systèmes Unix, elle devrait ressembler à ça:</p>
<pre class="bash bash"><span style="color: #c20cb9; font-weight: bold;">gcc</span> <span style="color: #660033;">-o</span> hello-gl-dummy hello-gl-dummy.c \
-I<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>X11R6<span style="color: #000000; font-weight: bold;">/</span>include -L<span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>X11R6<span style="color: #000000; font-weight: bold;">/</span>lib \
<span style="color: #660033;">-lGL</span> <span style="color: #660033;">-lGLEW</span> <span style="color: #660033;">-lglut</span></pre>
<p>Sur MacOS X:</p>
<pre class="bash bash"><span style="color: #666666; font-style: italic;"># Assuming GLEW was installed to /opt/local</span>
<span style="color: #c20cb9; font-weight: bold;">gcc</span> <span style="color: #660033;">-o</span> hello-gl-dummy hello-gl-dummy.c \
-I<span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>local<span style="color: #000000; font-weight: bold;">/</span>include -L<span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>local<span style="color: #000000; font-weight: bold;">/</span>lib \
<span style="color: #660033;">-framework</span> OpenGL <span style="color: #660033;">-framework</span> GLUT <span style="color: #660033;">-lGLEW</span></pre>
<p>Sur Windows avec Visual C++:</p>
<pre class="bash bash">cl <span style="color: #000000; font-weight: bold;">/</span>Fohello-gl-dummy.obj <span style="color: #000000; font-weight: bold;">/</span>c hello-gl-dummy.c
<span style="color: #c20cb9; font-weight: bold;">link</span> <span style="color: #000000; font-weight: bold;">/</span>out:hello-gl-dummy.exe hello-gl-dummy.obj \
opengl32.lib glut32.lib glew32.lib</pre>
<p>Sur Windows avec mingw:</p>
<pre class="bash bash"><span style="color: #c20cb9; font-weight: bold;">gcc</span> <span style="color: #660033;">-o</span> hello-gl-dummy.exe hello-gl-dummy.c \
<span style="color: #660033;">-lopengl32</span> <span style="color: #660033;">-lglut32</span> <span style="color: #660033;">-lglew32</span></pre>
<p>Le reposit contient également les makefiles de chacune de ses plateformes.</p>
<p>Vous pouvez compiler cette version du programme en utilisant hello-gl-dummy (ou hello-gl-dummy.exe sur Windows):</p>
<pre class="bash bash"><span style="color: #c20cb9; font-weight: bold;">make</span> <span style="color: #660033;">-f</span> Makefile.MacOSX hello-gl-dummy <span style="color: #666666; font-style: italic;"># or Makefile.Unix or Makefile.Mingw</span></pre>
<pre class="bash bash">nmake <span style="color: #000000; font-weight: bold;">/</span>f Nmakefile.Windows hello-gl-dummy.exe</pre>
<p>Une fois que vous avez compilé le programme, vous devriez être capable de le lancer et d'avoir une fenetre blanche, comme promis:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_02_OpenGL_Modern_Translate/gl2-dummy-screenshot.png" alt="gl2-dummy-screenshot.png" style="display:block; margin:0 auto;" title="gl2-dummy-screenshot.png, oct. 2010" height="334" width="412" /></p>
<p>Fermez la fenêtre, ou, sur MacOS X, quittez l'application.</p>
<center>:longBar:</center>
<h5>Chapitre suivant <a name="chapitre_suivant"></a></h5>
<p>Avec la satisfaction d'avoir une fenêtre ouverte devant nous, nous somme prêt à remplir OpenGL de vertex et d'images.</p>
<p>Dans le prochain article, j'introduirais les buffers et textures OpenGL.</p>
<blockquote><p>Vous pouvez retrouver l'article original sur <a href="http://duriansoftware.com/joe/An-intro-to-modern-OpenGL.-Chapter-2:-Hello-World:-The-Slideshow.html" hreflang="en">la page de Joe Groff</a>, un fois de plus: Merci à lui!</p></blockquote>CgFX - Des shaders temps réel dans le viewport Maya! - Part 4urn:md5:588d7eba85e3ca949923cd8a212b312f2010-08-04T23:49:00+02:002013-07-26T18:13:44+02:00NarannScript et code3dcgfxcodefrmayashadertexturetuto<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_part4_tn.png" alt="cgfx_part4_tn.png" style="float:left; margin: 0 1em 1em 0;" title="cgfx_part4_tn.png, août 2010" height="150" width="150" />Si vous êtes arrivés jusque là, vous devez surement vous dire: "Qu'est ce que c'est que ce tuto de shader de merde ou l'on vous fait bouffer trois parties sans voir une seule texture?".</p>
<p>Rassurez-vous, vos efforts vont être récompensés! Nous allons attaquer les textures! :sourit:</p>
<p>Vous allez voir que leur mise en place est relativement simple. Cette partie sera moins longue que les autres car je préfère l'aborder dans un seul billet et laisser au prochain, un seul sujet, le bump!</p> <center>:longBar:</center>
<h4>Ajout d'une texture <a name="ajout_dune_texture"></a></h4>
<p>Vous l'attendiez? Vous allez voir que ce n'est pas compliqué! :baffed:</p>
<center>:longBar:</center>
<h5>Sommaire:</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/04/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-4#les_attributs">Les attributs</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/04/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-4#maj_des_structures">Mise à jour des structures</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/04/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-4#les_uv_vertex_shader_et_pixel_shader">Les UV: Vertex Shader et Pixel Shader</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/04/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-4#switch_entre_la_diffuse_color_et_la_texture">Switch entre la diffuse color et la texture</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/08/04/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-4#conclusion">Conclusion</a></li>
</ul>
<center>:longBar:</center>
<h5>Les attributs <a name="les_attributs"></a></h5>
<p>Comme d'habitude, il va falloir déclarer notre texture. Cette déclaration se fait en deux temps. Voici le code:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">// Textures</span>
texture diffuseTexture
<span style="color: #339933;"><</span>
string ResourceName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"default_color.dds"</span><span style="color: #339933;">;</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Diffuse Texture"</span><span style="color: #339933;">;</span>
string ResourceType <span style="color: #339933;">=</span> <span style="color: #ff0000;">"2D"</span><span style="color: #339933;">;</span>
<span style="color: #339933;">>;</span>
sampler2D diffuseSampler <span style="color: #339933;">=</span> sampler_state
<span style="color: #009900;">{</span>
Texture <span style="color: #339933;">=</span> <span style="color: #339933;"><</span>diffuseTexture<span style="color: #339933;">>;</span>
MinFilter <span style="color: #339933;">=</span> LinearMipMapLinear<span style="color: #339933;">;</span>
MagFilter <span style="color: #339933;">=</span> Linear<span style="color: #339933;">;</span>
WrapS <span style="color: #339933;">=</span> Repeat<span style="color: #339933;">;</span>
WrapT <span style="color: #339933;">=</span> Repeat<span style="color: #339933;">;</span>
MaxAnisotropy <span style="color: #339933;">=</span> <span style="color:#800080;">16.0</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<p>Vous constaterez dans un premier temps que nous créons un attribut "texture". Si vous avez suivie ce tuto dans l'ordre, vous devriez déjà être en possession de cette texture, elle était donnée dans le shader d'exemple <a href="https://www.fevrierdorian.com/blog/post/2010/04/26/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-part-1">téléchargé en partie 1</a>.</p>
<p>L'attribut suivant est de type sample2D. C'est ici que nous allons définir tous les paramètres "techniques" de notre texture.</p>
<p>Le premier paramètre:</p>
<pre class="c c">Texture <span style="color: #339933;">=</span> <span style="color: #339933;"><</span>diffuseTexture<span style="color: #339933;">>;</span></pre>
<p>Est un lien vers notre "objet" texture créé plus haut.</p>
<pre class="c c">MinFilter <span style="color: #339933;">=</span> LinearMipMapLinear<span style="color: #339933;">;</span></pre>
<p>Cela applique un filtre linéaire sur votre texture, quand les pixels de la texture sont plus petits que les pixels de votre écran (texture loin), et force à utiliser le mipmap. Et l'inverse:</p>
<pre class="c c">MagFilter <span style="color: #339933;">=</span> Linear<span style="color: #339933;">;</span></pre>
<p>Cela applique un filtre linéaire sur votre texture, quand les pixels de la texture sont plus grands que les pixels de votre écran (texture proche).</p>
<pre class="c c">WrapS <span style="color: #339933;">=</span> Repeat<span style="color: #339933;">;</span>
WrapT <span style="color: #339933;">=</span> Repeat<span style="color: #339933;">;</span></pre>
<p>Si vous faite de la 3d, vous devez savoir à quoi ça correspond: C'est le comportement des textures quand on arrive au bout.</p>
<pre class="c c">MaxAnisotropy <span style="color: #339933;">=</span> <span style="color:#800080;">16.0</span><span style="color: #339933;">;</span></pre>
<p>Héhé! Ici on demande à la carte graphique d'utiliser le <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Anisotropic_filtering" hreflang="en">filtrage anisotropique</a> au lieu du filtrage linéaire, pour la texture. Le résultat est qu'il y a plus que piquet dans vos textures quand elles sont loin. Elles paraissent plus nettes. Vu que j'ai une grosse <del>bite</del> carte graphique pardon, je peux mettre à 16. Renseignez-vous sur la valeur maximum que supporte votre carte graphique. Sinon ça risque de ne pas marcher. :seSentCon:</p>
<center>:longBar:</center>
<h5>Mise à jour des structures <a name="maj_des_structures"></a></h5>
<p>L'étape suivante, avant d'aller utiliser nos textures, c'est de définir les arguments de notre structure en entrée et en sortie (on appele ça les varying, les "variant" en français). On définit ce que "transporte" les vertex:</p>
<pre class="c c"><span style="color: #993333;">struct</span> vIN
<span style="color: #009900;">{</span>
float3 Position <span style="color: #339933;">:</span> POSITION<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// La position du vertex en entrée</span>
float4 Normal <span style="color: #339933;">:</span> NORMAL<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// La normal du vertex en entrée</span>
float4 UV <span style="color: #339933;">:</span> TEXCOORD0<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// un set d'uv</span>
<span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<p>Et un set d'UV ajouté à nos vertex!</p>
<p>Rappelez-vous, c'est le vertex shader qui prend les varyings de vIN, les modifies (ou non) en créant vOUT, et c'est le Pixel Shader qui prend vOUT pour s'en servir. :zinzin:</p>
<pre class="c c"><span style="color: #993333;">struct</span> vOUT
<span style="color: #009900;">{</span>
float4 Position <span style="color: #339933;">:</span> POSITION<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// La position du vertex en sortie (doit être de type float4)</span>
float3 PointLight0Vec <span style="color: #339933;">:</span> TEXCOORD0<span style="color: #339933;">;</span>
float3 PointLight1Vec <span style="color: #339933;">:</span> TEXCOORD1<span style="color: #339933;">;</span>
float3 WorldNormal <span style="color: #339933;">:</span> TEXCOORD2<span style="color: #339933;">;</span>
float3 WorldView <span style="color: #339933;">:</span> TEXCOORD3<span style="color: #339933;">;</span>
float2 UV <span style="color: #339933;">:</span> TEXCOORD4<span style="color: #339933;">;</span>
<span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<p>Voila! Maintenant, nos vertex et pixels sont prêt à accueillir nos UVs.</p>
<center>:longBar:</center>
<h5>Les UVs: Vertex Shader et Pixel Shader <a name="les_uv_vertex_shader_et_pixel_shader"></a></h5>
<p>L'implémentation dans le Vertex Shader est d'une facilité déconcertante:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">// On copie les UV</span>
OUT.<span style="color: #202020;">UV</span> <span style="color: #339933;">=</span> IN.<span style="color: #202020;">UV</span>.<span style="color: #202020;">xy</span><span style="color: #339933;">;</span></pre>
<p>Trop dur... :siffle:</p>
<p>Du coup, je ne prends même pas la peine de vous expliquer tellement c'est simple...</p>
<p>Collez ça au dessus du "return OUT" et c'est fait!</p>
<p>Passons au Pixel Shader:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Calcul la valeur de la couleur de la Diffuse</span>
float3 diffuseColorResult<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">//diffuseColorResult = diffuseColor.rgb; // On commente cette ligne pour ne récupérer que la couleur de la texture.</span>
diffuseColorResult <span style="color: #339933;">=</span> tex2D<span style="color: #009900;">(</span>diffuseSampler<span style="color: #339933;">,</span> IN.<span style="color: #202020;">UV</span><span style="color: #009900;">)</span>.<span style="color: #202020;">rgb</span><span style="color: #339933;">;</span>
diffuseColorResult <span style="color: #339933;">*=</span> diffuseFactor<span style="color: #339933;">;</span></pre>
<p>Ici point de diffuse, on commente la ligne qui récupère les valeurs RGB de la couleur (ne la supprimez pas, on va y revenir), puis on demande au shader de récupérer la couleur du sampler2D, et donc, de notre texture...</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_part4_001.png" alt="cgfx_part4_001.png" style="display:block; margin:0 auto;" title="cgfx_part4_001.png, août 2010" height="362" width="433" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_part4_002.png" alt="cgfx_part4_002.png" style="display:block; margin:0 auto;" title="cgfx_part4_002.png, août 2010" height="327" width="349" /></p>
<center><i>Et voila!</i></center>
<p>Faites mumuse avec les paramètres, histoire de voir comment tout ça fonctionne.</p>
<p>Vous pouvez télécharger le shader ici: <a href="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_tuto_006.7z">->cgfx_tuto_006.7z<-</a></p>
<center>:longBar:</center>
<h5>Switch entre la diffuse color et la texture <a name="switch_entre_la_diffuse_color_et_la_texture"></a></h5>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/on-off-switch.jpg" alt="on-off-switch.jpg" style="display:block; margin:0 auto;" title="on-off-switch.jpg, août 2010" height="300" width="300" /></p>
<p>Bon, c'est cool cette histoire mais si on veut pouvoir switcher entre la couleur ou la texture, on l'a dans l'os...</p>
<p>On va donc faire un petit switch (une checkbox on/off) pour changer ça. Retournez au niveau des paramètres et ajoutez ça:</p>
<pre class="c c">bool bUseDiffuseTexture
<span style="color: #339933;"><</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Use Diffuse Texture"</span><span style="color: #339933;">;</span>
string UIWidget <span style="color: #339933;">=</span> <span style="color: #ff0000;">"RadioButton"</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">true</span><span style="color: #339933;">;</span></pre>
<p>Personnellement, j'ai calé ça dans la diffuse. :redface:</p>
<p>Maintenant allez directement dans le Pixel Shader et transformez les lignes de toute à l'heure:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Calcul la valeur de la couleur de la Diffuse</span>
float3 diffuseColorResult<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">//diffuseColorResult = diffuseColor.rgb;</span>
diffuseColorResult <span style="color: #339933;">=</span> tex2D<span style="color: #009900;">(</span>diffuseSampler<span style="color: #339933;">,</span> IN.<span style="color: #202020;">UV</span><span style="color: #009900;">)</span>.<span style="color: #202020;">rgb</span><span style="color: #339933;">;</span>
diffuseColorResult <span style="color: #339933;">*=</span> diffuseFactor<span style="color: #339933;">;</span></pre>
<p>Par ça:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Calcul la valeur de la couleur de la Diffuse</span>
float3 diffuseColorResult<span style="color: #339933;">;</span>
<span style="color: #b1b100;">if</span><span style="color: #009900;">(</span>bUseDiffuseTexture <span style="color: #339933;">==</span> <span style="color: #000000; font-weight: bold;">true</span><span style="color: #009900;">)</span>
<span style="color: #009900;">{</span>
diffuseColorResult <span style="color: #339933;">=</span> tex2D<span style="color: #009900;">(</span>diffuseSampler<span style="color: #339933;">,</span> IN.<span style="color: #202020;">UV</span><span style="color: #009900;">)</span>.<span style="color: #202020;">rgb</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span>
<span style="color: #b1b100;">else</span>
<span style="color: #009900;">{</span>
diffuseColorResult <span style="color: #339933;">=</span> diffuseColor.<span style="color: #202020;">rgb</span><span style="color: #339933;">;</span>
<span style="color: #009900;">}</span>
diffuseColorResult <span style="color: #339933;">*=</span> diffuseFactor<span style="color: #339933;">;</span></pre>
<p>Vous l'aurez compris, on fait un simple "if" pour savoir si oui ou non on n'utilise la texture.</p>
<blockquote><p>Dans <a href="https://www.fevrierdorian.com/blog/post/2010/05/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-2#c117">un commentaire</a>, Glim me faisait remarquer qu'utiliser des conditions dans son shader était mauvais en terme de performance. Après renseignement, il s'avère en effet exact que l'utilisation des conditions est à proscrire, mais uniquement si elles sont utilisées sur des "varyings" (vIN, vOUT), ce qui n'est pas le cas ici car elles sont utilisé sur des "uniforms" (des valeurs qui ne changent pas durant la durée du rendu).</p></blockquote>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_part4_003.png" alt="cgfx_part4_003.png" style="display:block; margin:0 auto;" title="cgfx_part4_003.png, août 2010" height="53" width="214" /></p>
<center><i>Et voila!</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_part4_004.png" alt="cgfx_part4_004.png" style="display:block; margin:0 auto;" title="cgfx_part4_004.png, août 2010" height="253" width="628" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_part4_005.png" alt="cgfx_part4_005.png" style="display:block; margin:0 auto;" title="cgfx_part4_005.png, août 2010" height="267" width="590" /></p>
<p>Vous pouvez télécharger le shader ici <a href="https://www.fevrierdorian.com/blog/public/billets/2010_08_04_CgFX_part4/cgfx_tuto_007.7z">->cgfx_tuto_007.7z<-</a></p>
<center>:longBar:</center>
<h5>Conclusion <a name="conclusion"></a></h5>
<p>La partie concernant les textures est terminée. Vous voyez? Elle n'était pas compliquée! J'espère que vous êtes un peu moins frustrés maintenant. :hihi:</p>
<p>N'hésitez pas à faire des variations de votre shader! Il n'y a que comme ça que vous comprendrez et vous amuserez (j'espère... ^^ )</p>
<p>Si je n'ai pas été clair quelque part, laissez un commentaire et je corrigerai!</p>
<p>Le prochain tuto concernera le bump mapping. Vous allez voir que c'est beaucoup plus costaud!</p>
<p>A bientôt!</p>
<p>Dorian</p>
<center><i>:marioCours:</i></center>
CgFX - Des shaders temps réel dans le viewport Maya! - Part 3urn:md5:e67a1c9a9eb7217d84d08fdad438beef2010-07-27T20:50:00+02:002013-07-26T18:13:51+02:00NarannScript et code3dcgfxcodefrmayashadertuto<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/shaderCgFX_Tuto_Part3_tn.png" alt="shaderCgFX_Tuto_Part3_tn.png" style="float:left; margin: 0 1em 1em 0;" title="shaderCgFX_Tuto_Part3_tn.png, juil. 2010" height="150" width="150" />Après pas mal de retard (boulot, etc...), je vous propose de découvrir la troisième partie de ma série de tutos sur le langage CgFX.</p>
<p>Ici nous allons ajouter une couleur de base à notre shader (la diffuse), de l'ambiant, ainsi qu'un effet très intéressant pour déboucher les bords: Le falloff. Avec cet effet, vous pourrez déjà donner un look sympa à vos playblasts alors n'hésitez pas à expérimenter. Je vous invite à partager vos liens et créations dans les commentaires. :youplaBoum:</p> <center>:longBar:</center>
<h5>Sommaire:</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#ajout_de_la_diffuse">Ajout de la diffuse</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#ajout_de_lambiant">Ajout de l'ambiant</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#ajout_du_falloff">Ajout du falloff</a>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#ajout_de_la_matrice_et_modification_de_la_strucure">Ajout de la matrice et modification de la strucure</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#modification_du_vexter_shader">Modification du Vexter Shader</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#modification_du_pixel_shader">Modification du Pixel Shader</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#ajout_des_attributs">Ajout des attributs</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#retour_au_pixel_shader">Retour au Pixel Shader</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/07/27/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-3#dilemme_de_lambiant_et_du_falloff">Dilemme de l'ambiant et du falloff</a></li>
</ul></li>
</ul>
<center>:longBar:</center>
<h4>Ajout de la diffuse <a name="ajout_de_la_diffuse"></a></h4>
<p>Actuellement, le shader n'a aucune couleur. Il se contente d'additionner les couleurs des deux pointLights (en fait, il se comporte comme si il était blanc). :reflechi:</p>
<p>Pour ajouter une couleur de base (que j'appellerai "diffuse"), il faut déjà ajouter les paramètres de diffusion (Ici, une couleur et un coefficient):</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">// Diffuse</span>
float3 diffuseColor
<span style="color: #339933;"><</span>
string type <span style="color: #339933;">=</span> <span style="color: #ff0000;">"color"</span><span style="color: #339933;">;</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Diffuse Color"</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color: #009900;">{</span><span style="color:#800080;">0.07</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.07</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.07</span><span style="color: #009900;">}</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> diffuseFactor
<span style="color: #339933;"><</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Diffuse Factor"</span><span style="color: #339933;">;</span>
string UIWidget <span style="color: #339933;">=</span> <span style="color: #ff0000;">"slider"</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMin <span style="color: #339933;">=</span> <span style="color:#800080;">0.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMax <span style="color: #339933;">=</span> <span style="color:#800080;">1.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIStep <span style="color: #339933;">=</span> <span style="color:#800080;">0.001</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_001.png" alt="cgfx_part3_001.png" style="display:block; margin:0 auto;" title="cgfx_part3_001.png, juil. 2010" height="84" width="394" /></p>
<p>Passons au calcul de la diffuse. C'est assez simple. Dans le "program" du Pixel Shader (MainPS()):</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Calcul la valeur de la couleur de la Diffuse</span>
float3 diffuseColorResult <span style="color: #339933;">=</span> diffuseColor.<span style="color: #202020;">rgb</span><span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// On la rend de la couleur de nos paramètres</span>
diffuseColorResult <span style="color: #339933;">*=</span> diffuseFactor<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Et on multiplie le tout par le coefficient</span></pre>
<p>Maintenant que nous avons calculé la valeur de diffuseColorResult, allons à la ligne de résultat:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> illumPointLight0 <span style="color: #339933;">+</span> illumPointLight1<span style="color: #339933;">;</span></pre>
<p>Et multiplions le tout par notre diffuseColorResult:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> <span style="color: #009900;">(</span>illumPointLight0 <span style="color: #339933;">+</span> illumPointLight1<span style="color: #009900;">)</span> <span style="color: #339933;">*</span> diffuseColorResult<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_002.png" alt="cgfx_part3_002.png" style="display:block; margin:0 auto;" title="cgfx_part3_002.png, juil. 2010" height="278" width="443" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_003.png" alt="cgfx_part3_003.png" style="display:block; margin:0 auto;" title="cgfx_part3_003.png, juil. 2010" height="180" width="216" /></p>
<p>Voila, c'est un peu "bizarre" l'effet (car parfait) mais ne vous inquiétez pas, quand on passera aux textures, ça sera tout de suite plus joli! ^^</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_tuto_004.7z">cgfx_tuto_004.7z</a></p>
<p>Maintenant, ajoutons de l'ambiant.</p>
<center>:longBar:</center>
<h4>Ajout de l'ambiant <a name="ajout_de_lambiant"></a></h4>
<p>Le principe de l'ambiant, c'est d'ajouter de l'illumination. C'est donc une addition mais uniquement sur l'illumination, pas sur le résultat total.</p>
<p>Si on "ajoutait" au résultat total, ça serait l'équivalent de l'incandescence dans Maya (ou Additional Color dans un shader mia_x). L'effet ne serait pas du tout le même!</p>
<p>C'est tellement simple que vous pourriez le faire vous-même:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">// Ambiant</span>
float3 ambiantColor <span style="color: #666666; font-style: italic;">// On créé l'attribut</span>
<span style="color: #339933;"><</span>
string type <span style="color: #339933;">=</span> <span style="color: #ff0000;">"color"</span><span style="color: #339933;">;</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Ambient Color"</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color: #009900;">{</span><span style="color:#800080;">0.07</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.07</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.07</span><span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<pre class="c c">float3 result <span style="color: #339933;">=</span> <span style="color: #009900;">(</span>illumPointLight0 <span style="color: #339933;">+</span> illumPointLight1 <span style="color: #339933;">+</span> ambiantColor<span style="color: #009900;">)</span> <span style="color: #339933;">*</span> diffuseColorResult<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_004.png" alt="cgfx_part3_004.png" style="display:block; margin:0 auto;" title="cgfx_part3_004.png, juil. 2010" height="46" width="368" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_005.png" alt="cgfx_part3_005.png" style="display:block; margin:0 auto;" title="cgfx_part3_005.png, juil. 2010" height="144" width="142" /></p>
<p>Encore une fois, si vous avez des doutes, essayez par vous même d'ajouter l'ambiant au total.. ^^ (Une modif, un save, un reload et on a le résultat direct! Profitez-en pour expérimenter!)</p>
<p>Vous verrez de suite la subtilité. :sourit:</p>
<center>:longBar:</center>
<h4>Ajout du falloff <a name="ajout_du_falloff"></a></h4>
<p>Maintenant nous allons attaquer un effet que je trouve très intéressant (et qui est particulièrement à la mode dans le jeux vidéo): Le falloff</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/mario-galaxy-hd.jpg" alt="mario-galaxy-hd.jpg" style="display:block; margin:0 auto;" title="mario-galaxy-hd.jpg, juil. 2010" height="332" width="580" /></p>
<center><i>It's meee! Mario! :marioCours:</i></center>
<p>Que serait une intro sans une image... (tiré de la galerie du thread: <a href="http://www.neogaf.com/forum/showthread.php?p=16546261#post16546261" hreflang="en">Wii emulator can do 720p HD</a>)</p>
<p>Voyez l'effet sur la casquette et le pantalon, ça débouche les contours, et bien c'est ce que nous allons faire! :sourit:</p>
<center>:longBar:</center>
<h5>Ajout de la matrice et modification de la strucure <a name="ajout_de_la_matrice_et_modification_de_la_strucure"></a></h5>
<p>Et c'est maintenant que ça ce complique. Vous vous rappelez comment on à fait notre illumination à partir des deux pointLights? Et bien grossièrement ici, c'est la même chose, sauf que notre pointLight, c'est la "view", la camera... On éclaire par la caméra, mais en négatif... :zinzin:</p>
<p>Nous allons dans un premier temps, ajouter, tout en haut du fichier, une matrice appelée ViewInverse. (Je ne suis pas trop calé sur les différentes matrices de CgFX. Je ne sais donc pas ce qu'elles représentent mathématiquement. Cela dit, j'ai trouvé une doc intéressante sur les matrices utilisées en temps réel: <a href="http://code.google.com/intl/fr-FR/apis/o3d/docs/shadinglanguage.html" title="http://code.google.com/intl/fr-FR/apis/o3d/docs/shadinglanguage.html">http://code.google.com/intl/fr-FR/a...</a> Cette doc n'est pas spécifique au langage CgFX.):</p>
<pre class="c c">float4x4 viMatrix <span style="color: #339933;">:</span> ViewInverse<span style="color: #339933;">;</span></pre>
<p>Puis on ajoute un paramètre "WorldView" à notre structure vOUT:</p>
<pre class="c c"><span style="color: #993333;">struct</span> vOUT
<span style="color: #009900;">{</span>
float4 Position <span style="color: #339933;">:</span> POSITION<span style="color: #339933;">;</span>
float3 PointLight0Vec <span style="color: #339933;">:</span> TEXCOORD0<span style="color: #339933;">;</span>
float3 PointLight1Vec <span style="color: #339933;">:</span> TEXCOORD1<span style="color: #339933;">;</span>
float3 WorldNormal <span style="color: #339933;">:</span> TEXCOORD2<span style="color: #339933;">;</span>
float3 WorldView <span style="color: #339933;">:</span> TEXCOORD3<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// ajout d'une variable à la structure</span>
<span style="color: #009900;">}</span><span style="color: #339933;">;</span></pre>
<center>:longBar:</center>
<h5>Modification du Vexter Shader <a name="modification_du_vexter_shader"></a></h5>
<p>Là c'est bcp moins classe: Dans MainVS(), on "copie-colle" le morceau de code que j'ai moi même copié-collé d'un des shaders de NVidia... :nevroz:</p>
<pre class="c c">OUT.<span style="color: #202020;">WorldView</span> <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>float3<span style="color: #009900;">(</span>viMatrix<span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span>.<span style="color: #202020;">w</span><span style="color: #339933;">,</span>viMatrix<span style="color: #009900;">[</span><span style="color: #0000dd;">1</span><span style="color: #009900;">]</span>.<span style="color: #202020;">w</span><span style="color: #339933;">,</span>viMatrix<span style="color: #009900;">[</span><span style="color: #0000dd;">2</span><span style="color: #009900;">]</span>.<span style="color: #202020;">w</span><span style="color: #009900;">)</span> <span style="color: #339933;">-</span> PositionWorld.<span style="color: #202020;">xyz</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>Je suis incapable d'expliquer ce que ça fait ormis que ça remplit votre "OUT.WorldView" bien comme il faut! :sourit:</p>
<p>Notre WorldView est maintenant remplit par le Vertex Shader: MainVS().</p>
<center>:longBar:</center>
<h5>Modification du Pixel Shader <a name="modification_du_pixel_shader"></a></h5>
<p>Passons a MainPS(), on normalise notre valeur (comme les autres):</p>
<pre class="c c">float3 Vn <span style="color: #339933;">=</span> normalize<span style="color: #009900;">(</span>IN.<span style="color: #202020;">WorldView</span><span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>Et on s'en sert pour générer le falloff:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//FallOff</span>
<span style="color: #993333;">float</span> ndv <span style="color: #339933;">=</span> dot<span style="color: #009900;">(</span>Nn<span style="color: #339933;">,</span>Vn<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<center><i>dot()... La fonction magique... :hehe:</i></center>
<p>A ce stade vous pouvez essayer de n'afficher "que" la valeur de ndv dans votre shader pour voir le résultat:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> ndv<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_006.png" alt="cgfx_part3_006.png" style="display:block; margin:0 auto;" title="cgfx_part3_006.png, juil. 2010" height="158" width="161" /></p>
<p>On y est presque on dirait! :sourit:</p>
<center>:longBar:</center>
<h5>Ajout des attributs <a name="ajout_des_attributs"></a></h5>
<p>On va ajouter deux paramètres:</p>
<ul>
<li>Un pour contrôler la visibilité (opacité) du falloff (Un float de 0 à 1)</li>
<li>L'autre pour contrôler "sa compression"</li>
</ul>
<p>Le tout, calé en dessous de l'ambiant. Ce qui donne, pour les attributs:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">// FallOff</span>
<span style="color: #993333;">float</span> fallOffCompression
<span style="color: #339933;"><</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"FallOff Compression"</span><span style="color: #339933;">;</span>
string UIWidget <span style="color: #339933;">=</span> <span style="color: #ff0000;">"slider"</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMin <span style="color: #339933;">=</span> <span style="color:#800080;">0.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMax <span style="color: #339933;">=</span> <span style="color:#800080;">10.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIStep <span style="color: #339933;">=</span> <span style="color:#800080;">0.001</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color:#800080;">1.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> ambiantFallOffFactor
<span style="color: #339933;"><</span>
string UIName <span style="color: #339933;">=</span> <span style="color: #ff0000;">"Ambient FallOff Factor"</span><span style="color: #339933;">;</span>
string UIWidget <span style="color: #339933;">=</span> <span style="color: #ff0000;">"slider"</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMin <span style="color: #339933;">=</span> <span style="color:#800080;">0.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIMax <span style="color: #339933;">=</span> <span style="color:#800080;">1.0</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> UIStep <span style="color: #339933;">=</span> <span style="color:#800080;">0.001</span><span style="color: #339933;">;</span>
<span style="color: #339933;">></span> <span style="color: #339933;">=</span> <span style="color: #0000dd;">1</span><span style="color: #339933;">;</span></pre>
<p>Voyez que j'ai appelé le coefficient: "Ambient FallOff Factor". Vous verrez la relation entre l'Ambiant et le Falloff plus tard...</p>
<center>:longBar:</center>
<h5>Retour au Pixel Shader <a name="retour_au_pixel_shader"></a></h5>
<p>Maintenant retournons dans notre program MainPS() et on ajoute:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//FallOff</span>
<span style="color: #993333;">float</span> ndv <span style="color: #339933;">=</span> dot<span style="color: #009900;">(</span>Nn<span style="color: #339933;">,</span>Vn<span style="color: #009900;">)</span><span style="color: #339933;">;</span>
<span style="color: #993333;">float</span> fallOffRamp <span style="color: #339933;">=</span> pow<span style="color: #009900;">(</span>ndv<span style="color: #339933;">,</span> fallOffCompression<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>En fait c'est comme si on faisait ndv^fallOffCompression: Si votre fallOffCompression = 2, alors nous auront ndv au carré!</p>
<p>C'est ce fallOffRamp qui va être multiplié au ambiantFallOffFactor. Je vous propose, pour bien voir le résultat de le faire de suite:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> fallOffRamp <span style="color: #339933;">*</span> ambiantFallOffFactor<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_007.png" alt="cgfx_part3_007.png" style="display:block; margin:0 auto;" title="cgfx_part3_007.png, juil. 2010" height="61" width="392" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_008.png" alt="cgfx_part3_008.png" style="display:block; margin:0 auto;" title="cgfx_part3_008.png, juil. 2010" height="141" width="162" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_009.png" alt="cgfx_part3_009.png" style="display:block; margin:0 auto;" title="cgfx_part3_009.png, juil. 2010" height="61" width="391" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_010.png" alt="cgfx_part3_010.png" style="display:block; margin:0 auto;" title="cgfx_part3_010.png, juil. 2010" height="144" width="166" /></p>
<p>Amusez vous avec les paramètres, change le "result", bidouillez! Il n'y a que comme ça que je suis arrivé et je vous invite à le faire!</p>
<p>Pour inverser la couleur (pour avoir du blanc sur les contours):</p>
<pre class="c c"><span style="color: #993333;">float</span> fallOffRamp <span style="color: #339933;">=</span> pow<span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">-</span>ndv<span style="color: #339933;">,</span> fallOffCompression<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_011.png" alt="cgfx_part3_011.png" style="display:block; margin:0 auto;" title="cgfx_part3_011.png, juil. 2010" height="146" width="191" /></p>
<center>:longBar:</center>
<h5>Dilemme de l'ambiant et du falloff <a name="dilemme_de_lambiant_et_du_falloff"></a></h5>
<p>A ce stade nous avons, un falloff dont nous contrôlons "la compression" et l'opacité. L'idée est que ce falloff agisse de la même façon que l'ambiant, à savoir, comme un ajout à l'illumination.</p>
<p>Simple me diriez vous. Il suffit de faire ça:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> <span style="color: #009900;">(</span>illumPointLight0 <span style="color: #339933;">+</span> illumPointLight1 <span style="color: #339933;">+</span> fallOffRamp <span style="color: #339933;">*</span> ambiantFallOffFactor<span style="color: #009900;">)</span> <span style="color: #339933;">*</span> diffuseColorResult<span style="color: #339933;">;</span> <span style="color: #666666; font-style: italic;">// Vous n'oubliez pas quelque chose?</span></pre>
<p>Oui... Et ambiantColor on en fait quoi? :sourit:</p>
<p>Je m'explique: Il nous faut s'arranger pour que les valeurs d'ambiant et de falloff ne fassent plus qu'une. Mais essayez de visualisez ce que nous avons:</p>
<ul>
<li>Une ambiant (ambiantColor), qui est donc juste une couleur.</li>
<li>Un falloff (fallOffRamp * ambiantFallOffFactor), dont vous voyez très bien à quoi ça ressemble.</li>
</ul>
<p>Si nous additionnons bêtement les valeurs, cela donnera quelque chose d'un peu chiant à régler.</p>
<p>Essayez (comme toujours...):</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> ambiantColor <span style="color: #339933;">+</span> fallOffRamp <span style="color: #339933;">*</span> ambiantFallOffFactor<span style="color: #339933;">;</span></pre>
<p>Et jouez avec les paramètres. Vous allez vite vous rendre compte du problème. :reflechi:</p>
<p>De même qui si nous les multiplions, l'ambiant jouera le rôle d'un "simple" coefficient:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> ambiantColor <span style="color: #339933;">*</span> fallOffRamp <span style="color: #339933;">*</span> ambiantFallOffFactor<span style="color: #339933;">;</span></pre>
<p>Ici, ambiantColor et ambiantFallOffFactor jouent le même role au final... Donc inutile.</p>
<p>Encore une fois, petite prise de tête en perspective... :siffle:</p>
<p>Simplifions un peu: Ce qui serait bien, ça serait que ambiantFallOffFactor soit une sort de "switch" entre l'ambiantColor color et le fallOffRamp:</p>
<ul>
<li>Si ambiantFallOffFactor = 0, alors c'est l'ambiantColor qui à le dessus (ambiantColor*1 et fallOffRamp*0).</li>
<li>Si ambiantFallOffFactor = 1, alors c'est la fallOffRamp qui est à fond (ambiantColor*0 et fallOffRamp*1).</li>
</ul>
<p>Enoncé comme ça, ça doit commencer à vous parler un peu...</p>
<p>J'ai mis pas mal de temps à trouver la combine mais la voici:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Ambiant</span>
float3 AmbiResult <span style="color: #339933;">=</span> ambiantColor <span style="color: #339933;">*</span> <span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">-</span>ambiantFallOffFactor<span style="color: #009900;">)</span> <span style="color: #339933;">+</span> fallOffRamp <span style="color: #339933;">*</span> ambiantFallOffFactor<span style="color: #339933;">;</span></pre>
<p>Regardez bien cette ligne (Vous -> :gne: )et comparé là à ce que j'ai dis juste au dessus, ça devrait faire tilte! Une fois qu'on a compris le raisonnement, ça saute aux yeux!</p>
<p>Ca marche! Modifiez et faites vos tests: Le ambiantFallOffFactor fait bien office de "switch".</p>
<p>On y est presque! Le dernier souci c'est concernant la couleur. En effet, rien pour l'instant ne permet de choisir la couleur du falloff...</p>
<p>Il y a plusieurs façons de procéder. La plus simple serait de créer un autre paramètre que vous pouvez appeler fallOffColor et le multiplier (plus haut) à la fallOffRamp.</p>
<p>Je n'ai pas choisi cette option et je vais vous expliquer pourquoi :redface: :</p>
<p>Pour moi, le principe est que le falloff ne fait que régir le comportement de l'ambiant:</p>
<ul>
<li>Si il est désactivé (ambiantFallOffFactor = 0), nous avons une ambiant "de base": Une addition linéaire ambiantColor sur l'illumination.</li>
<li>Si il est activé (ambiantFallOffFactor = 0.5), nous avons une ambiant qui se "falloffise", mais qui doit conserver la couleur de l'ambiant...</li>
</ul>
<p>Et pour faire ça, on multiplie ambiantColor au falloff:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Ambiant</span>
float3 AmbiResult <span style="color: #339933;">=</span> ambiantColor <span style="color: #339933;">*</span> <span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">-</span>ambiantFallOffFactor<span style="color: #009900;">)</span> <span style="color: #339933;">+</span> ambiantColor <span style="color: #339933;">*</span> fallOffRamp <span style="color: #339933;">*</span> ambiantFallOffFactor<span style="color: #339933;">;</span></pre>
<p>Mais dites donc! Ça ne vous rappelle rien? Mais si! Vos anciens cours de math! Nous avons ambiantColor qui est multiplié deux fois... Il faut Factoriser! :baffed:</p>
<pre class="c c"><span style="color: #666666; font-style: italic;">//Ambiant</span>
float3 AmbiResult <span style="color: #339933;">=</span> ambiantColor <span style="color: #339933;">*</span> <span style="color: #009900;">(</span><span style="color: #009900;">(</span><span style="color: #0000dd;">1</span><span style="color: #339933;">-</span>ambiantFallOffFactor<span style="color: #009900;">)</span> <span style="color: #339933;">+</span> fallOffRamp <span style="color: #339933;">*</span> ambiantFallOffFactor<span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<center><i>J'avoue, c'est vraiment pour faire chier! Mais c'est tellement rare de pouvoir utiliser ce qu'on c'est cassé le cul à apprendre à l'école que ça fait plaisir! :hehe:</i></center>
<p>Testez avec:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> AmbiResult<span style="color: #339933;">;</span></pre>
<p>En gros, quand l'ambiant est à fond mais que l'ambiantFallOffFactor est à fond aussi, c'est le falloff qui prend le dessus:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_012.png" alt="cgfx_part3_012.png" style="display:block; margin:0 auto;" title="cgfx_part3_012.png, juil. 2010" height="93" width="410" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_013.png" alt="cgfx_part3_013.png" style="display:block; margin:0 auto;" title="cgfx_part3_013.png, juil. 2010" height="152" width="187" /></p>
<p>Par contre, si l'ambiantFallOffFactor est nul, aucun falloff pour le shader, c'est l'ambiant color qui prend le dessus:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_014.png" alt="cgfx_part3_014.png" style="display:block; margin:0 auto;" title="cgfx_part3_014.png, juil. 2010" height="95" width="395" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_015.png" alt="cgfx_part3_015.png" style="display:block; margin:0 auto;" title="cgfx_part3_015.png, juil. 2010" height="138" width="161" /></p>
<p>Le tout, avec une belle transition, propre, et colorée!</p>
<p>Et pour finir, on réécrit le "result" en ajoutant AmbiResult au reste de l'illumination:</p>
<pre class="c c">float3 result <span style="color: #339933;">=</span> <span style="color: #009900;">(</span>illumPointLight0 <span style="color: #339933;">+</span> illumPointLight1 <span style="color: #339933;">+</span> AmbiResult<span style="color: #009900;">)</span> <span style="color: #339933;">*</span> diffuseColorResult<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_016.png" alt="cgfx_part3_016.png" style="display:block; margin:0 auto;" title="cgfx_part3_016.png, juil. 2010" height="346" width="432" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_part3_017.png" alt="cgfx_part3_017.png" style="display:block; margin:0 auto;" title="cgfx_part3_017.png, juil. 2010" height="203" width="236" /></p>
<center><i>Vous voyez le petit débouché de lumière en bas à gauche?</i></center>
<p>Il commence à y avoir un petit effet de matière intéressant :sourit: .</p>
<p>Pour télécharger le shader -> <a href="https://www.fevrierdorian.com/blog/public/billets/2010_07_26_CgFX_part3/cgfx_tuto_005.7z">cgfx_tuto_005.7z</a></p>
<center>:longBar:</center>
<h4>Conclusion</h4>
<p>Je m'arrête ici pour cette troisième partie. Je sais que le plus intéressant (texture et bump) n'a pas été abordé mais <a href="https://www.fevrierdorian.com/blog/post/2010/08/04/CgFX-Des-shaders-temps-reel-dans-le-viewport-Maya-Part-4">la prochaine partie</a> sera dédiée aux textures :sauteJoie: .</p>
<p>En attendant, n'hésitez pas à laisser un commentaire si vous avez des questions et/ou suggestions.</p>
<p>A bientôt!</p>
<p>Dorian</p>
<center><i>:marioCours:</i></center>
MediaInfo: Lire les informations des fichiers vidéo et audio en ligne de commande. (Part 2)urn:md5:793201b93c64f690294afcb0ee05c9bf2009-11-29T18:59:00+01:002013-07-26T22:36:20+02:00NarannScript et codeaudiofichiersfrimageinformationligne de commandeliremediainfopythontutovideo<p><img src="https://www.fevrierdorian.com/blog/public/billets/mediainfo/python_fouilleQuicktime150.png" alt="python_fouilleQuicktime150.png" style="float:left; margin: 0 1em 1em 0;" title="python_fouilleQuicktime150.png, nov. 2009" height="150" width="150" />Lors du <a href="https://www.fevrierdorian.com/blog/post/2009/10/20/MediaInfo%3A-Lire-les-informations-des-fichiers-vid%C3%A9o-et-audio-en-ligne-de-commande.">billet précédent</a> nous avons vu comment extraire une information (le nombre d'image) d'un fichier Quicktime en ligne de commande. Je vous propose ici la suite de ce tutorial en utilisant Python afin d'étendre cette possibilité à tout les fichiers d'un dossier.</p>
<p>MediaInfo reconnaissant un bon paquet de format, à la fin de ce tuto, vous aurez la possibilité d'étendre ce script suivant votre usage.</p> <h5>Ce qu'on veux faire <a name="Ce_quon_veux_faire"></a></h5>
<p>L'idée est de récupérer une liste de ce type:</p>
<p>SEQ006PL055.mov -> 134 images<br />
SEQ006PL056.mov -> 97 images<br />
SEQ006PL057.mov -> 63 images<br />
etc...</p>
<p>Ladite liste étant les fichiers Quicktime d'un dossier...</p>
<h5>Et maintenant, le python <a name="et_mnt_le_python"></a></h5>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">re</span>
<span style="color: #ff7700;font-weight:bold;">def</span> frameCount<span style="color: black;">(</span>path<span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">for</span> fileName <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">listdir</span><span style="color: black;">(</span>path<span style="color: black;">)</span>:
filePath = path+<span style="color: #483d8b;">"/"</span>+fileName <span style="color: #808080; font-style: italic;">#"C:/myQuicktimeFiles" + "/" + "SEQ006PL055.mov"</span>
<span style="color: #ff7700;font-weight:bold;">if</span><span style="color: black;">(</span><span style="color: #dc143c;">os</span>.<span style="color: black;">access</span><span style="color: black;">(</span>filePath, <span style="color: #dc143c;">os</span>.<span style="color: black;">F_OK</span><span style="color: black;">)</span><span style="color: black;">)</span>: <span style="color: #808080; font-style: italic;">#verifie que le chemin filePath est bien un fichier et qu'on peut y acceder</span>
handle = <span style="color: #dc143c;">os</span>.<span style="color: black;">popen</span><span style="color: black;">(</span><span style="color: #483d8b;">"C:/MediaInfo/MediaInfo.exe --Inform=Video;%FrameCount% "</span>+filePath<span style="color: black;">)</span>.<span style="color: black;">read</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#recupere la sortie du programme</span>
<span style="color: #ff7700;font-weight:bold;">print</span><span style="color: black;">(</span>fileName+<span style="color: #483d8b;">" -> "</span>+<span style="color: #dc143c;">re</span>.<span style="color: black;">sub</span><span style="color: black;">(</span><span style="color: #483d8b;">"<span style="color: #000099; font-weight: bold;">\n</span>"</span>, <span style="color: #483d8b;">""</span>, handle<span style="color: black;">)</span>+<span style="color: #483d8b;">" images"</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#affiche le resultat</span>
frameCount<span style="color: black;">(</span><span style="color: #483d8b;">"C:/myQuicktimeFiles"</span><span style="color: black;">)</span></pre>
<p>Dans un premier temps, il faut lister les fichiers contenus dans le dossier spécifié. Pour ça, nous utilisons la methode os.listdir():</p>
<pre class="python python"><span style="color: #66cc66;">>>></span> <span style="color: #dc143c;">os</span>.<span style="color: black;">listdir</span><span style="color: black;">(</span><span style="color: #483d8b;">"C:/myQuicktimeFiles"</span><span style="color: black;">)</span>
<span style="color: black;">[</span><span style="color: #483d8b;">'SEQ006PL055.mov'</span>, <span style="color: #483d8b;">'SEQ006PL056.mov'</span>, <span style="color: #483d8b;">'SEQ006PL057.mov'</span><span style="color: black;">]</span></pre>
<p>Pour exécuter une commande système en python on peut utiliser plusieurs méthodes. La première que j'ai utilisé fut:</p>
<pre class="python python"><span style="color: #dc143c;">os</span>.<span style="color: black;">system</span><span style="color: black;">(</span><span style="color: #483d8b;">"ma commande"</span><span style="color: black;">)</span></pre>
<p>Mais le problème est que cette commande renvois la valeur de retour du programme (souvent "0" ce qui signifie que tout c'est bien passé) mais elle ne renvois pas ce qu'écrit le programme et c'est précisément ça qui nous intéresse.</p>
<p>Après quelques recherche j'ai trouvé exactement la commande qui m'intéressait:</p>
<pre class="python python"><span style="color: #dc143c;">os</span>.<span style="color: black;">popen</span><span style="color: black;">(</span><span style="color: #483d8b;">"ma commande"</span><span style="color: black;">)</span></pre>
<p>Cette commande renvois donc tout ce qu'a sortie la commande exécutée dans un "tunnel de communication"... J'ai un peu galéré pour savoir comment "réinterpréter" tout ça mais voici la commande que j'ai trouvé <a href="http://techkr.blogspot.com/2007/02/python-how-to-get-output-from-ossystem.html" hreflang="en">ici</a>:</p>
<pre class="python python"><span style="color: #dc143c;">os</span>.<span style="color: black;">popen</span><span style="color: black;">(</span><span style="color: #483d8b;">"ma commande"</span><span style="color: black;">)</span>.<span style="color: black;">read</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Avec ça, nous avons une chaine de caractère toute propre comme si elle sortait de notre console! :sourit:</p>
<p>Au niveau du print j'ai du faire une bidouille pour éviter qu'il ne saute deux ligne à chaque ligne:</p>
<pre class="python python"><span style="color: #dc143c;">re</span>.<span style="color: black;">sub</span><span style="color: black;">(</span><span style="color: #483d8b;">"<span style="color: #000099; font-weight: bold;">\n</span>"</span>, <span style="color: #483d8b;">""</span>, handle<span style="color: black;">)</span></pre>
<p>Cette ligne "substitue" les caractère de retour à la ligne "\n" par "rien" dans la chaine de caractère "handle"</p>
<p>Et c'est tout! (Enfin je crois... ^^ )</p>
<p>Ce deuxième tuto n'était qu'une formalité comparé au <a href="https://www.fevrierdorian.com/blog/post/2009/10/20/MediaInfo%3A-Lire-les-informations-des-fichiers-vid%C3%A9o-et-audio-en-ligne-de-commande.">précédent</a> mais il était dans la continuité.</p>
<p>N'hésitez pas à poster un commentaire si vous rencontrez des difficultés lié à l'utilisation de ce tutorial.</p>
<p>A bientôt!</p>