Dorian Fevrier's blog - Mot-clé - apiJe 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:695d9c73474c33ce3dab043823509c4bDotclearL'instanciation implicite du format Alembicurn:md5:88b132d883e85fe0aee35fcca778bc062017-06-23T23:10:00+02:002023-05-10T11:11:50+02:00NarannInfographie 3D - Boulotalembicapicodeinstancemayapython<p><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_tn.png" style="float: left; margin: 0 1em 1em 0;" />Suite à la publication de <a class="ref-post" href="https://www.fevrierdorian.com/blog/post/2017/06/11/Post-mortem%3A-Ballerina">mon post mortem sur Ballerina</a>, certains d’entre vous semblaient intrigués par le paragraphe concernant l’instanciation implicite des fichiers Alembics.</p>
<p>Dans ce billet, je vous propose d’aller un peu plus loin avec une explication théorique, un peu de pratique et un peu de code (la recette du bonheur en somme :petrus: ).</p> <h4>Le principe</h4>
<p>Dans un fichier Alembic, les géométries sont stockées sous forme de tableaux.:</p>
<ul>
<li>Les positions des sommets : [point1.x, point1.y, point1.z, point2.x, point2.y, point2.z, point3.x, …]</li>
<li>Les nombres de « points par face » (souvent quatre, comme vous pouvez vous en douter): [4, 4, 4, 4, 4, 4, 4, …]</li>
<li>Les indices de positions par face : [0, 1, 2, 3, 3, 2, 4, 5, …]</li>
</ul>
<p>Le dernier est le plus subtil à comprendre : Combiné au second, il permet de construire les faces : La face 1 est compose de 4 indices. On prend donc, dans le tableau de position, les indices de position (dernier tableau): 0, 1, 2, 3. La face 2, est compose de 4 indices. On prend donc, dans le tableau d’indice de position, les 4 indices suivants qu’on va chercher dans le tableau des positions : 3, 2, 4, 5. Et ainsi de suite.</p>
<p>C’est un peu bizarre si on n’est pas habitué, mais on stocke très souvent les données géométriques de cette façon et je vais tenter de vous expliquer pourquoi. :hehe:</p>
<p>Vous l’aurez compris, le premier tableau ne concerne que les positions des sommets (<em>vertices</em> en anglais) et les deux seconds tableaux, la topologie de la géométrie. Quand un objet est animé, ce ne sont souvent que ses sommets qui bougent. Sa topologie (l’ordre de ses faces, arêtes et sommets) ne change pas.</p>
<p>Mais comment, quand on exporte de la géométrie image après image, la lib Alembic sait-elle que la topologie n’a pas changée ? C’est la magie des <a href="https://fr.wikipedia.org/wiki/Fonction_de_hachage" hreflang="fr">fonctions de hachage</a>.</p>
<p>Histoire de vous éviter la lecture de la page Wikipédia, une fonction de hachage sert, grosso modo, à générer une signature numérique (qui ressemble vaguement à « 867fc32883baaa34 ») depuis une suite de bit.</p>
<p>Comme vous vous en doutez, la suite de bit en question, ce sont nos tableaux. En langage bas niveau (C++ en l’occurrence), un tableau est une suite de valeur fortement typées. Un chiffre flottant <a href="https://fr.wikipedia.org/wiki/IEEE_754#Format_simple_pr.C3.A9cision_.2832_bits.29" hreflang="fr">se stocke sur 32 bits</a>. Une position se stocke sur 3 chiffres flottants (x, y, z), soit 3x32=96bits. Pour 8 positions (un cube) il faut donc 8x96=768bits. Ce sont ces 768bits que la fonction de hachage (<a href="https://github.com/alembic/alembic/blob/d75360416b1b20df85c4f58195d0898695ceb525/lib/Alembic/Util/SpookyV2.h#L37" hreflang="en">Spooky</a> de son petit nom) va ingurgiter pour nous sortir une valeur bizarre (eg. « 867fc32883baaa34 »): La signature numérique du tableau de position. Si on renvoie le même tableau (avec des positions parfaitement identiques), on a la même signature.</p>
<p>Vous venez juste de vous farcir un cours de science informatique en vitesse de la lumière la ! :hihi:</p>
<p>Dans un Alembic, chaque tableau possède donc sa signature numérique.</p>
<p>À chaque fois que vous envoyez un nouveau tableau à Alembic (pour chaque image en fait), ce dernier calcule sa signature numérique (son hash). S’il est déjà présent dans le fichier, il ne l’ajoute pas au fichier mais précise simplement que l’image en question utilise le tableau avec le hash que vous venez de calculer.</p>
<p>Avec ce système on peut avoir plusieurs tableaux contenant la position des sommets animés (un par image en fait) tout en gardant les deux tableaux de topologie unique pour tout le fichier.</p>
<p>Pour résumer, si vous exporter l’animation d’un simple cube déformé sur 10 images vous aurez :</p>
<ul>
<li>10 tableaux de position (un par image)</li>
<li>1 tableau de point par face (celui de la première image, réutilise sur toutes les images)</li>
<li>1 tableau de position par face (celui de la première image, réutilise sur toutes les images)</li>
</ul>
<p>Pour les plus curieux d’entre vous, voici <a href="https://github.com/alembic/alembic/blob/895a7c6a8b3396a21c6ba03ddf91ed91514d7b93/maya/AbcExport/MayaMeshWriter.cpp#L983" hreflang="en">la ligne de code</a> de l’exporteur Alembic de Maya qui s’occupe d’envoyer les différents tableaux que je vous ai présenté ci-dessus dans un fichier Alembic. Notez que l’exporteur ne fait aucune distinction. Pour chaque image, il envoie tout à l’Alembic et c’est ce dernier qui décide de stocker les tableaux dans le fichier ou non.</p>
<p>Une fois qu’on a un beau fichier tout optimisé qu’est ce qui se passe ?</p>
<p>Et bien quand l’application (un moteur de rendu par exemple) demande, les tableaux de l’image 5 à un fichier Alembic, ce dernier (enfin le code de la lib Alembic) renvoi le tableau de position de l’image 5, puis les deux tableaux de la topologie de la première image.</p>
<blockquote>
<p>Notez que je ne vous ai pas parlé des UVs, normals, vertex color, etc. Mais sachez que le concept est le même que pour les positions et la topologie.</p>
</blockquote>
<p>Et les instances implicites là-dedans ?</p>
<p>J’arrive au dernier point, celui qui devrait vous faire tilter. :idee:</p>
<p>En plus de stocker de la géométrie, Alembic stock aussi la hiérarchie. Un <em>transform</em> est un objet présenté sous la forme de translation, rotation, échelle et dont la représentation mathématique est une matrice 4 × 4 (je ne rentre pas dans les détails mais sachez que quand vous manipulez un <em>transform</em>, vous manipulez en fait une matrice). Une hiérarchie de <em>transform</em> est donc une hiérarchie de matrice.</p>
<p><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_022.png" style="margin: 0 auto; display: table;" /></p>
<p style="text-align: center;">Derrière les paramètres que vous manipulez tous les jours se cache un objet mathématique bien cool : La matrice ! :youplaBoum:</p>
<p>Au même titre que pour les tableaux de position et de topologie, les matrices peuvent être animée par image (quand on anim un simple déplacement qui ne déforme pas la géométrie de l’objet)</p>
<p>Notez que dans mon exemple précédant, j’ai précisé qu’il s’agissait d’un cube déformé. L’animation ne se situait donc pas sur le <em>transform</em> du cube mais directement sur les sommets (c’est la géométrie qui bouge à chaque image, comme un personnage skinné en fait).</p>
<p>Sauf que si, au lieu de déformer l’objet, vous n’animer que son <em>transform</em> (translation, rotation, échelle), le tableau des positions des sommets ne change pas d’une image à l’autre, seule le <em>transform</em> parent de la <em>shape</em> (la matrice parent) change.</p>
<p>Ce qui veut dire (arriver ici vous devriez l’avoir compris) que si un modeleur duplique des centaines d’objets sans les modifier puis exporte un Alembic, les sommets et topologies des objets ne sont stockés qu’une seule fois dans le fichier et seul la position des matrices (différentes pour chaque objet) sont stockés de manière individuelle. Et ça, <em>c’est</em> la définition d’une instance géométrique !</p>
<p>Je reprends un schéma que <a class="ref-post" href="https://www.fevrierdorian.com/blog/post/2017/03/25/Pourquoi-les-instances-Maya-sont-si-lourdes">j’avais utilisé</a> pour expliquer le principe des instances Maya (c’est pas super adapté mais ça représente bien le principe):</p>
<p><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_03_26_instance_lourde_maya/instance_001.png" style="margin: 0 auto; display: table;" /></p>
<h4>Un exemple concret</h4>
<p>Comme je sais que vous ne me croyez pas, je vous propose un exemple concret à l’aide de Maya. :siffle:</p>
<p>Créez une sphère :</p>
<p><img alt="instance_implicite_alembic_001" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_001.png" style="margin: 0px auto; display: table; width: 404px; height: 101px;" /></p>
<p><img alt="instance_implicite_alembic_002" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_002.png" style="margin: 0px auto; display: table; width: 254px; height: 207px;" /></p>
<p>Subdivisez-la histoire qu’on puisse voir des différences de poids facilement lors de l’export :</p>
<p><img alt="instance_implicite_alembic_003" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_003.png" style="margin: 0px auto; display: table; width: 192px; height: 243px;" /></p>
<p><img alt="instance_implicite_alembic_004" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_004.png" style="margin: 0px auto; display: table; width: 201px; height: 184px;" /></p>
<p>Quatre, c’est très bien :</p>
<p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_005.png"><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/.instance_implicite_alembic_005_m.png" style="margin: 0px auto; display: table; width: 560px; height: 246px;" /></a></p>
<p><img alt="instance_implicite_alembic_006" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_006.png" style="margin: 0px auto; display: table; width: 434px; height: 429px;" /></p>
<p style="text-align: center;">:trollface:</p>
<p>Détruisez l’historique afin de ne garder que la <em>shape</em> :</p>
<p><img alt="instance_implicite_alembic_007" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_007.png" style="margin: 0px auto; display: table; width: 393px; height: 254px;" /></p>
<p><img alt="instance_implicite_alembic_008" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_008.png" style="margin: 0px auto; display: table; width: 455px; height: 121px;" /></p>
<p>C’est parti ! Dupliquez ça plusieurs fois :</p>
<p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_009.png"><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/.instance_implicite_alembic_009_m.png" style="margin: 0px auto; display: table; width: 560px; height: 137px;" /></a></p>
<p>Notez qu’il ne s’agit en aucun cas d’instances Maya au sens propre. Ce sont de simples duplications. Notez aussi comment, malgré le poids de la géométrie, Maya reste réactif. Je soupçonne en effet que ce dernier utilise aussi l’instance implicite quand on fait des duplications et ne duplique la géométrie de chaque objet en mémoire qu’une fois qu’on commence à modifier l’objet. :reflechi:</p>
<p>Mais on ne s’arrête pas ! :grenadelauncher:</p>
<p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_010.png"><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/.instance_implicite_alembic_010_m.png" style="margin: 0px auto; display: table; width: 560px; height: 316px;" /></a></p>
<p>C’est pas mal, maintenant on exporte tout ça :</p>
<p><img alt="instance_implicite_alembic_011" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_011.png" style="margin: 0px auto; display: table; width: 327px; height: 160px;" /></p>
<p>Une seule image (vous pourrez refaire le test sur un range plus large, ça ne changera pas grand-chose):</p>
<p><img alt="instance_implicite_alembic_012" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_012.png" style="margin: 0px auto; display: table; width: 417px; height: 199px;" /></p>
<p>Pas besoin des normales ni des UVs :</p>
<p><img alt="instance_implicite_alembic_013" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_013.png" style="margin: 0px auto; display: table; width: 381px; height: 281px;" /></p>
<p>Puis validez (ou exécutez cette commande MEL):</p>
<pre>
<code class="language-bash">AbcExport -j "-frameRange 1 1 -dataFormat ogawa -file /home/narann/test/test1.abc";</code></pre>
<p>L’export devrait être assez rapide. Chez moi, le fichier fait 3.12Mo. Clairement, toute la géométrie n’est pas stockée. C’est parce qu’Alembic a reconnu que toutes les sphères étaient identiques. Les données qui composent sa géométrie (tableau de position des sommets et topologie) ne sont donc stockées qu’une seule fois, le reste étant des <em>transforms</em> (matrices) pointant vers la même géométrie. :redface:</p>
<p>Mais peut-être qu’avec 3.12Mo vous n’êtes toujours pas convaincu. Peut-être que 105 sphères subdivise à 4 ça ne pèse que 3.12Mo après tout… :perplex:</p>
<p>On va donc faire un truc qui est très souvent fait en production, un truc souvent demande par le <em>rig</em> pour plein de bonnes raisons : On va réinitialiser les <em>transforms</em>. Sélectionnez tout :</p>
<p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_014.png"><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/.instance_implicite_alembic_014_m.png" style="margin: 0px auto; display: table; width: 560px; height: 320px;" /></a></p>
<p>Faites un "<em>Freeze Transformations</em>":</p>
<p><img alt="instance_implicite_alembic_015" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_015.png" style="margin: 0px auto; display: table; width: 201px; height: 100px;" /></p>
<p>Les valeurs des positions sont donc revenus à 0 mais les centres géométriques des objets n’ont pas bouge. C’est dû au fait que Maya permet de désolidariser le point de pivot de l’objet par rapport au centre géométrique.</p>
<p><img alt="instance_implicite_alembic_016" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_016.png" style="margin: 0px auto; display: table; width: 321px; height: 291px;" /></p>
<p>On va donc faire un "<em>Reset Transformations</em>" :</p>
<p><img alt="instance_implicite_alembic_017" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_017.png" style="margin: 0px auto; display: table; width: 208px; height: 91px;" /></p>
<p>Celui-ci vient modifier les valeurs géométriques de tous les sommets pour qu’ils correspondent au point de pivot. Dans notre cas, chaque objet a maintenant son <em>transform</em> ainsi que son centre géométrique au centre de la scène :</p>
<p><img alt="instance_implicite_alembic_018" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_018.png" style="margin: 0px auto; display: table; width: 350px; height: 244px;" /></p>
<p>Cela veut dire que chaque sommet de chaque sphère possède une position identique à sa position dans le monde (0, 0, 0). Chaque sommet ayant une position unique par rapport à son centre géométrique, on a donc perdu toute forme d’instanciation implicite pour Alembic. Mais qu’a cela ne tienne, testez pas vous-même :</p>
<pre>
<code class="language-bash">AbcExport -j "-frameRange 1 1 -dataFormat ogawa -file /home/narann/test/test2.abc";</code></pre>
<p>Chez moi, le fichier fait 121Mo. :siffle: Chaque sphère possède ses propres tableaux de position de sommet. En principe les tableaux de topologie sont instanciés car notre petite manipulation des points de pivot n’a pas change la topologie. Si on modifiait la topologie aléatoirement pour chaque sphère, le fichier aurait été encore plus gros.</p>
<p>Bon, on a deux fichiers, il serait peut-être temps de les tester dans nos moteurs de rendu favoris pour savoir ce qu’il en est.</p>
<h4>Dans Guerilla</h4>
<p>Je vais tester dans Guerilla car c’est avec lui que je suis le plus à l’aise et il dispose d’un bon retour pour savoir si l’Alembic est correctement interprété.</p>
<p>Importez votre premier Alembic. Les applications étant souvent friandes de moyen d’optimiser le chargement des fichiers, elles s’appuient sur l’instanciation implicite que leur propose Alembic (comme expliqué dans le post mortem, ce fut flagrant sur Mari). Guerilla n’y échappe pas et c’est assez rapide :</p>
<p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_019.png"><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/.instance_implicite_alembic_019_m.png" style="margin: 0px auto; display: table; width: 560px; height: 335px;" /></a></p>
<p>Avant de faire le premier rendu, activez la <em>Verbosity</em> à <em>Diagnostics</em> puis cochez <em>Diagnostic Shapes</em> et <em>Diagnostic Accelerator</em>:</p>
<p><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_020.png" style="margin: 0px auto; display: table; width: 236px; height: 515px;" /></p>
<p>Puis faites un rendu. Voici le log :</p>
<pre>
<code>06/19/2017 15:36:48 RNDR DIA: hash for 'test:pSphere61|test:pSphereShape61' is 867fc32883baaa34:60c4e1c779f31ee
06/19/2017 15:36:48 RNDR DIA: build accel 'test:pSphere61|test:pSphereShape61'
06/19/2017 15:36:48 SHAP DIA: loaded shape '/home/narann/test/test1.abc' '/pSphere61/pSphereShape61.RenderGeometry'
06/19/2017 15:36:48 SHAP DIA: P float3[99842] min=(-0.979728,-0.997817,-0.979728) max=(0.979728,0.997817,0.979728)
06/19/2017 15:36:48 SHAP DIA: N float3[99842] min=(-1.000000,-1.000000,-1.000000) max=(1.000000,1.000000,1.000000)
06/19/2017 15:36:48 MBVH DIA: Building triangle accelerator for 'test:pSphere61|test:pSphereShape61'
06/19/2017 15:36:48 MBVH DIA: Built accelerator for 'test:pSphere61|test:pSphereShape61', 199680 triangles, 8.81M (geo 3.81M, tree 5.00M)
06/19/2017 15:36:48 RNDR DIA: hash for 'test:pSphere62|test:pSphereShape62' is 867fc32883baaa34:60c4e1c779f31ee
06/19/2017 15:36:48 BRDF DIA: hash for 'test:pSphere76|test:pSphereShape76' is 867fc32883baaa34:60c4e1c779f31ee
06/19/2017 15:36:48 BRDF DIA: hash for 'test:pSphere49|test:pSphereShape49' is 867fc32883baaa34:60c4e1c779f31ee
06/19/2017 15:36:48 BRDF DIA: hash for 'test:pSphere33|test:pSphereShape33' is 867fc32883baaa34:60c4e1c779f31ee
06/19/2017 15:36:48 BRDF DIA: hash for 'test:pSphere18|test:pSphereShape18' is 867fc32883baaa34:60c4e1c779f31ee
06/19/2017 15:36:48 BRDF DIA: hash for 'test:pSphere48|test:pSphereShape48' is 867fc32883baaa34:60c4e1c779f31ee
...</code></pre>
<p>Comme vous pouvez le constater, la <em>shape</em> n’est chargée qu’une seule fois (<em>loaded shape</em> dans le log) puis Guerilla s’appuie sur le hash, toujours identique, pour placer les autres sphères.</p>
<p>Si vous changez de fichier Alembic et que vous relancez le rendu, vous constaterez que le message de chargement de la <em>shape</em> « <em>loaded shape</em> » s’applique pour chaque sphère du fichier et que le rendu met plus de temps avant de démarrer.</p>
<p>Là ou c’est intéressant (et je suis sûr que tous les autres moteurs le font) c’est que quand Guerilla charge plusieurs Alembic, il instancie entre fichier. Si un modeleur a utilisé deux objets identiques dans deux Alembic différents, Guerilla le remarque et ne le charge qu’une fois en mémoire. Forcément, quand tes bâtiments ne sont que des variantes de silhouette utilisant des objets géométriques identiques c’est du pain béni pour le moteur.</p>
<h4>Comment permettre aux modeleurs de savoir quand deux objets vont être instancie dans un Alembic ?</h4>
<blockquote>
<p>Si vous ne connaissez pas Python, vous risquez d’être un peu perdu sur cette dernière partie, je préfère vous prévenir. :)</p>
</blockquote>
<p>Sur Ballerina nous avions une commande développée en externe qui nous permettait d’avoir le même hash que ceux qui allaient être mis généré par l’Alembic. C’est assez difficile à faire et mon but c’est de vous mettre le pied à l’étrier.</p>
<p>Je vous propose deux code :</p>
<ul>
<li>Un très simple, à base de commande Python qui ne s’occupe que des sommets.</li>
<li>Un autre, plus compliqué, faisant des appels à l’API Maya en Python mais qui prend en compte les UVs.</li>
</ul>
<p>Bien entendu, ce sont des codes que j’ai fais chez moi sur des scènes cubes et sphère mais absolument pas teste en production. À vous de voir ce qu’ils valent.</p>
<h4>En commande Maya</h4>
<p>Voici le premier code :</p>
<pre>
<code class="language-python">import collections
import maya.cmds as mc
h_vtx = collections.defaultdict(set)
for shp in mc.ls(type='mesh'):
h = hash(tuple(mc.xform(shp+'.vtx[*]', query = True, objectSpace = True, translation = True)))
h_vtx[h].add(shp)</code></pre>
<p>Et l’explication ligne à ligne :</p>
<pre>
<code class="language-python">h_vtx = collections.defaultdict(set)</code></pre>
<p>On crée d’abords un dictionnaire (<a href="https://docs.python.org/2/library/collections.html#collections.defaultdict" hreflang="en">defaultdict</a>, qui permet d’ajouter un objet, ici un set, à la volée):</p>
<ul>
<li>Les clefs seront les hash des positions des sommets (comme <em>14653146579</em>)</li>
<li>Les valeurs un <em>set()</em> de <em>shape</em> correspondant au hash en clef (comme <em>set('|pSphere1|pSphereShape1', '|pSphere2|pSphereShape2')</em>)</li>
</ul>
<pre>
<code class="language-python">for shp in mc.ls(type='mesh'):</code></pre>
<p>Via cette boucle nous allons traverser toutes les <em>shapes</em> de type <em>mesh</em> de la scène.</p>
<pre>
<code class="language-python"> h = hash(tuple(mc.xform(shp+'.vtx[*]', query = True, objectSpace = True, translation = True)))</code></pre>
<p>Il y a plusieurs commandes empaquetées ici :</p>
<ul>
<li><a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-Tech-Docs/CommandsPython/xform.html" hreflang="en">mc.xform()</a> renvoi la position des sommets en espace objet.</li>
<li><a href="https://docs.python.org/2/tutorial/datastructures.html#tuples-and-sequences" hreflang="en">tuple()</a> permet d’avoir une séquence immutable ordonnée à partir de laquelle on pourra générer un hash (plus d’info <a href="https://stackoverflow.com/questions/7027199/hashing-arrays-in-python" hreflang="en">ici</a>).</li>
<li><a href="https://docs.python.org/2/library/functions.html#hash" hreflang="en">hash()</a> permet de générer une signature depuis une variable (la liste des positions des sommets).</li>
</ul>
<p>On stocke le hash dans la variable… « <em>h</em> ».</p>
<pre>
<code class="language-python"> h_vtx[h].add(shp)</code></pre>
<p>Ici on ajoute la <em>shape</em> à la liste des <em>shapes</em> ayant le même hash que celui trouvé précédemment.</p>
<p>Imaginons une scène qui ressemble à ça (juste des sphères dupliquées avec une, au centre, dont j’ai bouge un vertex):</p>
<p><a class="media-link" href="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/instance_implicite_alembic_021.png"><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/.instance_implicite_alembic_021_m.png" style="margin: 0px auto; display: table; width: 560px; height: 401px;" /></a></p>
<p>Si on exécute ce bout de code et qu’on print "<em>h_vtx</em>" on obtient :</p>
<pre>
<code class="language-python"># Result: defaultdict(<type 'set'>, {-1145497079: set([u'pSphereShape9', u'pSphereShape3', u'pSphereShape2', u'pSphereShape1', u'pSphereShape10', u'pSphereShape7', u'pSphereShape6', u'pSphereShape5', u'pSphereShape4', u'pSphereShape12', u'pSphereShape11']), 1873436783: set([u'pSphereShape8'])}) #
</code></pre>
<p>Le dictionnaire montre deux hashes (<em>-1145497079</em> et <em>1873436783</em>), le dernier n’ayant qu’une sphère, celle dont le vertex a été bouge. Vous pouvez sélectionner les sphères du premier groupe :</p>
<pre>
<code class="language-python">mc.select(list(h_vtx[h_vtx.keys()[0]]))</code></pre>
<p>Modifiez <em>0</em> par <em>1</em> pour sélectionner la sphère du second groupe.</p>
<p>Dès lors, vous pouvez commencer à expérimenter : Dupliquer la sphère du centre (celle avec un vertex en vrac) plusieurs fois puis réexécutez le code et voyez comment il reconnait, dans le dictionnaire « h_vtx », les sphères identiques. Ensuite, faite une autre modification sur une sphère puis réexécutez le code et voyez comment cette sphère dispose maintenant de son propre hash.</p>
<p>Dans tous les cas, vous remarquerez que le script les regroupe bien qu’il ne s’agisse pas d’instances réelles Maya.</p>
<p>Arrivez ici.</p>
<h4>En utilisant l’API Maya</h4>
<p>Ici c’est un peu plus compliqué, mais on se rapproche beaucoup plus de ce que fait Alembic :</p>
<pre>
<code class="language-python">import maya.OpenMaya as om
sel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(sel)
fn_meshes = []
for i in xrange(sel.length()):
dag_path = om.MDagPath()
sel.getDagPath(i, dag_path)
fn_mesh = om.MFnMesh(dag_path)
fn_meshes.append((fn_mesh.fullPathName(), fn_mesh))
# on aurait pu ajouter les normals, les crease edges, les colors mais osef
h_pt = {}
h_vtx_counts = {}
h_vtx_ids = {}
h_uv_counts = {}
h_uv_ids = {}
h_uvs = {}
for full_path, fn_mesh in fn_meshes:
# vertex positions
pts = om.MPointArray()
fn_mesh.getPoints(pts)
h = hash(tuple((pts[i].x, pts[i].y, pts[i].z) for i in xrange(pts.length())))
h_pt[full_path] = h
# vertex topology
vtx_counts = om.MIntArray()
vtx_ids = om.MIntArray()
fn_mesh.getVertices(vtx_counts, vtx_ids)
h = hash(tuple(vtx_counts[i] for i in xrange(vtx_counts.length())))
h_vtx_counts[full_path] = h
h = hash(tuple(vtx_ids[i] for i in xrange(vtx_ids.length())))
h_vtx_ids[full_path] = h
# uv positions
uv_us = om.MFloatArray()
uv_vs = om.MFloatArray()
fn_mesh.getUVs(uv_us, uv_vs)
assert uv_us.length() == uv_vs.length()
h = hash(tuple((uv_us[i], uv_vs[i]) for i in xrange(uv_us.length())))
h_uvs[full_path] = h
# uv topology
uv_counts = om.MIntArray()
uv_ids = om.MIntArray()
fn_mesh.getAssignedUVs(vtx_count, vtx_list)
h = hash(tuple(uv_counts[i] for i in xrange(uv_counts.length())))
h_uv_counts[full_path] = h
h = hash(tuple(uv_ids[i] for i in xrange(uv_ids.length())))
h_uv_ids[full_path] = h
# the hash of the hashes
h_total = {}
for full_path in h_pt.keys():
h_total[full_path] = hash((h_pt[full_path],
h_vtx_counts[full_path],
h_vtx_ids[full_path],
h_uvs[full_path],
h_uv_counts[full_path],
h_uv_ids[full_path]))
path_per_h = collections.defaultdict(set)
for full_path, h in h_total.iteritems():
path_per_h[h].add(full_path)
print path_per_h
mc.select(list(path_per_h[path_per_h.keys()[1]]))</code></pre>
<p>Pas de panique, voici l’explication ligne à ligne.</p>
<pre>
<code class="language-python">import maya.OpenMaya as om
sel = om.MSelectionList()
om.MGlobal.getActiveSelectionList(sel)</code></pre>
<p>Comme je n’aime pas les longs espace de nom, j’importe <a href="http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__cpp_ref_group___open_maya_html" hreflang="en">OpenMaya</a> sous l’espace de nom "<em>om</em>". :seSentCon:</p>
<p>Ensuite, on fabrique une <a href="http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__cpp_ref_class_m_selection_list_html" hreflang="en">MSelectionList</a> qui est une sorte de « liste spécialement adaptée à la sélection ». Et on appelle une <a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_global.html#a6d81d38246555884897fb153c93aaf42" hreflang="en">commande globale bien pratique</a> qui récupère la sélection.</p>
<p>TL;DR: On fait l’équivalent de <a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-Tech-Docs/CommandsPython/ls.html" hreflang="en">mc.ls()</a>. avec plus de lignes. :baffed:</p>
<pre>
<code class="language-python">fn_meshes = []
for i in xrange(sel.length()):
dag_path = om.MDagPath()
sel.getDagPath(i, dag_path)
fn_mesh = om.MFnMesh(dag_path)
fn_meshes.append((fn_mesh.fullPathName(), fn_mesh))</code></pre>
<p>Avec cette boucle, on va récupérer les <a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_fn_mesh.html" hreflang="en">MFnMesh</a> de chacun des mesh de notre sélection. Un MFnMesh est un « ensemble de fonction » (Function set, préfixé <em>MFn</em> dans l’API Maya) qui permet de lier des fonctions sur des données (C’est un peu technique mais dans l’API Maya, les nœuds sont simplement des données compatibles avec certains ensemble de fonction).</p>
<pre>
<code class="language-python"> dag_path = om.MDagPath()
sel.getDagPath(i, dag_path)</code></pre>
<p>On crée un <a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_dag_path.html" hreflang="en">MDagPath</a> vide qu’on remplit avec l’item de la sélection (« i » de la boucle). Un MDagPath est un « chemin vers un nœud hiérarchisé ».</p>
<pre>
<code class="language-python"> fn_mesh = om.MFnMesh(dag_path)</code></pre>
<p>Maintenant qu’on a un chemin direct, on récupère l’ensemble de fonction.</p>
<pre>
<code class="language-python"> fn_meshes.append((fn_mesh.fullPathName(), fn_mesh))</code></pre>
<p>Enfin, on l’ajoute à la liste sous la forme un tuple de deux éléments (le chemin du nœud et l’ensemble de fonction).</p>
<p>On avance dans le script pour la seconde boucle :</p>
<pre>
<code class="language-python"># on aurait pu ajouter les normals, les crease edges, les colors mais osef
h_pt = {}
h_vtx_counts = {}
h_vtx_ids = {}
h_uv_counts = {}
h_uv_ids = {}
h_uvs = {}
</code></pre>
<p>Ici on prépare simplement des dictionnaires de hash. Ils sont tous préfixés d’un « <em>h_</em> » parce qu’ils contiennent des…? Hash bien-sur ! Vous regrettez déjà de ne pas avoir fais math sup’ math spé’ je le sais. Que voulez-vous, certains réussissent et d’autres écrivent un blog. :baffed:</p>
<p>Bref, la clef de chacun des dictionnaires sera le chemin complet d’un nœud, et la valeur, sa valeur de hash. Un peu comme ceci :</p>
<pre>
<code class="language-python">h_pt = {'|pSphere1|pSphereShape1': 1574633,
'|pSphere2|pSphereShape2': 1574633,
'|pSphere3|pSphereShape3': 1657615,
...}</code></pre>
<ul>
<li>« <em>h_pt</em> » contiendra les hash des tableaux de la position des sommets</li>
<li>« <em>h_vtx_counts</em> » contiendra les hash des tableaux du nombre de sommet par face</li>
<li>« <em>h_vtx_ids</em> » contiendra les hash des tableaux des indices des sommets</li>
</ul>
<p>Et pareil pour les uvs… :sourit:</p>
<blockquote>
<p>Notez que je me suis arrêté à la géométrie et aux UVs, mais on aurait pu ajouter les normales, les couleurs par sommet, etc. Simplement que comme on ne les exporte pas avec l’Alembic : On s’en fout ! :dentcasse:</p>
</blockquote>
<p>C’est parti pour la boucle principale (qui est en fait compose de plusieurs blocs assez similaires.</p>
<pre>
<code class="language-python">for full_path, fn_mesh in fn_meshes:</code></pre>
<p>On déroule la boucle, pour chaque chemin complet d’un nœud on a son ensemble de fonction.</p>
<pre>
<code class="language-python"> # vertex positions
pts = om.MPointArray()
fn_mesh.getPoints(pts)
h = hash(tuple((pts[i].x, pts[i].y, pts[i].z) for i in xrange(pts.length())))
h_pt[full_path] = h</code></pre>
<p>On fabrique un <a href="http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__cpp_ref_class_m_point_array_html" hreflang="en">MPointArray()</a> (un tableau de… <a href="http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__cpp_ref_class_m_point_html" hreflang="en">MPoint()</a>) nommé "<em>pts</em>", qu’on remplit avec les points du mesh via la méthode <a href="http://help.autodesk.com/view/MAYAUL/2017/ENU/?guid=__cpp_ref_class_m_fn_mesh_html" hreflang="en">getPoints()</a> de l’ensemble de fonction "<em>fn_mesh</em>".</p>
<p>Ensuite, on déroule les valeurs de chaque point dans un itérateur qu’on déroule à son tour, comme le script précédant, dans un tuple() dont on génère le hash.</p>
<p>La raison pour laquelle on déroule la position des points c’est qu’un MPointArray() n’est pas hashable par python. Il faut donc générer une structure en pur python sinon, dans mon cas, hash renvoi toujours la même valeur, indépendamment du contenu du MPointArray(). :slowclap:</p>
<p>Et la dernière ligne stock le hash pour le chemin complet du nœud.</p>
<p>Et le reste de la boucle c’est tout pareil ! :hehe:</p>
<p>Ne change que le type des tableaux (<a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_int_array.html" hreflang="en">MIntArray()</a> et <a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_float_array.html" hreflang="en">MFloatArray()</a>) ainsi que les méthodes pour récupérer les informations (<a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_fn_mesh.html#a2f2e041f01dc1ec3648002e2524fbfbc" hreflang="en">getVertices()</a>, <a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_fn_mesh.html#aa41349e47d082451f1744cff85412159" hreflang="en">getUVs()</a>, <a href="http://help.autodesk.com/cloudhelp/2017/ENU/Maya-SDK/cpp_ref/class_m_fn_mesh.html#abe6b4afaec85f867401eb3658c3e5459" hreflang="en">getAssignedUVs()</a>).</p>
<p>Juste un petit <em>assert</em> (que j’utilise souvent) pour expliquer que je m’attends à ce que le tableau contenant les valeurs de U et de V fassent la même taille.</p>
<p>On passe à la suite :</p>
<pre>
<code class="language-python"># the hash of the hashes
h_total = {}
for full_path in h_pt.keys():
h_total[full_path] = hash((h_pt[full_path],
h_vtx_counts[full_path],
h_vtx_ids[full_path],
h_uvs[full_path],
h_uv_counts[full_path],
h_uv_ids[full_path]))</code></pre>
<p>Viens l’avant-dernière boucle qui consiste, comme le commentaire l’indique, à générer le « hash des hash ». En effet, bien qu’on ait séparé les hashes par type de tableau (position des sommets, topologie, UVs), ce qui peut être très utile pour mettre le doigt sur les parties qui ne s’accommode pas aux autres, je vous propose de générer un hash final, par nœud.</p>
<p>On génère donc un itérateur avec tous les hash, qu’on envoie dans un hash.</p>
<p>Et pour finir :</p>
<pre>
<code class="language-python">path_per_h = collections.defaultdict(set)
for full_path, h in h_total.iteritems():
path_per_h[h].add(full_path)</code></pre>
<p>On inverse notre dictionnaire avec, en guise de clef, le hash et en guise de valeur, un <em>set()</em> des chemins des nœuds avec ce hash. Ce qui nous donne un dictionnaire qui ressemble à ça :</p>
<pre>
<code class="language-python">h_pt = {1574633: set(['|pSphere1|pSphereShape1',
'|pSphere2|pSphereShape2']),
1657615: set(['|pSphere3|pSphereShape3',
...}</code></pre>
<p>Et on peut sélectionner les nœuds par hash comme ça :</p>
<pre>
<code class="language-python">mc.select(list(path_per_h[path_per_h.keys()[1]]))</code></pre>
<p><img alt="" class="media" src="https://www.fevrierdorian.com/blog/public/billets/2017_06_17_instance_implicite_alembic/sanity_check_modeleur_hash.jpg" style="margin: 0px auto; display: table; width: 480px; height: 601px;" /></p>
<p>Conclusion</p>
<p>J’espère que le principe des instances implicites des fichiers Alembics est plus clair pour vous maintenant. Si vous êtes à l’aise en script, je vous invite à essayer de structurer ces informations dans une petite interface de sélection pour aider vos modeleurs. Ce n’est pas un petit boulot mais sur un projet un peu ambitieux ça peut valoir le coup.</p>
<p style="text-align: center;">:marioCours:</p>
<p>Édit du 25 juin 2017 : J’ai bien conscience que ce billet est assez technique, surtout sa première partie. S’il y a des points qui vous semblent mal expliqués, n’hésitez pas à m’en faire part dans les commentaires, j’essaierai de peaufiner mes explications.</p>Geometry voxelization using Maya Python API (English Translation)urn:md5:92070cf095ad4a8188940b424f02707b2013-03-16T13:47:00+01:002013-07-26T17:59:10+02:00NarannScript et codeapiengeomtriemayapythonvoxel<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray_tn.png" alt="voxel_vray_tn.png" style="float:left; margin: 0 1em 1em 0;" title="voxel_vray_tn.png, mar. 2013" height="150" width="150" />Yet another little script only aimed to use the Maya API. :sauteJoie:</p>
<p>The idea is to reproduce the voxel geometric effect you may have <a href="http://www.bilderzucht.de/blog/3d-pixel-voxel/" hreflang="en">already seen</a>.</p>
<p>Nothing too serious and it probably already exists and maybe faster. To be honest, I did not even look. The goal is again to use the API. :baffed:</p> <h3>Principe</h3>
<p>Before we begin, know that this problem is a textbook case. There are a thousands of ways to solve it and each method has these pro and con. :reflechi:</p>
<p>The method chosen here is simple: For each vertex of the geometry, we find the position "in cube unit" and generates a cube.</p>
<p>The only dificulty with this implementation is, once the vertex position recovered, to know where must be the center of the cube. :seSentCon:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray.png" title="voxel_vray.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/.voxel_vray_m.jpg" alt="voxel_vray.png" style="display:block; margin:0 auto;" title="voxel_vray.png, mar. 2013" height="420" width="560" /></a></p>
<h3>The code</h3>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</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>
voxelStep = <span style="color: #ff4500;">1.0</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span>
grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><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;">xrange</span><span style="color: black;">(</span>pointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<h3>Explainations</h3>
<pre class="python python">sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Basically, a <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_selection_list.html,topicNumber=cpp_ref_class_m_selection_list_html" hreflang="en">MSelectionList</a> is a list of MObject.</p>
<p>I never really understood what the term "selection" meant in this context as it actually does not "select" anything. :bete:</p>
<p>The particularity of a <em>MSelectionList</em> is to retrieve the <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject</a> of a Maya node from its name, what we will do later.</p>
<pre class="python python">dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>It is always difficult to explain to begginers what is a DAG and a DAG path. :gne:</p>
<p>As a large summary:</p>
<ul>
<li>DAG: This is the Maya "hierarchy" (parent/child).</li>
<li>DAG path: This is the "path" of a node through the hierarchy (and thus, having all these transformations).</li>
</ul>
<p>Many Maya API functions require DAG path to work.</p>
<p>For example, you can't retrieve world space coordinates of shape node's vertices if you don't know the path by which you get there. Two instances have only one shape node but it is indeed two different DAG path and world space coordinates of one vertex can have two possible values depending "from where we go".</p>
<pre class="python python">sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span></pre>
<p>We add the Maya object "pSphere1" in the <em>MSelectionList</em> and we retrieve its DAG path (zero is the index in the list).</p>
<p>So we've "converted" "pSphere1" (that doesn't mean anything in Maya API) in true <em>MObject</em>.</p>
<pre class="python python">inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span></pre>
<p>By default, all what you get from the API are <em>MObjects</em>. In general, we check the <em>MObject</em> type using <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject.apiType()</a> or <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_dag_node.html,topicNumber=cpp_ref_class_m_fn_dag_node_html" hreflang="en">MObject.hasFn()</a>.</p>
<p>But here we assume the user provide a mesh. :siffle:</p>
<p>The <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MFnMesh</a> class allow you to deal with a "true" programming object from which we will be able to get informations. It's a <em>function set</em>.</p>
<p>I invite you to read <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">the documentation</a> to understand the how and why of <em>function sets</em>.</p>
<p>So we have a _inMesh_ that we will use to retrieve mesh's vertices.</p>
<pre class="python python">pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span></pre>
<p>The first line creates a <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html" hreflang="en">MPointArray</a> to store our mesh vertices positions.</p>
<p>And the second line fills the array with vertices coordinates world space (position relative to the center of the scene, not the center of the object itself).</p>
<pre class="python python">grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span></pre>
<p>Here we only create an empty group that will store cubes we will create later. It is just more convenient to delete a group with everything in it than select all the cubes manually. :dentcasse:</p>
<pre class="python python">voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>We create a list which will be used to store identifiers (or keys) of areas where cubes have already been generated. I return below.</p>
<pre class="python python">cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep</pre>
<p>And here are the small lines that does everything. You'll see, it is very simple.</p>
<p>So, what are we trying to do with this?</p>
<p>Let's suppose the desired size cubes is 2.0.</p>
<p>It comes across a vertex set with a X value of 10.2. Of course, we will not put our cube in the center of the vertex (10.2). We would not get the desired effect at all. :nannan:</p>
<p>We need the exact position of the cube. We must "count the number of cube."</p>
<p>How do I know what is the 10.2 distance in cube size 2.0? By simply: 10.2/2.0 = 5.1.</p>
<p>As we cann't have 5.1 cubes, we round using the round() function. In the case of round(5.1), we have 5 (5.7 would give 6).</p>
<p>So now we know that if we create a cube of size 2.0, you should move it 5 times its size to make it emcompass the vertex. We then multiply the rounded value (5) by the size of a cube (2) to obtain a new position: The position of the cube, not wedged on the vertex but keyed to the voxel grid.</p>
<p>And voila! Now you know! :laClasse:</p>
<p>We did this for the three axes.</p>
<pre class="python python">cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>Here, we create a "hash" (a string) of the cube position and we store it in a list. That way, if we fall on a vertex which, once rounded, is in the same places than an already existing cube, it does not create it (no duplicates! :hehe:).</p>
<p>Although method seems <del>a little</del> very odd (convert vertex positions to string), I felt it was the easiest way to manage a unknown sized grid without too many lines of code.</p>
<p>But if you have another one short and quick to implement, don't hesitate to share. : D</p>
<pre class="python python">myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<p>After all of this, everything is simple:</p>
<ul>
<li>We create our cube from it desired size (2.0).</li>
<li>We applied a bevel on it because it looks good. :smileFou:</li>
<li>We parent it to the group.</li>
<li>We place it to the calculated position.</li>
</ul>
<p>And start again for another vertex!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_001.png" alt="voxel_maya_api_001.png" style="display:block; margin:0 auto;" title="voxel_maya_api_001.png, mar. 2013" height="387" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_002.png" alt="voxel_maya_api_002.png" style="display:block; margin:0 auto;" title="voxel_maya_api_002.png, mar. 2013" height="598" width="616" /></p>
<p>Again: This script is not optimized at all, it is a rough prototype for training purpose, not a production tool. Just give it a heavy mesh to realize. :mechantCrash:</p>
<h3>Conclusion</h3>
<p>This express ticket is over.</p>
<p>As you see, principle is quite simple. Well, once again we could have done this differently and probably more effective (try with <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=a5b3f852a00f3dc096c136cbe04057733" hreflang="en">MFnMesh.allIntersections</a>).</p>
<p>Personally, playing with the Maya API always amuses me so much. :)</p>
<p>See you!</p>
<center>:marioCours:</center>
<h3>EDIT 2013/03/17</h3>
<p>I couldn't resist to try <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.allIntersections()</a>. There is a well more optimized version (with acceleration structure <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.autoUniformGridParams()</a>).</p>
<p>The main difference between the previous one and this one is we don't go through each vertex anymore but we project rays through a grid instead (X, Y, Z).</p>
<p>The second difference is the code work on a animated mesh (1 to 24 here). You should test, the effect is cool. :)</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</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>
startFrame = <span style="color: #ff4500;">1</span>
endFrame = <span style="color: #ff4500;">24</span>
voxelSize = <span style="color: #ff4500;">20.0</span>
voxelStep = <span style="color: #ff4500;">0.5</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
grpReelNames = <span style="color: #008000;">dict</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
grpName = <span style="color: #483d8b;">"frameGrp_%s"</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">int</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
grpReelName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>name=grpName, empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime-<span style="color: #ff4500;">0.1</span><span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">1.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime+<span style="color: #ff4500;">1</span><span style="color: black;">]</span><span style="color: black;">)</span>
grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span> = grpReelName
<span style="color: #ff7700;font-weight:bold;">for</span> grpReelName <span style="color: #ff7700;font-weight:bold;">in</span> grpReelNames :
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">cmds</span>.<span style="color: black;">objExists</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">delete</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#I use while just because xrange with floats is impossible</span>
i = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> i <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
j = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> j <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
<span style="color: #ff7700;font-weight:bold;">for</span> axis <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">[</span><span style="color: #483d8b;">"zSide"</span>, <span style="color: #483d8b;">"ySide"</span>, <span style="color: #483d8b;">"xSide"</span><span style="color: black;">]</span> :
z = <span style="color: #ff4500;">0</span>
y = <span style="color: #ff4500;">0</span>
x = <span style="color: #ff4500;">0</span>
zOffset = <span style="color: #ff4500;">0</span>
zDir = <span style="color: #ff4500;">0</span>
yOffset = <span style="color: #ff4500;">0</span>
yDir = <span style="color: #ff4500;">0</span>
xOffset = <span style="color: #ff4500;">0</span>
xDir = <span style="color: #ff4500;">0</span>
<span style="color: #ff7700;font-weight:bold;">if</span> axis == <span style="color: #483d8b;">"zSide"</span> :
x = i
y = j
zOffset = <span style="color: #ff4500;">10000</span>
zDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"ySide"</span> :
x = i
z = j
yOffset = <span style="color: #ff4500;">10000</span>
yDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"xSide"</span> :
y = i
z = j
xOffset = <span style="color: #ff4500;">10000</span>
xDir = -<span style="color: #ff4500;">1</span>
raySource = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> x+xOffset, y+yOffset, z+zOffset <span style="color: black;">)</span>
rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatVector</span><span style="color: black;">(</span>xDir, yDir, zDir<span style="color: black;">)</span>
faceIds=<span style="color: #008000;">None</span>
triIds=<span style="color: #008000;">None</span>
idsSorted=<span style="color: #008000;">False</span>
space=<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span>
maxParam=<span style="color: #ff4500;">99999999</span>
testBothDirections=<span style="color: #008000;">False</span>
accelParams=inMesh.<span style="color: black;">autoUniformGridParams</span><span style="color: black;">(</span><span style="color: black;">)</span>
sortHits=<span style="color: #008000;">False</span>
hitPoints = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
hitRayParam=<span style="color: #008000;">None</span>
hitFacePtr = <span style="color: #008000;">None</span><span style="color: #808080; font-style: italic;">#OpenMaya.MScriptUtil().asIntPtr()</span>
hitTriangle=<span style="color: #008000;">None</span>
hitBary1=<span style="color: #008000;">None</span>
hitBary2=<span style="color: #008000;">None</span>
hit = inMesh.<span style="color: black;">allIntersections</span><span style="color: black;">(</span>raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
space,
maxParam,
testBothDirections,
accelParams,
sortHits,
hitPoints,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> hit :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #808080; font-style: italic;"># for each interestected points</span>
<span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>hitPoints.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
j += voxelStep
i += voxelStep</pre>
<center><i>Sorry for the horizontal code. :dentcasse:</i></center>
<p>I don't explain this version because I think if you understood the previous one, you shouldn't have too much problems with this one. :gniarkgniark:</p>
<h3>EDIT 2013/03/19</h3>
<p>Justin Israel <a href="https://groups.google.com/d/msg/python_inside_maya/1pYjnkTB5l0/JUFgz8LSD-gJ" hreflang="en">caught my attention</a> about my <em>voxelIdList</em>. I've learn something so I share his message with you:</p>
<p>If you are using it as a lookup for the hash of previously seen items, using a list is going to be progressively slower and slower over time as the list grows, because doing "x in list" is O(n) complexity. You might want to use a set():</p>
<pre class="python python">voxelIdSet = <span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: black;">)</span>
...
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdSet :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
voxelIdSet.<span style="color: black;">add</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>A set is O(1) complexity, so doing "x in set" will instantly find the item by its hash, as opposed to have to scan the entire list looking for an equality match.</p>Voxelisation polygonale via l'API Python Mayaurn:md5:d298e12473f80efc972d74e54de4a4ed2013-03-16T12:04:00+01:002013-07-26T17:59:21+02:00NarannScript et codeapifrgeometriemayapythonvoxel<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray_tn.png" alt="voxel_vray_tn.png" style="float:left; margin: 0 1em 1em 0;" title="voxel_vray_tn.png, mar. 2013" height="150" width="150" />Et encore un petit script prétexte à utilisation de l'API Maya. :sauteJoie:</p>
<p>L’idée est de reproduire l'effet de géométrie en voxel que vous avez peut être <a href="http://www.bilderzucht.de/blog/3d-pixel-voxel/" hreflang="en">déjà observe</a>.</p>
<p>Rien de bien méchant et ça existe surement déjà et en plus rapide. Pour tout vous dire, je n'ai même pas cherché. Le but est encore une fois d'utiliser l'API. :baffed:</p> <h3>Principe</h3>
<p>Avant de commencez, sachez que ce problème est un cas d’école. Il y a mille et une façons de le résoudre, chaque méthode ayant ces avantages et ces inconvénients. :reflechi:</p>
<p>La méthode choisi ici est la plus simple: Pour chaque vertex de la géométrie, on trouve la position "arrondi au cube près" et on génère un cube.</p>
<p>La seule difficulté de cette implémentation est, une fois la position du vertex récupéré, de savoir ou doit être le centre du cube. :seSentCon:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray.png" title="voxel_vray.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/.voxel_vray_m.jpg" alt="voxel_vray.png" style="display:block; margin:0 auto;" title="voxel_vray.png, mar. 2013" height="420" width="560" /></a></p>
<h3>Le code</h3>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</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>
voxelStep = <span style="color: #ff4500;">1.0</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span>
grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><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;">xrange</span><span style="color: black;">(</span>pointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<h3>Les explications</h3>
<pre class="python python">sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Fondamentalement, une <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_selection_list.html,topicNumber=cpp_ref_class_m_selection_list_html" hreflang="en">MSelectionList</a> est une liste de MObject.</p>
<p>Je n'ai jamais vraiment compris ce que la notion de "sélection" voulait dire avec cet objet car il ne "sélectionne" rien. :bete:</p>
<p>La particularité d'une <em>MSelectionList</em> c'est de pouvoir récupérer le <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject</a> d'un node Maya depuis son nom, ce qu'on fait plus loin.</p>
<pre class="python python">dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>C'est toujours assez difficile d'expliquer ce qu'est le DAG de Maya et encore plus un DAG path. :gne:</p>
<p>Pour faire un gros résumé:</p>
<ul>
<li>DAG: C'est la "hiérarchie" (parent/enfant) de Maya.</li>
<li>DAG path: C'est le "chemin" d'un node en passant par la hiérarchie (et donc, en ayant toutes ces transformations).</li>
</ul>
<p>Beaucoup de fonctions de l'API Maya nécessitent un DAG path pour pouvoir fonctionner.</p>
<p>Par exemple, on ne peut récupérer les coordonnées dans l'espace des vertices d'un node de shape si on ne connait pas le chemin par lequel on y arrive. Deux instances ont un seul node de shape mais il s'agit bien de deux DAG path différents et les coordonnes dans l’espace du même vertex auront deux valeurs possibles suivant "par ou on passe".</p>
<pre class="python python">sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span></pre>
<p>On ajoute l'object Maya "pSphere1" dans la <em>MSelectionList</em> et on récupère son DAG path (le zéro étant l'index dans la liste de sélection).</p>
<p>Nous avons donc "converti" "pSphere1" (qui ne veut rien dire en API Maya) en vrai <em>MObject</em>.</p>
<pre class="python python">inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span></pre>
<p>Par défaut, tout ce qu'on récupère dans l'API ce sont des <em>MObjects</em>. En général, on vérifie le type du <em>MObject</em> via un <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject.apiType()</a> ou <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_dag_node.html,topicNumber=cpp_ref_class_m_fn_dag_node_html" hreflang="en">MObject.hasFn()</a></p>
<p>Mais là on part du principe que le user a bien donné une mesh. :siffle:</p>
<p>La fonction <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MFnMesh</a> permet d'avoir à disposition un "vrai" objet (en terme programmation j'entend) sur lequel on va pouvoir agir pour récupérer des informations. C'est un <em>function set</em>.</p>
<p>Je vous invite a lire <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">la documentation</a> afin de comprendre le comment du pourquoi des <em>function sets</em>.</p>
<p>Nous avons donc un object _inMesh_ qui va nous servir a récupérer les vertices de notre mesh.</p>
<pre class="python python">pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span></pre>
<p>La première ligne créée un tableau de point <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html" hreflang="en">MPointArray</a> pour accueillir les positions des vertices de notre mesh.</p>
<p>Et la second ligne remplit le tableau avec les coordonne des vertices en espace monde (position par rapport au centre de la scène, non par rapport au centre de l'objet lui même).</p>
<pre class="python python">grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span></pre>
<p>Ici on ne fait que créer un groupe vide qui accueillera les cubes que nous allons créer par la suite. C'est juste qu'il est plus pratique de supprimer un groupe avec tout dedans que de sélectionner tous les cubes à la pogne. :dentcasse:</p>
<pre class="python python">voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>On créé une liste qui servira a stocker les identifiant (ou code) des zones ou les cubes on déjà été générés. J'y reviens plus bas.</p>
<pre class="python python">cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep</pre>
<p>Et voici la petite ligne qui fait tout. Vous allez voir, elle est très simple.</p>
<p>Alors, qu'est ce qu'on cherche à faire avec ça?</p>
<p>On va partir du principe que la taille voulue des cubes fait 2.0.</p>
<p>On tombe sur un vertex positionné en X à 10.2. Bien entendu, on ne va pas placer notre cube au centre du vertex (à 10.2). On n'obtiendrais pas du tout l'effet voulu. :nannan:</p>
<p>Il nous faut la position exacte du cube. Il faut "comptez en nombre de cube".</p>
<p>Comment savoir à quoi correspond la distance 10.2 en cube de taille 2.0? En faisant tout simplement: 10.2/2.0. Soit 5.1.</p>
<p>Comme on ne peut pas avoir 5.1 cubes, on arrondi via la fonction round(). Dans le cas de round(5.1), on obtient 5 (5.7 aurait donne 6).</p>
<p>On sait donc maintenant que si on crée un cube de taille 2.0, il faut le déplacer de 5 fois sa taille pour qu'il englobe le vertex. On multiplie donc la valeur arrondi (5) par la taille d'un cube (2) pour obtenir une nouvelle position: La position du cube, non plus calée sur le vertex mais calée sur la grille du voxel.</p>
<p>Et voila, vous avez capté! :laClasse:</p>
<p>On fait ça pour les trois axes.</p>
<pre class="python python">cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>Ici, on créé une "signature" (en string) de la position du cube et on la stock dans une liste. Comme ça, si on retombe sur un vertex qui, une fois arrondi, se retrouve au même endroits qu'un cube déjà existant, on ne le crée pas (pas de doublons! :hehe:).</p>
<p>Bien que cette manière de faire <del>semble un peu</del> est très bizarre (convertir les positions des vertex en string), j'ai eu l'impression que c’était la méthode la plus simple pour gérer une grille dont on ne connait pas la taille sans trop de lignes de code.</p>
<p>Mais si vous en avez une autres assez rapide à mettre en place, n’hésitez pas. :D</p>
<pre class="python python">myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<p>Après c'est simple:</p>
<ul>
<li>On créé notre cube de la taille voulu (2.0).</li>
<li>On lui applique un bevel parce que ça rend bien. :smileFou:</li>
<li>On le parente au groupe.</li>
<li>On le place à la position calculée plus tôt.</li>
</ul>
<p>Et on repart pour un autre vertex!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_001.png" alt="voxel_maya_api_001.png" style="display:block; margin:0 auto;" title="voxel_maya_api_001.png, mar. 2013" height="387" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_002.png" alt="voxel_maya_api_002.png" style="display:block; margin:0 auto;" title="voxel_maya_api_002.png, mar. 2013" height="598" width="616" /></p>
<p>Encore une fois: Ce script n'est pas optimisé du tout, c'est plus un prototype grossier qu'un outil de prod. Il suffit de lui donner un mesh un peu lourd pour s'en rendre compte. :mechantCrash:</p>
<h3>Conclusion</h3>
<p>Ainsi s’achève ce billet express.</p>
<p>Vous avez vue, c'est assez bête dans le principe. Bon, encore une fois on aurait pu faire différemment et surement plus efficace (essayez avec <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=a5b3f852a00f3dc096c136cbe04057733" hreflang="en">MFnMesh.allIntersections()</a>).</p>
<p>Personnellement, jouer avec l'API Maya m'amuse toujours autant. :)</p>
<p>A bientôt!</p>
<center>:marioCours:</center>
<h3>EDIT 2013/03/17</h3>
<p>Je n'ai pas pu résister à l'appel de la méthode <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.allIntersections()</a>. Voici donc une version bien plus optimisé que précédemment (avec structure d’accélération <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.autoUniformGridParams()</a>).</p>
<p>La principale différence comparé au code précédent est qu'ici nous ne passons plus par chaque vertex mais nous envoyons des rayon sur trois grille (X, Y, Z).</p>
<p>La seconde différence est que ce code fonctionne sur un mesh animé (1 a 24 ici). Testez, l'effet est assez sympa. :)</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</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>
startFrame = <span style="color: #ff4500;">1</span>
endFrame = <span style="color: #ff4500;">24</span>
voxelSize = <span style="color: #ff4500;">20.0</span>
voxelStep = <span style="color: #ff4500;">0.5</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
grpReelNames = <span style="color: #008000;">dict</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
grpName = <span style="color: #483d8b;">"frameGrp_%s"</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">int</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
grpReelName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>name=grpName, empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime-<span style="color: #ff4500;">0.1</span><span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">1.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime+<span style="color: #ff4500;">1</span><span style="color: black;">]</span><span style="color: black;">)</span>
grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span> = grpReelName
<span style="color: #ff7700;font-weight:bold;">for</span> grpReelName <span style="color: #ff7700;font-weight:bold;">in</span> grpReelNames :
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">cmds</span>.<span style="color: black;">objExists</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">delete</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#I use while just because xrange with floats is impossible</span>
i = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> i <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
j = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> j <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
<span style="color: #ff7700;font-weight:bold;">for</span> axis <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">[</span><span style="color: #483d8b;">"zSide"</span>, <span style="color: #483d8b;">"ySide"</span>, <span style="color: #483d8b;">"xSide"</span><span style="color: black;">]</span> :
z = <span style="color: #ff4500;">0</span>
y = <span style="color: #ff4500;">0</span>
x = <span style="color: #ff4500;">0</span>
zOffset = <span style="color: #ff4500;">0</span>
zDir = <span style="color: #ff4500;">0</span>
yOffset = <span style="color: #ff4500;">0</span>
yDir = <span style="color: #ff4500;">0</span>
xOffset = <span style="color: #ff4500;">0</span>
xDir = <span style="color: #ff4500;">0</span>
<span style="color: #ff7700;font-weight:bold;">if</span> axis == <span style="color: #483d8b;">"zSide"</span> :
x = i
y = j
zOffset = <span style="color: #ff4500;">10000</span>
zDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"ySide"</span> :
x = i
z = j
yOffset = <span style="color: #ff4500;">10000</span>
yDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"xSide"</span> :
y = i
z = j
xOffset = <span style="color: #ff4500;">10000</span>
xDir = -<span style="color: #ff4500;">1</span>
raySource = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> x+xOffset, y+yOffset, z+zOffset <span style="color: black;">)</span>
rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatVector</span><span style="color: black;">(</span>xDir, yDir, zDir<span style="color: black;">)</span>
faceIds=<span style="color: #008000;">None</span>
triIds=<span style="color: #008000;">None</span>
idsSorted=<span style="color: #008000;">False</span>
space=<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span>
maxParam=<span style="color: #ff4500;">99999999</span>
testBothDirections=<span style="color: #008000;">False</span>
accelParams=inMesh.<span style="color: black;">autoUniformGridParams</span><span style="color: black;">(</span><span style="color: black;">)</span>
sortHits=<span style="color: #008000;">False</span>
hitPoints = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
hitRayParam=<span style="color: #008000;">None</span>
hitFacePtr = <span style="color: #008000;">None</span><span style="color: #808080; font-style: italic;">#OpenMaya.MScriptUtil().asIntPtr()</span>
hitTriangle=<span style="color: #008000;">None</span>
hitBary1=<span style="color: #008000;">None</span>
hitBary2=<span style="color: #008000;">None</span>
hit = inMesh.<span style="color: black;">allIntersections</span><span style="color: black;">(</span>raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
space,
maxParam,
testBothDirections,
accelParams,
sortHits,
hitPoints,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> hit :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #808080; font-style: italic;"># for each interestected points</span>
<span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>hitPoints.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
j += voxelStep
i += voxelStep</pre>
<center><i>Désolé pour le code horizontal. :dentcasse:</i></center>
<p>Je ne fais pas de commentaires supplémentaires car je pense que si vous avez compris le premier code, celui ci est tout aussi simple. :gniarkgniark:</p>
<h3>EDIT 2013/03/19</h3>
<p>Justin Israel a fait <a href="https://groups.google.com/d/msg/python_inside_maya/1pYjnkTB5l0/JUFgz8LSD-gJ" hreflang="en">une remarque très pertinante</a> concernant ma <em>voxelIdList</em>. J'ai appris un truc ducoup. Je vous traduis son message ici:</p>
<p>Si tu utilise <em>voxelIdList</em> pour chercher la signature d'un cube déjà placé, le fait que ce soit une liste va progressivement ralentir la recherche au fil qu'elle se remplit, car <em>x in list</em> est de complexité O(n). Tu devrais utiliser un <em>set()</em>:</p>
<pre class="python python">voxelIdSet = <span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: black;">)</span>
...
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdSet :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
voxelIdSet.<span style="color: black;">add</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>Un type set est de complexité O(1) donc faire un <em>x in set</em> va instantanément trouver l'item depuis sa signature, à l'inverse d'une liste qu'une faut parcourir entièrement.</p>Quickly retrieve vertex positions of a Maya mesh (English Translation)urn:md5:5d032309b99261355833f3413e46f8432011-09-27T22:16:00+02:002013-07-26T18:05:41+02:00NarannScript et codeapienmayameshpythonvertexxform<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" />If you ever had to recover vertex positions of a mesh object, you probably crash yourself against the <em>xform</em> command and his legendary slowness. :baffed:</p>
<p>In this post, I purpose a piece of Python script using the Python Maya API that allows you to faster recover the list of all vertex positions of an object.</p>
<p>Primarily didactic, these codes may interest peoples who want to look a little more deeply how to use the API while having a concrete application. :laClasse:</p> <h3>Using xform</h3>
<p>Here is the <a href="http://download.autodesk.com/us/maya/2011help/Commands/xform.html">xform</a> version of the script:</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;"># will contain positions un space of all object vertex</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>With Maya API</h3>
<p>Alternative to 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>Notice: I thought that specifying the size of the list from the start would gain time but it changes nothing. It seems that <a href="http://stackoverflow.com/questions/311775/python-create-a-list-with-initial-capacity">Python handles it well</a>. So, I learned a new saying: <em>Premature optimization is the root of all evil</em>. :gniarkgniark:</p></blockquote>
<h3>Benchmarks</h3>
<p>Well, it's fine to say it's faster. But how much? :reflexionIntense:</p>
<p>Here are times I have with a 50x50 pSphere smoothed 4 (633602 vertices):</p>
<ul>
<li>With xform: 39 sec</li>
<li>With Python Maya API: 4 sec</li>
</ul>
<p>10x faster! This would give me almost want to code in C++, just to see results (which is suppose to be 10x faster than Python!)!!! :grenadelauncher:</p>
<h3>Conclusion</h3>
<p>Well now it's up to you to know when use it.</p>
<p>You can try to optimize <a href="https://www.fevrierdorian.com/blog/post/2011/03/14/Remplir-un-mesh-de-spheres-dans-Maya-La-methode-d-un-senior">Djelloul's algorythm</a> and let me know your results! :sourit:</p>
<p>Now, I couldn't do without the API if I have to retrieve vertex positions of a mesh. It's much faster. Even if it's a little more complicate, I admit, but when you have this code, you keep it and use it at the right time! :aupoil:</p>
<p>Hope you like this trick!</p>
<p>See you soon!</p>
<center>:marioCours:</center>
Project 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>API Python de Maya, les choses à savoirurn:md5:24e12e2c1c594c4af88d13ae6c99b9ca2010-10-04T22:27:00+02:002013-07-26T18:13:07+02:00NarannCrashs et bugsapicodecrashfrmayapython<p><img src="https://www.fevrierdorian.com/blog/public/logos/pythonVsMaya_tn150.png" alt="pythonVsMaya_tn150.png" style="float:left; margin: 0 1em 1em 0;" title="pythonVsMaya_tn150.png, oct. 2010" height="150" width="150" />Étant de plus en plus amené à utiliser l'API Python de Maya dans le cadre de mon travail, je remarque que beaucoup de choses ne fonctionnent pas très bien, voir, pas du tout...</p>
<p>L'implémentation Python (qui à au moins le mérite d'exister et d'être très pratique) est pas mal buggé... On se demande souvent si la faute vient de nous. Mais c'est une fois qu'on a réussi à isoler le bug et à trouver un workaround qu'on se rend compte que le problème n'était pas due (qu')à notre incompétence... :sourit:</p> <p>Autodesk s'efforce d'améliorer son API Python au fil des versions (C'est d'ailleurs très dommageable, en prod, de devoir attendre une nouvelle version pour corriger des bugs d'API...).</p>
<p>La plupart des utilisateurs de l'API Python n'étant pas des développeurs (qui eux bossent, en général, en C++) mais plutôt des graphistes avancés, TD, ils leur arrive souvent de "douter".</p>
<p>L'idée, dans ce billet, est de parler des limitation de l'API Python de Maya que j'ai pu rencontré et qui m'ont vraiment bloqué. Le but étant que vous ne restiez pas vous même bloqué sur des trucs bêtes, comme j'ai pu l'être.</p>
<p>Ce billet ne se veut pas exhaustif et est bien entendu destiné au personnes qui connaissent un temps soit peu l'API. N'hésitez pas à laisser un commentaire si vous rencontrez d'autres bugs ainsi que leur workaround. :siffle:</p>
<p>On commence!</p>
<center>:youplaBoum:</center>
<center>:longBar:</center>
<h5>Sommaire</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/04/API-Python-de-Maya%2C-les-choses-%C3%A0-savoir#les_mfloat_Matrix_array_point_etc_v2010">Les MFloat(Matrix, Array, Point, etc...) (v2010)</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/04/API-Python-de-Maya%2C-les-choses-%C3%A0-savoir#les_probleme_de_typage_avec_les_array_v2010">Les problème de typage avec les Array (v2010)</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2010/10/04/API-Python-de-Maya%2C-les-choses-%C3%A0-savoir#fin_et_suite">Fin et suite</a></li>
</ul>
<center>:longBar:</center>
<h5>Les MFloat(Matrix, Array, Point, etc...) (v2010) <a name="les_mfloat_Matrix_array_point_etc_v2010"></a></h5>
<blockquote><p>Pour éviter de retaper à chaque fois: MFloat(Matrix, Array, Point, etc...) et M(Matrix, Array, Point, etc..) dans mes exemples, je prendrais la formulation MType et MFloatType.</p></blockquote>
<p>Un des premier point sur lequel j'ai "buté" est la conversion de l'objet Maya: MFloatMatrix et tout les objets de type MFloat(Matrix, Array, Point, etc..) en leur équivalent M(Matrix, Array, Point, etc..)</p>
<p>En effet, certaines méthodes et constructeurs de classes ne demande, en entré, un MType. Si on n'a pas été assez vigilant, on se retrouve avec des MFloatMatrix dans notre code python et bien entendu, si vous passez un MFloatMatrix à la place d'un MMatrix, ça vous envoi balader...</p>
<p>Ce "double typage" des objets Maya est fait afin de donner facilement un équivalent aux de type de nombre flottant en C:</p>
<ul>
<li>Double (8 octets, le "format" par défaut de Maya)</li>
<li>Float (4 octets)</li>
</ul>
<p>Autant la conversion de MType à MFloatType est assez simple en C++ (voir <a href="http://forums.cgsociety.org/showthread.php?t=598626" hreflang="en">ce billet</a>) autant en Python c'est beaucoup plus laborieux.</p>
<p>La solution la plus simple que je vous conseil est donc de bannir les MFloatType autant que possible en Python. Je n'ai pas vraiment trouvé de solution pour convertir les deux type en Python (mais c'est surement possible :baffed: ).</p>
<p>EDIT 2010 10 08: Sur <a href="http://zoomy.net/2009/07/31/fastidious-python-shrub/" hreflang="en">ce billet</a> <a href="http://zoomy.net/about/" hreflang="en">Peter J. Richardson</a> propose une solution (qui est en fait très simple :pasClasse: ):</p>
<pre class="python python">myMPoint = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPoint</span><span style="color: black;">(</span><span style="color: black;">)</span>
myMFloatPoint = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> myMPoint<span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>, myMPoint<span style="color: black;">[</span><span style="color: #ff4500;">1</span><span style="color: black;">]</span>, myMPoint<span style="color: black;">[</span><span style="color: #ff4500;">2</span><span style="color: black;">]</span> <span style="color: black;">)</span></pre>
<p>Je l'ai testé et ça fonctionne très bien! J'ai un peu honte de ne pas l'avoir trouvé moi même... (Je pourrais tenter de m'excuser lamentablement en disant que quand on se met à tripatouiller l'API de Maya, les choses sont plus simple si on n'en "sort pas" :baffed: ).</p>
<p>EDIT 2011 02 11: La méthode ci dessus n'est pas applicable pour une <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_matrix.html,topicNumber=cpp_ref_class_m_matrix_html" hreflang="en">MMatrix</a>. Vous pouvez utiliser la classe <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_script_util.html,topicNumber=cpp_ref_class_m_script_util_html" hreflang="en">MScriptUtil</a> qui permet de créer une <em>MMatrix</em> ou <em>MFloatMatrix</em> depuis une liste (un tuple plutôt).</p>
<p>Voici un code qui permet de convertir une <em>MFloatMatrix</em> en <em>MMatrix</em>:</p>
<pre class="python python">myFloatMatrix = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatMatrix</span><span style="color: black;">(</span><span style="color: black;">)</span>
myMatrix = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MMatrix</span><span style="color: black;">(</span><span style="color: black;">)</span>
floatList = <span style="color: black;">(</span>myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">0</span>,<span style="color: #ff4500;">0</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">0</span>,<span style="color: #ff4500;">1</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">0</span>,<span style="color: #ff4500;">2</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">0</span>,<span style="color: #ff4500;">3</span><span style="color: black;">)</span>,
myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">1</span>,<span style="color: #ff4500;">0</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">1</span>,<span style="color: #ff4500;">1</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">1</span>,<span style="color: #ff4500;">2</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">1</span>,<span style="color: #ff4500;">3</span><span style="color: black;">)</span>,
myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">2</span>,<span style="color: #ff4500;">0</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">2</span>,<span style="color: #ff4500;">1</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">2</span>,<span style="color: #ff4500;">2</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">2</span>,<span style="color: #ff4500;">3</span><span style="color: black;">)</span>,
myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">3</span>,<span style="color: #ff4500;">0</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">3</span>,<span style="color: #ff4500;">1</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">3</span>,<span style="color: #ff4500;">2</span><span style="color: black;">)</span>, myFloatMatrix<span style="color: black;">(</span><span style="color: #ff4500;">3</span>,<span style="color: #ff4500;">3</span><span style="color: black;">)</span><span style="color: black;">)</span>
<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;">createMatrixFromList</span><span style="color: black;">(</span>floatList, myMatrix<span style="color: black;">)</span></pre>
<p>Ne me sautez pas a la gorge de suite, c’était juste pour l'exemple :grenadelauncher: . Il y a un moyen beaucoup plus rapide de faire ça:</p>
<pre class="python python">myFloatMatrix = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatMatrix</span><span style="color: black;">(</span><span style="color: black;">)</span>
myMatrix = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MMatrix</span><span style="color: black;">(</span>myFloatMatrix.<span style="color: black;">matrix</span><span style="color: black;">)</span></pre>
<p>Il faut en effet utiliser l'attribut <em>.matrix</em>. Le résultat des deux méthodes est le même. Juste qu'il y en a une chiante a écrire et l'autre en deux lignes. :seSentCon:</p>
<center>:longBar:</center>
<h5>Les problème de typage avec les Array (v2010) <a name="les_probleme_de_typage_avec_les_array_v2010"></a></h5>
<p>Dans l'API, un MPointArray est une classe tableau de MPoint (logique!). L'opérateur <a href="https://www.fevrierdorian.com/blog/post/2010/10/04/"></a> (crochet) permet, de récupérer rapidement un MPoint dans ce tableau. (relogique!)</p>
<p>En C++, ça doit sûrement marcher. :gne: Mais en Python c'est une autre histoire.</p>
<p>Comme rien ne vaut mieux qu'un exemple:</p>
<pre class="python python">myMPointTmp = myMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span></pre>
<p>Ceci fera un segmentation fault! (Le truc qui quitte Maya instantanément :siffle: ).</p>
<p>Après m'être arraché les cheveux, là dessus, il se trouve que la solution est assez... "Spéciale". Jugez par vous même:</p>
<pre class="python python">inMeshMPointTmp = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPoint</span><span style="color: black;">(</span> inMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span> <span style="color: black;">)</span></pre>
<p>Ceci marchera impeccable! En gros, il faut ré-encapsuler ce qu'on récupère d'un Array Maya dans un objet de son type. :aupoil:</p>
<p>Faut avouez que c'est assez pathétique comme méthode... Mais bon.</p>
<p>Il faut savoir que la plupart des soucis de segmentation fault avec l'API Python sont due à ce problème de typage (Autodesk n'a pas complètement fini son travail on va dire. :seSentCon: )</p>
<p>N'hésitez donc pas, quand vous avez ce genre de soucis (crash sans raisons apparentes) à ré-encapsuler toutes vos variables. ça résout quelques soucis.</p>
<center>:longBar:</center>
<h5>Fin et suite <a name="fin_et_suite"></a></h5>
<p>Je continuera à ajouter des "choses bizarres" que je trouve dans l'API Python au fil du temps.</p>
<p>N'hésitez surtout pas à me laisser un commentaire si vous rencontrez, vous aussi, quelque chose de "tricky"! (Je sais que les MString font aussi de jolies choses! :hihi: )</p>
<p>A bientôt!</p>
<p>Dorian</p>Creating Custom Locator with Maya's Python API (English Translation)urn:md5:c82400507ef5dded320bdb670f4eacfb2010-02-12T22:36:00+01:002013-07-26T22:32:05+02:00NarannScript et codeapicppenmayamelopenglpythonscripttranslation<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocator001.png" alt="pythonLocator001.png" style="float:left; margin: 0 1em 1em 0;" title="pythonLocator001.png, mar. 2009" height="150" width="150" />This post is a english translation of <a href="https://www.fevrierdorian.com/blog/post/2009/04/15/Cr%C3%A9er-un-Custom-Locator-avec-l-API-Python-de-Maya">this post</a>. I'd wrote it after post <a href="http://forums.cgsociety.org/showthread.php?t=845916" hreflang="en">this on CGTalk</a>. Great thanks to Daisy C. Lyle. She translate all the french version to english and make a great work! She save me hours doing this! :sourit: . Hope that this tutorial will be usefull.</p> <p>As <a href="https://www.fevrierdorian.com/blog/index.php?post/2009/03/31/Python%2C-Maya-et-sont-API...">I mentioned before</a>, I’m starting to use the Maya API Python binding. So I had a look at <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/mayaapi.html#2" hreflang="en">Rob Bateman’s</a> sources (which I found incomprehensible a year ago) and “translated” them (not without difficulty) into Python…So I’ve created a little script with a custom locator. It’s obviously not the simplest thing in the world to begin with (I did have some grounding thanks to <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/maya/mfn_traversing.htm" hreflang="en">other tutorials</a>, the Python “plug-ins” already incorporated into Maya and the OpenGL tutorials I’d done) but once the code is up and running, it’s quite fun to modify it to make your own locator…</p>
<p>So, on the menu we have:</p>
<ul>
<li>OpenGL (Well, how else are you going to draw your locator? :hehe: )</li>
<li>Color change as a function of the selection state of the locator.</li>
</ul>
<p>It’s not much but you’ll see it’s enough to be getting on with!</p>
<center>:longBar:</center>
<h5>Scripting is pointless, it’s for newbies!</h5>
<p>No! If you think that, then you haven’t understood anything. The two are closely related. If I chose the example of the locator, it’s not just because it’s “cool” but also because it’s an example of a thing it is only possible to do with the API…</p>
<p>But the API doesn’t do everything! :cayMal:</p>
<p>As you doubtless already know, the Maya interface is entirely written in MEL, which allows a scripter to quickly create a GUI for his or her scripts. Not all software packages offer this (did anyone say XSI?) and Maya is one of the few that boasts it.</p>
<p>Most Maya function calls are also scripted. This means you can “batch” your operations (when you have 50 scenes to open to change the value of an attribute, you won’t be laughing so hard! :hehe: )</p>
<p>I can only invite you to read <a href="http://www.creativecrash.com/forums/api/topics/new-to-the-api-then-read-this-33" hreflang="en">my mentor’s post</a> on the subject (displaying the C++ equivalent of a MEL code).</p>
<p>And finally: Python is a scripting language! Even if it can be “pre-compiled” by the interpreter, it’s still script.</p>
<p>That said, I think that this argument is a bit fruitless as even if you script using the Maya API Python binding, once the basics are understood you realize that the differences in syntax between a piece of C++ code and its equivalent in Python are minimal.</p>
<p>A little example of variable declaration:</p>
<center><I>C++</I></center>
<pre class="cpp cpp">MFnPlugin fnPlugin<span style="color: #008000;">(</span>obj<span style="color: #008000;">)</span><span style="color: #008080;">;</span></pre>
<center><I>Python</I></center>
<pre class="python python">fnPlugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span></pre>
<p>C++ is still the most user-friendly, I think (I’m not saying that because of this examples but in a more general way), which seems logical because that it's the historically used implementation, added to the fact that it remains a language fairly close to machine.</p>
<p>Then it all depends on whether or not you want to go fast. Compiled code (C++) will obviously go faster (about ten time) but I'm supposing that if you want to go fast, either you’re not good and you’re writing rotten code which leaves you no other solution than to resort to a lower-level language to speed it up, or you’re a “real” developer… If that’s the case, I don’t see what you’re doing on the blog of a Sunday coder! (Me! :aupoil: ).</p>
<p>What the Hell, one day I’m going to make a bench between MEL, pythonForMel, C++ and pythonForMayaApi :sourit: .</p>
<center>:longBar:</center>
<h5>Code basics</h5>
<p>Before we start messing around with the locator, we need the base code. Here we go!</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>Bang! Not acting so clever now are we? (I wasn’t acting clever the first time around either! :baffed: ).</p>
<p>Right, in fact it’s not very complicated. Explanation:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender</pre>
<p>Here we are importing the main modules. The first (“sys”) is the system module which we will use to write the plug-in initialization error messages (that’s all.)</p>
<p>The next three are Maya API Python modules. They have been divided into several modules in order to clarify their use:</p>
<ul>
<li>OpenMaya is the module for classes relating to node and command definitions and their “assembly” into plug-ins.</li>
<li>OpenMayaMPx is a Python-specific module. It contains the Maya’s proxy (or MPx class) bindings.</li>
<li>OpenMayaRender is the module for classes relating to render (hardware or software).</li>
</ul>
<pre class="python python">nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span></pre>
<p>This variable (I declare it at the beginning) will be used to give a name to the type of node you wish to create.</p>
<pre class="python python">nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span></pre>
<p>This variable (I also declare it at the beginning) will be used to give an ID (identifier) to our type of node.</p>
<pre class="python python">glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>This is where it gets complicated (especially for me.) This command is not referred to in the documentation (even though it appears in the example C++ codes.) This command makes it possible to retrieve a pointer to the class in charge of the render (hardware) of the viewports. It’s thanks to this that we are going to be able to “draw” our locator (using OpenGL commands.)</p>
<pre class="python python">glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The glFunctionTable() method returns a pointer to an object containing all the OpenGL instructions handled by Maya.</p>
<p>Now here we’re really getting stuck into OpenGL (well perhaps not quite, as you’re about to see :sourit: ):</p>
<center>:longBar:</center>
<h5>OpenGL, some informations</h5>
<p>What is OpenGL? (<a href="http://fr.wikipedia.org/wiki/OpenGL" hreflang="en">Wiki</a>) To summarize, it’s a way of communicating with the graphics card. It involves simple commands, e.g. to display points, lines, triangles, quads, textures, etc…</p>
<p>Before we go any further, we’re going to have to understand how this all works. If you want to “draw” locators, you have to know how to use the pen that is going to do it.</p>
<p>As a piece of code is worth more than a long lecture, here is how to “draw” a line in OpenGL (this is a C source. For Maya, we will see that there are two or three things you have to know first):</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>This technique is the “basic”, or “immediate mode” technique.</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span></pre>
<p>This command allows us to “go into primitive draw mode”(In a 3-D space.) We’re putting the pencil to the paper, if you like. The argument (GL_LINES) enables us to say how the following commands will be interpreted, inline here. Every two vertices, we write another line. One line per pair of vertices.</p>
<pre class="c c">glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span></pre>
<p>These two commands are the commands which “place” the vertices in space. The command is glVertex, the number is the number of arguments (here, three: X,Y and Z) and the last letter is the <a href="http://www.cplusplus.com/doc/tutorial/variables/" hreflang="en">data type</a> (here, “f” for float.)</p>
<p>Basically, we’re placing two vertices in space. And given that we have put ourselves into “line draw” mode (per vertex pair), we have just drawn a line.</p>
<pre class="c c">glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>And that’s just a way of “shutting down communications” with the graphics card and returning to the main program.</p>
<p>As you see, OpenGL isn’t as hard as all that! This was also how I understood that it’s the processor that calculates all the vertices’ positions in each image (well, that’s the old method; there are others, especially for static objects which are stored in the memory of the graphics card and called by a single instruction. Having said that, a large part of the work is done by the processor).</p>
<p>With this, you are almost ready to draw your own locator. I have just one point left to address.</p>
<center>:longBar:</center>
<h5>OpenGL, the Maya version! <a name="openGLVersionMaya"></a></h5>
<p>In fact, the code that I have shown you is a C program. I am now going to explain to you some of the few OpenGL peculiarities specific to Maya.</p>
<p>Maya, out of concern for interoperability (I suppose there are other reasons) has its own OpenGL implementation. So, when we wish to draw in the viewport, we don’t directly use the Windows or Mac implementation but in fact Maya’s own. (This can be done in C++, which makes it possible to dispense with the Maya wrapper, but you have to handle the interoperability in your code. It should also be possible to do this in Python but I haven’t tried). That doesn’t change much in the code but the constants (GL_LINES for instance) have another name.</p>
<p>As written in the Maya API documentation:</p>
<blockquote><p>The naming convention used in this file is to take the regular GL* definitions and add a "M" prefix</p></blockquote>
<p>Translation: “We put an ‘M’ in front of all the OpenGL constants”. :seSentCon:</p>
<p>So Maya won’t understand GL_LINES, only OpenMayaRender.MGL_LINES :baffed:</p>
<p>And here is how we write a line in OpenGL in Maya:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>So no big differences then ^^. Any experienced OpenGL user should be able to do it with their eyes closed!</p>
<center>:longBar:</center>
<h5>So shall we write this locator then?</h5>
<p>No! Not yet! There are still things to learn! After this little introduction to OpenGL, let’s go back to our Python script.</p>
<p>To be able to draw the locator, we have to derive the “draw” function (yes, because Maya allows you to derive functions…Not everyone can say as much, eh, XSI? :siffle: ). This function is used to…draw custom geometries using OpenGL functions.</p>
<p>Here is the basic code for the “draw” method:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The beginGL( ) and endGL( ) functions make it possible (well, this is my interpretation) to tell Maya that we are going to use OpenGL commands between them. According to the documentation, if they aren’t included in the right place, the program will exceed its allocated memory the program will therefore crash.</p>
<p>I’m deliberately going to forget a few functions for the moment, we’ll look at them later.</p>
<center>:longBar:</center>
<h5>Go Pawaaaah!</h5>
<p>So our code now looks like this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>You can download it here:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode001.7z">CustomLocatorNode001.7z</a></p>
<p>Load it (as a plug-in), and enter:</p>
<pre class="mel mel">createNode myCustomLocator<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocLineFirstLocator.png" alt="pythonLocLineFirstLocator.png" style="display:block; margin:0 auto;" title="pythonLocLineFirstLocator.png, avr. 2009" height="194" width="222" /></p>
<p>We’ve just made out first locator! :sourit: With this we can already do a few things… If you give it some elbow grease, you can get this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">random</span></pre>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode002.7z">CustomLocatorNode002.7z</a></p>
<p>You’ll have a wicked locator! ^^</p>
<p>Right, it’s all very well making lines but that’s not all there is to it… I suggest we do… a quad! :laClasse:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode003.7z">CustomLocatorNode003.7z</a></p>
<p style="font-style:italic; text-align:center;">Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuad.png" alt="pythonLocQuad.png" style="display:block; margin:0 auto;" title="pythonLocQuad.png, avr. 2009" height="276" width="405" /></p>
<p style="font-style:italic; text-align:center;">Paf le locator! <img class="smiley" alt=":baffed:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/baffed.gif"/></p>
<p>OK, it’s a bit flashy but we’re going to change that. :reflechi: We’re going to change the color and add transparency.</p>
<p>First, the color. As default, Maya uses the interface colors (blue when unselected, green when selected, pink when ‘templated’ etc….) To change the color, all you have to do is add the command glColor3f( )</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#on change la couleur</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode004.7z">CustomLocatorNode004.7z</a></p>
<p style="font-style:italic; text-align:center;">Re-Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColor.png" alt="pythonLocQuadColor.png" style="display:block; margin:0 auto;" title="pythonLocQuadColor.png, avr. 2009" height="342" width="507" /></p>
<p style="font-style:italic; text-align:center;">Re-Paf le locator! <img class="smiley" alt=":aupoil:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/aupoil.gif"/></p>
<p>Still just as flashy, huh? You’ll notice that only the line changes color as a function of the selection state. To ‘lighten up’ our locator a bit, we’re going to add transparency to our quad.</p>
<center>:longBar:</center>
<h5>The Alpha</h5>
<p>We are going to “enable” one of OpenGL's features: GL_BLEND (Don’t forget to disable it at the end):</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#We activate feature</span>
glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#Change color</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#Don't forget to unactivate in the end!</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>When this feature is enabled, OpenGL blends the given color with the resulting color (the background) according to a final factor (the Alpha.) If you run the code as it is, you won’t see any change. So we must add one last component to our color:</p>
<pre class="python python">glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#Change color and add alpha</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode005.7z">CustomLocatorNode005.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColorAlpha.png" alt="pythonLocQuadColorAlpha.png" style="display:block; margin:0 auto;" title="pythonLocQuadColorAlpha.png, avr. 2009" height="298" width="435" /></p>
<p style="font-style:italic; text-align:center;">Et voila! Better no? ;)</p>
<p>We are coming to the last part: the selection states.</p>
<center>:longBar:</center>
<h5>Selection</h5>
<p>In fact we can change aspects of our locator depending on its selection state. Here, we're going to change... The color! (very original... -_-).</p>
<p>First of all, there are several "states" of a "drawn" object (Listed in M3dView::DisplayStatus). I'm going to give you the two main ones, the ones we’re going to be using:</p>
<ul>
<li>kActive -> The active (selected) objects. (Be careful, this is tricky!)</li>
<li>kLead -> The last object selected.</li>
<li>kDormant -> The inactive objects.</li>
</ul>
<p>So, let’s explain the subtle difference::</p>
<p>In Maya, when you select an object, it becomes green. And when you add another object to the selection, this second object becomes green and the previous, white. In fact the green object is “in DisplayStatus” kLead, and the white object in kActive. Here’s a picture to explain (or remind you of?) the principle:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelectionState.png" alt="pythonLocSelectionState.png" style="display:block; margin:0 auto;" title="pythonLocSelectionState.png, avr. 2009" height="160" width="418" /></p>
<p>So we are going to be able to change the color as a function of the selection state.</p>
<p>As seen above, the derived function is draw, and the argument we use to know the state of the locator is "status". And nothing could be easier than using all this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaUI</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaUI <span style="color: #808080; font-style: italic;"># on top</span></pre>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kLead</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.3</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#rouge</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kActive</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#jaune</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kDormant</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#mauve</span></pre>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode006.7z">CustomLocatorNode006.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection001.png" alt="pythonLocSelection001.png" style="display:block; margin:0 auto;" title="pythonLocSelection001.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection002.png" alt="pythonLocSelection002.png" style="display:block; margin:0 auto;" title="pythonLocSelection002.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection003.png" alt="pythonLocSelection003.png" style="display:block; margin:0 auto;" title="pythonLocSelection003.png, avr. 2009" height="216" width="382" /></p>
<p>OK, it’s not that pretty but it works… :baffed:</p>
<p>I hope that this tutorial will shed some light on a few areas of the Python Maya API... Note however that I didn't choose the easiest thing to start with (if you know nothing about the API... Otherwise, it's quite easy I think.) But with this, you've already got a first toolkit that will enable you to take your first steps... :sourit:</p>
<p>Don’t hesitate to ask if you have questions on this tutorial (if the examples lack clarity or you have trouble making them work…)</p>
<p>See you soon!</p>
<p>Dorian</p>
<p>PS: As you could see, I'm not english so if I'd made redundant errors, don't hesitate to notice me! :sourit:</p>
<center>:longBar:</center>
<h5>Edit - Blog wich talk about this tutorial:</h5>
<ul>
<li><a href="http://marc.dubrois.free.fr/?p=179" hreflang="en">http://marc.dubrois.free.fr/?p=179</a></li>
</ul>Débutant sur l'API Maya? Et bien lisez ça!urn:md5:4437badf49d1f30f3212b77be9c0e7fa2010-02-02T21:00:00+01:002013-07-26T22:33:09+02:00NarannScript et codeapicodecppdebutantfrmayamel<p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_02_debut_api_maya/codeAPIMaya001_tn.png" alt="codeAPIMaya001_tn.png" style="float:left; margin: 0 1em 1em 0;" title="codeAPIMaya001_tn.png, fév. 2010" height="150" width="150" />Voici la traduction d'<a href="http://www.creativecrash.com/forums/api/topics/new-to-the-api-then-read-this-33" hreflang="en">un des meilleurs résumé</a> que je connaisse pour les personnes qui souhaitent commencer sous l'API Maya. Il est principalement destiné au développeurs, n'ayant aucune connaissance en Maya, qui souhaiterai commencer à écrire des nodes. Mais je pense qu'il peut également être lu par des scripteurs confirmés qui souhaitent se pencher sur l'API. Encore une fois, il s'agit d'un maitre, peut être pas le plus grand développeur mais incontestablement celui qui a tenter de mettre le script et la programmation dans Maya à la porté de tous (ce qui pour moi, lui donne le titre de maitre ^^ ), j'ai nommé: <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/" hreflang="en">Robert Bateman</a>.</p> <p><img src="https://www.fevrierdorian.com/blog/public/billets/2010_02_debut_api_maya/village_people_API.png" alt="village_people_API.png" style="display:block; margin:0 auto;" title="village_people_API.png, fév. 2010" height="302" width="336" /></p>
<h4>Traduction:</h4>
<p>Lisez ce billet en entier avant de commencer à bidouiller dans l'API Maya - Il vous permettra d'économiser beaucoup temps et d'efforts à long terme, je peux vous l'assurer.</p>
<h5>Conseils</h5>
<ul>
<li>Vérifiez TOUT les MStatus renvoyé par l'API Maya. Je sais que vous trouvez ça lourd, mais Maya a l'habitude d'échouer silencieusement. Si vous ne prenez pas l'habitude de vérifier les retours d'erreurs, vous pouvez ne pas en voir une qui s'est produite! Cela accélèrera grandement le développement si vous prenez l'habitude de tout vérifier.</li>
<li>Vous devez penser à écrire votre code (dans l'API) de façon à ce qu'il fonctionne de la manière dont Maya fonctionne. Essayer de faire fonctionner l'API à votre sauce ne vous garantie aucun résultat.</li>
<li>Utilisez Visual Studio 2005 ou plus récent. Vous ne pouvez pas utiliser d'autre compilateur. Utilisez GCC sous Linux/Mac.</li>
<li>Le MEL EST plus puissant que l'API C++. Difficile à croire et pourtant vrai.</li>
<li>L'API n'est pas une version C++ du MEL. Les deux ont des usages différents.</li>
<li>N'importe quel plugin est un mixte de MEL et d'API. Vous ne pouvez pas vous passer du MEL.</li>
<li>Si vous ne savez pas utiliser Maya, apprenez! C'est le seul moyen d'être sur que vos outils fonctionneront de manière cohérente avec lui.</li>
<li>Si vous avez du code en MEL que vous voulez porter en C++ dans un soucis de vitesse, avant cela, désactiver l'undo, soyez sur que vous n'utilisez pas la commande "print()", et voyez à quelle vitesse il va. Vous devez trouver un moyen d'accélérer le script suffisamment afin d'éviter d'avoir à utiliser l'API.</li>
<li>Gardez vos nodes le plus petit possible (ndt: "small and atomic" dans le texte). Un node "couteau suisse" ne sera pas efficacement à traiter par Maya, donc découpez les opérations complexes en plusieurs nodes.</li>
<li>Essayez autant que possible d'utilisez les nodes pré-existant, et n'ajoutez de nouveaux nodes uniquement pour des choses que vous ne pouvez pas faire avec l'API.</li>
<li>Comprendre les différentes entre les nodes de type DAG et les nodes de type DG est vital si vous travaillez avec l'API. Lisez ce post pour meilleur description: <a href="http://www.highend3d.com/boards/index.php?showtopic=242681" hreflang="en">http://www.highend3d.com/boards/index.php?showtopic=242681</a> (ndt: le liens est mort mais il semble que ce soit ce billet: <a href="http://www.creativecrash.com/forums/api/topics/dag-confusion" hreflang="en">http://www.creativecrash.com/forums/api/topics/dag-confusion</a>).</li>
<li>N'utilisez que des fichiers Maya au format ASCII (.ma). D'une part ils fonctionnent avec un logiciel de contrôle de version, mais plus important, vous gardez une chance de pouvoir charger le fichier si vous changez les attributs du plug-in. Dans le cas des fichier binaires, Maya charge les informations comme un bloque de donné "brut". Si vous changez les attributs, ou l'ordre dans lequel les attributs sont définis, vous chargerez des données corrompues. Dans la mesure ou le format Maya ASCII charge les attributs "par nom", vous pouvez normalement faire quelques modifications minimes tout gardant la possibilité de charger vos scènes.</li>
</ul>
<h5>Sur le forum (ndt: <a href="http://www.creativecrash.com/forums/api/topics" hreflang="en">http://www.creativecrash.com/forums/api/topics</a>)</h5>
<ul>
<li>Vous remarquerez que des questions reçoivent la réponse "utilise du MEL". Nous n'essayons pas d'être énervant quand nous disons cela, nous somme juste réaliste. La question que vous avez posé est soit quelque chose d'impossible à faire dans l'API, ou soit quelque chose pour laquelle l'API est totalement inadaptés (voir l'exemple du code plus bas). Si vous avez la réponse "utilise le MEL", alors s'il vous plait, utilisez le MEL! Bon nombre de gens s'énerve quand nous leur répondons ça, mais honnêtement, l'API est loin d'être aussi puissante que MEL, et 99% du temps, les choses devraient être fait en MEL.</li>
<li>Vous pouvez penser que les gens n'ont pas la réponse à votre question. C'est tout à fait normal dans la mesure ou l'API est assez conséquente et vous ne pouvez pas attendre des gens qu'ils connaissent tout cela. Dans ce cas, il est bon de garder son thread d'ouvert et de mettre à jour votre progression dans la mesure ou cela forme un enregistrement dans google qui peut aider les autres plus tard. En fin de compte, la connaissance que nous avons acquise au fil des ans est dû aux gens qui font ça.</li>
<li>Ne m'envoyez pas de questions sur l'API en PM, ni aux modérateurs ni aux autres membres du forum. Si vous faites ça, votre question n'est pas public. Si vous avez un problème, l'endroit ou demander est le forum. Comme c'est écrit dans les règles du forum, nous ne répondons pas aux question posé par PM car nous voulons garder un tableau "googleable" des question posées, et de leur éventuel réponse dans le forum.</li>
<li>Ne m'envoyez pasde lien vers un post que vous avez crée sur le forum. Ni à moi, ni aux autres membres. Les modérateur d'ici (ndt: les forums de creativecrash.com) reçoivent un e-mail à chaque topic créé, et nous regardons chaque question qui est posé. Si nous pouvons donner une réponse nous le ferons. Envoyer un PM avec un lien vers le billet ne fait que nous énerver.</li>
</ul>
<h5>Les exporters</h5>
<p>Si vous voulez simplement écrire un exporter pour Maya, vous avez de la chance - c'est l'une des choses les plus facile à faire avec l'API, et cela vous donnera un joli aperçu de ce qu'il faut savoir sur Maya. J'ai écrit une documentation il y a longtemps, qui peut être <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/" hreflang="en">trouvé ici</a>. Elle fut écrit pour Maya 6.5, mais devrait vous indiquer la marche à suivre. (Allez y pour avoir des exemples à compiler sur Maya 8.0 ou plus récent, soyez certain d'avoir ajouté le define "REQUIRE_IOSTREAM" dans vos options de pre-processeur).</p>
<h5>Les importeurs</h5>
<p>Écrire un importer pour Maya est un peu plus compliqué, mais ce n'est pas la mer à boire. Typiquement, vous devez chercher une class sous la forme MFn####, ou #### est la chose importante (exemple: Transform, Mesh, etc...). Chaque classe MFn a une méthode "create", l'idée étant justement de faire l'inverse de ce qui est <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/index.htm" hreflang="en">présenté ici</a>. Notez que vous ne pouvez pas faire tout ce que vous souhaitez aussi facilement avec l'API, donc attendez vous à faire du MEL occasionnellement.</p>
<h5>Les nodes</h5>
<p>Il est intéressant de commencer à apprendre l'API en créant vos propres nodes. Les nodes sont essentiels pour comprendre comment Maya fonctionne, et en réalité, quelque soit la chose que vous souhaitiez faire, elle sera probablement mieux faite avec un node (et non une commande, comme vous pouvez le penser). Encore une fois, j'ai quelques exemples pour débuter sur <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/" hreflang="en">mon site web</a> (<a href="http://robthebloke.org/" hreflang="en">http://robthebloke.org</a>) - <a href="http://www.davidgould.com/Books/index.html" hreflang="en">Les livres de David Gould</a> donnent également des explications sur comment débuter. Les choses dont vous avez besoin pour un nouveau node sont:</p>
<ul>
<li>Le code C++, MPxNode, MPxEmitter, etc...</li>
<li>L'AETemplate pour "customiser" l'Attribut Editor de votre node (en MEL)</li>
<li>Des commandes MEL pour créer le node et le connecter dans le DG</li>
<li>Un "custom menu item with options box" (Sauvegardez les options en utilisant optionVar)</li>
</ul>
<h5>Les nodes de locator</h5>
<p>Ils sont assez trivial à faire, ils peuvent être utilisé comme "marqueurs personnalisés" dans la scène. Il y a des exemples <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/" hreflang="en">sur mon site</a>, ainsi que dans <a href="http://www.davidgould.com/Books/index.html" hreflang="en">les livres de David Gould</a>.</p>
<h5>Des primitives poly/nurbs personnalisées</h5>
<p>Vous souhaitez créer une shape personnalisée, comme les primitives polyCube/polySPhere? Utilisez un node! Vous pouvez créer un attribut de sortie de type MPxData::kMesh, ou kNurbsSurface, etc... Qui sera modifié grace à la méthode "compute".</p>
<h5>Modifier la géométry ou d'autres informations</h5>
<p>N'allez pas essayer de supprimer une face d'un mesh, ou ajouter des vertex à un mesh, ou, en fait, n'importe quoi qui change les données dans Maya. Il y a 99% de chance que cela ne marche pas. Toute les modifications de données devrait être fait en utilisant des nodes. Considérez cet exemple: Si vous créez un poly cube à partir d'une MPxCommand, que vous supprimez une de ses faces en utilisant MFnMesh. Tout ce qui se passera c'est que la prochaine evalutation du DG (ndt: le prochain refresh Maya) va simplement réécrire votre mesh modifié par dessus l'ancien. Vous avez en fait besoin de créer un node qui modifie les données comme le ferait le DG!
Comme point de départ, vous pouvez utilisez les exemples de polyModifier qui sont dans le SDK de Maya.</p>
<h5>Les commandes</h5>
<p>Alors, vous voulez convertir une commande MEL toute lente pour qu'elle s'executer plus vite dans l'API? Dans un premier temps, éssayez de désactiver "l'undo queue", soyez sur que vous ne "printez" rien dans le script éditor, et voyez à quelle vitesse elle va. Cela peut résoudre votre problème.
La seul vrai raison pour écrire une class dérivé de la MPxCommand c'est si vous souhaitez interfacer du code C++ ou les librairies. Toute autre utilisation est une perte de temps, et vous devriez utiliser le MEL.</p>
<h5>GUI</h5>
<p>Oubliez ça. L'API Maya n'a pas de GUI, vous devez utiliser le MEL. La seule exception est un "context" personnalisé que vous pouvez écrire en C++ pour gérer les entrées de la souris pour vous. Vous pouvez cepandant utiliser une commande MEL comme scriptCtx pour faire ça (Bien que ce ne soit pas aussi flexible que la version C++)</p>
<h5>Applications Stand alone</h5>
<p>Vous pouvez vouloir utiliser la MLibrary pour avoir Maya qui fonctionne sans la GUI, ce qui est entierement possible sans trop d'effort. Mais avant de faire ça, est ce que mayabatch avec un MEL script est capable de faire le boulot plus rapidement? Aussi longtemps que vous éviterez d'utiliser les commandes MEL de GUI, cela devrait vous satisfaire.</p>
<h5>La classe MPxCommand</h5>
<p>Elle peut être pratique à utiliser comme un bac à sable pour apprendre l'API, mais 99 fois sur 100, vous n'irez pas loin. Je recommande fortement d'utiliser le MEL pour toute les commandes, dans la mesure ou vous n'avez pas à vous soucier de l'implémentation du undo!</p>
<h5>Le MEL est plus puissant que l'API Maya</h5>
<p>Bien que l'API Maya soit très puissante, il est également très facile de mal la comprendre lorsque vous débutez. L'idée de chaque plugin devrait être d'améliorer le MEL, et non de le remplacer par du C++. L'exemple suivant devrait, esperons le, démontrer que le MEL est simple mais également très flexible:</p>
<pre class="mel mel">$ret <span style="color: #339933;">=</span> `polyCube <span style="color: #339933;">-</span>n <span style="color: #ff0000;">"hello"</span>`<span style="color: #339933;">;</span>
expression <span style="color: #339933;">-</span>o $ret<span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> <span style="color: #339933;">-</span>s <span style="color: #ff0000;">"tx = time"</span> <span style="color: #339933;">-</span>ae <span style="color: #0000dd;">1</span> <span style="color: #339933;">-</span>uc all<span style="color: #339933;">;</span></pre>
<p>Tout ce que ce code fait, c'est connecter un couple de node ensemble. La commande "polyCube" et "expression" sont déja implémenté en MEL, et elles sont toute les deux très puissante, et extrêmement flexibles. Pour refaire ce code à l'identique avec l'API, vous devrez vous préoccuper des "set options", vous devrez être sur que le undo/redo fonctionne, vous devrez vous préoccuper des assignations de matériaux, etc... Pour faire court, la quantité de travail n'est pas négligeable. La version C++ de ses deux lignes de MEL est en fait équivalent à 350 lignes de code, simplement pour manipuler des nodes tout simples! Imaginez avec des centaines de node, vos 200 lignes de MEL pourrait ressembler à 35000 lignes de C++!!!</p>
<p>Donc, éssayez d'utiliser le MEL sinon vous finirez par perdre beaucoup de temps, un peu comme ça:</p>
<pre class="cpp cpp"><span style="color: #666666;">// to execute once compiled, call this in mel:</span>
<span style="color: #666666;">//</span>
<span style="color: #666666;">// wasteOfTime "hello";</span>
<span style="color: #666666;">//</span>
<span style="color: #666666;">// wasteOfTime.h</span>
<span style="color: #339900;">#ifndef __WHY_MPX_COMMAND_IS_A_WASTE_OF_TIME__H__</span>
<span style="color: #339900;">#define __WHY_MPX_COMMAND_IS_A_WASTE_OF_TIME__H__</span>
<span style="color: #339900;">#ifdef WIN32</span>
<span style="color: #339900;">#define NT_PLUGIN</span>
<span style="color: #339900;">#define REQUIRE_IOSTREAM</span>
<span style="color: #339900;">#pragma once</span>
<span style="color: #339900;">#endif</span>
<span style="color: #339900;">#include <maya/MPxCommand.h></span>
<span style="color: #339900;">#include <maya/MGlobal.h></span>
<span style="color: #339900;">#include <maya/MFnSet.h></span>
<span style="color: #339900;">#include <maya/MFnDependencyNode.h></span>
<span style="color: #339900;">#include <maya/MDagModifier.h></span>
<span style="color: #339900;">#include <maya/MSyntax.h></span>
<span style="color: #339900;">#include <maya/MArgDatabase.h></span>
<span style="color: #339900;">#include <maya/MSelectionList.h></span>
<span style="color: #339900;">#include <maya/MPlug.h></span>
<span style="color: #339900;">#include <maya/MFnDagNode.h></span>
<span style="color: #339900;">#include <maya/MDagPath.h></span>
<span style="color: #339900;">#include <maya/MFnExpression.h></span>
<span style="color: #339900;">#include <iostream></span>
<span style="color: #339900;">#define CHECK_STAT(X) \
if( (X) != MS::kSuccess) { \
std::cout << __FILE__ << ":" << __LINE__ << std::endl; \
MGlobal::displayError(status.errorString()); \
return (X); \
}</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">class</span> WasteOfTime
<span style="color: #008080;">:</span> <span style="color: #0000ff;">public</span> MPxCommand
<span style="color: #008000;">{</span>
<span style="color: #0000ff;">public</span><span style="color: #008080;">:</span>
<span style="color: #0000ff;">bool</span> isUndoable<span style="color: #008000;">(</span><span style="color: #008000;">)</span> <span style="color: #0000ff;">const</span><span style="color: #008080;">;</span>
MStatus doIt<span style="color: #008000;">(</span><span style="color: #0000ff;">const</span> MArgList<span style="color: #000040;">&</span> args<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MStatus redoIt<span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MStatus undoIt<span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">static</span> MSyntax newSyntax<span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">static</span> <span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> creator<span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">private</span><span style="color: #008080;">:</span>
MDagModifier mDagMod<span style="color: #008080;">;</span>
MDagPath mPath<span style="color: #008080;">;</span>
MObject mExpression<span style="color: #008080;">;</span>
<span style="color: #008000;">}</span><span style="color: #008080;">;</span>
<span style="color: #339900;">#endif</span>
<span style="color: #666666;">// wasteOfTime.cpp</span>
CODE
<span style="color: #339900;">#include "WasteOfTime.h"</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">bool</span> WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">isUndoable</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span> <span style="color: #0000ff;">const</span>
<span style="color: #008000;">{</span>
<span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">void</span><span style="color: #000040;">*</span> WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">creator</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
<span style="color: #0000ff;">return</span> <span style="color: #0000dd;">new</span> WasteOfTime<span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
MSyntax WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">newSyntax</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
MSyntax syn<span style="color: #008080;">;</span>
syn.<span style="color: #007788;">addArg</span><span style="color: #008000;">(</span>MSyntax<span style="color: #008080;">::</span><span style="color: #007788;">kString</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">return</span> syn<span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
MStatus WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">doIt</span><span style="color: #008000;">(</span><span style="color: #0000ff;">const</span> MArgList<span style="color: #000040;">&</span> args<span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
MStatus status <span style="color: #000080;">=</span> MS<span style="color: #008080;">::</span><span style="color: #007788;">kFailure</span><span style="color: #008080;">;</span>
<span style="color: #666666;">// verify args</span>
MArgDatabase db<span style="color: #008000;">(</span>syntax<span style="color: #008000;">(</span><span style="color: #008000;">)</span>,args,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #666666;">// grab name arg</span>
MString name<span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> db.<span style="color: #007788;">getCommandArgument</span><span style="color: #008000;">(</span><span style="color: #0000dd;">0</span>,name<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #666666;">// avoid annoying creatNode class with base</span>
MDGModifier<span style="color: #000040;">&</span> mod <span style="color: #000080;">=</span> mDagMod<span style="color: #008080;">;</span>
<span style="color: #666666;">// create nodes</span>
MObject oT <span style="color: #000080;">=</span> mDagMod.<span style="color: #007788;">createNode</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"transform"</span>,MObject<span style="color: #008080;">::</span><span style="color: #007788;">kNullObj</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MObject oM <span style="color: #000080;">=</span> mDagMod.<span style="color: #007788;">createNode</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"mesh"</span>,oT,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MObject oP <span style="color: #000080;">=</span> mod.<span style="color: #007788;">createNode</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"polyCube"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #666666;">// rename based on args</span>
status <span style="color: #000080;">=</span> mDagMod.<span style="color: #007788;">renameNode</span><span style="color: #008000;">(</span>oM,name <span style="color: #000040;">+</span> <span style="color: #FF0000;">"Shape"</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> mDagMod.<span style="color: #007788;">renameNode</span><span style="color: #008000;">(</span>oT,name<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> mDagMod.<span style="color: #007788;">renameNode</span><span style="color: #008000;">(</span>oP,name <span style="color: #000040;">+</span> <span style="color: #FF0000;">"Creator"</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #666666;">// connect polycube to mesh</span>
<span style="color: #008000;">{</span>
MFnDependencyNode fnPC<span style="color: #008000;">(</span>oP,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MFnDependencyNode fnPM<span style="color: #008000;">(</span>oM,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MPlug plgI <span style="color: #000080;">=</span> fnPM.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"i"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MPlug plgO <span style="color: #000080;">=</span> fnPC.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"out"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> mDagMod.<span style="color: #007788;">connect</span><span style="color: #008000;">(</span>plgO,plgI<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">// grab current options for polyCube, and set on node</span>
<span style="color: #008000;">{</span>
<span style="color: #0000ff;">double</span> w<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">double</span> h<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">double</span> d<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> sdw<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> sdh<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> sdd<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> tex<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> axis<span style="color: #000080;">=</span><span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeWidth<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,w,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeHeight<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,h,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeDepth<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,d,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeSX<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,sdw,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeSY<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,sdh,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeSZ<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,sdd,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeTexture<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,tex,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">executeCommand</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"optionVar -q <span style="color: #000099; font-weight: bold;">\"</span>polyPrimitiveCubeAxis<span style="color: #000099; font-weight: bold;">\"</span>"</span><span style="color: #008000;">)</span>,axis,<span style="color: #0000ff;">false</span>,<span style="color: #0000ff;">false</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MPlug plg<span style="color: #008080;">;</span>
MFnDependencyNode fnP<span style="color: #008000;">(</span>oP,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
plg <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"w"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> plg.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span>w<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
plg <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"h"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> plg.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span>h<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
plg <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"d"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> plg.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span>d<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
plg <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"sw"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> plg.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span>sdw<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
plg <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"sh"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> plg.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span>sdh<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
plg <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"sd"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> plg.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span>sdd<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
plg <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"tx"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> plg.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span>tex<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MPlug axx <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"axx"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MPlug axy <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"axy"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MPlug axz <span style="color: #000080;">=</span> fnP.<span style="color: #007788;">findPlug</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"axz"</span>,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">switch</span> <span style="color: #008000;">(</span>axis<span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
<span style="color: #0000ff;">case</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">:</span>
status <span style="color: #000080;">=</span> axx.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">1.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> axy.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">0.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> axz.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">0.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">case</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">:</span>
status <span style="color: #000080;">=</span> axx.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">0.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> axy.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">1.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> axz.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">0.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">default</span><span style="color: #008080;">:</span>
<span style="color: #0000ff;">case</span> <span style="color: #0000dd;">2</span><span style="color: #008080;">:</span>
status <span style="color: #000080;">=</span> axx.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">0.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> axy.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">0.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> axz.<span style="color: #007788;">setValue</span><span style="color: #008000;">(</span><span style="color:#800080;">1.0</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">// now assign the mesh to the default shading group.</span>
<span style="color: #008000;">{</span>
MFnDagNode fnM<span style="color: #008000;">(</span>oM,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> fnM.<span style="color: #007788;">getPath</span><span style="color: #008000;">(</span>mPath<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #0000ff;">return</span> redoIt<span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
MStatus WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">redoIt</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
MStatus status <span style="color: #000080;">=</span> mDagMod.<span style="color: #007788;">doIt</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">selectByName</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"initialShadingGroup"</span>,MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">kReplaceList</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MSelectionList sl<span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">getActiveSelectionList</span><span style="color: #008000;">(</span>sl<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MObject o<span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> sl.<span style="color: #007788;">getDependNode</span><span style="color: #008000;">(</span><span style="color: #0000dd;">0</span>,o<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MFnSet fnS<span style="color: #008000;">(</span>o,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> fnS.<span style="color: #007788;">addMember</span><span style="color: #008000;">(</span>mPath,MObject<span style="color: #008080;">::</span><span style="color: #007788;">kNullObj</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #008000;">{</span>
MDagPath p <span style="color: #000080;">=</span> mPath<span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> p.<span style="color: #007788;">pop</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MFnExpression fnE<span style="color: #008080;">;</span>
MObject oT <span style="color: #000080;">=</span> p.<span style="color: #007788;">node</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
mExpression <span style="color: #000080;">=</span> fnE.<span style="color: #007788;">create</span><span style="color: #008000;">(</span>MString<span style="color: #008000;">(</span><span style="color: #FF0000;">"tx = time"</span><span style="color: #008000;">)</span>,oT,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #0000ff;">return</span> status<span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
MStatus WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">undoIt</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
MStatus status<span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">selectByName</span><span style="color: #008000;">(</span><span style="color: #FF0000;">"initialShadingGroup"</span>,MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">kReplaceList</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MSelectionList sl<span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">getActiveSelectionList</span><span style="color: #008000;">(</span>sl<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MObject o<span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> sl.<span style="color: #007788;">getDependNode</span><span style="color: #008000;">(</span><span style="color: #0000dd;">0</span>,o<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
MFnSet fnS<span style="color: #008000;">(</span>o,<span style="color: #000040;">&</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> fnS.<span style="color: #007788;">removeMember</span><span style="color: #008000;">(</span>mPath,MObject<span style="color: #008080;">::</span><span style="color: #007788;">kNullObj</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> MGlobal<span style="color: #008080;">::</span><span style="color: #007788;">deleteNode</span><span style="color: #008000;">(</span>mExpression<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">return</span> mDagMod.<span style="color: #007788;">undoIt</span><span style="color: #008000;">(</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #339900;">#include <maya/MFnPlugin.h></span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
<span style="color: #339900;">#ifdef WIN32</span>
<span style="color: #339900;">#pragma comment(lib,"Foundation.lib")</span>
<span style="color: #339900;">#pragma comment(lib,"OpenMaya.lib")</span>
<span style="color: #339900;">#endif</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
<span style="color: #339900;">#ifdef WIN32</span>
<span style="color: #339900;">#define EXPORT __declspec(dllexport)</span>
<span style="color: #339900;">#else</span>
<span style="color: #339900;">#define EXPORT</span>
<span style="color: #339900;">#endif</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
EXPORT MStatus initializePlugin<span style="color: #008000;">(</span>MObject obj<span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
MStatus status<span style="color: #008080;">;</span>
MFnPlugin fnPlugin<span style="color: #008000;">(</span> obj, <span style="color: #FF0000;">"Rob Bateman"</span>, <span style="color: #FF0000;">"1.0"</span>, <span style="color: #FF0000;">"Any"</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> fnPlugin.<span style="color: #007788;">registerCommand</span><span style="color: #008000;">(</span> <span style="color: #FF0000;">"wasteOfTime"</span>,WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">creator</span>,WasteOfTime<span style="color: #008080;">::</span><span style="color: #007788;">newSyntax</span><span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">return</span> status<span style="color: #008080;">;</span>
<span style="color: #008000;">}</span>
<span style="color: #666666;">//---------------------------------------------------------------------------------</span>
EXPORT MStatus uninitializePlugin<span style="color: #008000;">(</span>MObject obj<span style="color: #008000;">)</span>
<span style="color: #008000;">{</span>
MStatus status<span style="color: #008080;">;</span>
MFnPlugin fnPlugin<span style="color: #008000;">(</span>obj<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
status <span style="color: #000080;">=</span> fnPlugin.<span style="color: #007788;">deregisterCommand</span><span style="color: #008000;">(</span> <span style="color: #FF0000;">"wasteOfTime"</span> <span style="color: #008000;">)</span><span style="color: #008080;">;</span>
CHECK_STAT<span style="color: #008000;">(</span>status<span style="color: #008000;">)</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">return</span> status<span style="color: #008080;">;</span>
<span style="color: #008000;">}</span></pre>
<h4>Conclusion</h4>
<p>J'espere que cette traduction vous servira. Je ne crois pas qu'il existe ce genre d'information en français (cela dis, il ne doit pas y avoir beaucoups de dev qui utilise l'API Maya...) :franceHappy:</p>
<p>Cela dis, depuis qu'il est possible de faire des plugins et des nodes Maya directement en Python je m'interesse un peu à comment l'API fonctionne. Cité plusieurs fois, le site de Rob est très interessant car il fourmille de codes, exemples, explications sur l'API Maya. Malgrès le fait qu'il soit en C++, si vous commencez à avoir l'habitude de l'API Maya python et que vous connaissez un peu le C++, vous n'aurez (presque) aucun soucis à faire la transition. :sourit:</p>
<p>Voila, je n'ai pas grand chose à dire si ce n'est: "+1" car je trouve que les choses sont bien résumés.</p>
<p>A bientôt!</p>
<center>:marioCours:</center>
Créer un Custom Locator avec l'API Python de Mayaurn:md5:564cf9553e292605113263f72cc08d0e2009-04-15T22:40:00+02:002013-07-26T22:41:00+02:00NarannScript et codeapicppfrmayamelopenglpythonscript<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocator001.png" alt="pythonLocator001.png" style="float:left; margin: 0 1em 1em 0;" title="pythonLocator001.png, mar. 2009" height="150" width="150" />Comme <a href="https://www.fevrierdorian.com/blog/index.php?post/2009/03/31/Python%2C-Maya-et-sont-API...">je vous le disais</a>, je commence à utiliser le binding Python de l'API Maya. J'ai donc regardé les codes sources de <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/mayaapi.html#2" hreflang="en">Rob Bateman</a> (que je trouvais incompréhensibles il y a un an) et les ai "traduit" (non sans mal) en Python... J'ai donc créé un petit script avec un locator personnalisé. Ce n'est clairement pas le plus simple pour commencer (J'avais quand mêmes des bases grâce à <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/maya/mfn_traversing.htm" hreflang="en">d'autres tutos</a>, aux "plugins" Python déjà intégré à Maya et a des tutos OpenGL que j'avais fait) mais une fois le code fonctionnel, c'est assez drôle de le modifier pour fabriquer son propre locator...</p> <p>Au programme donc:</p>
<ul>
<li>OpenGL (Vous voulez le dessiner comment votre locator? :hehe: )</li>
<li>Changement de couleur en fonction de l'état de sélection du locator.</li>
</ul>
<p>C'est peu mais vous allez voir qu'on a de quoi faire!</p>
<center>:longBar:</center>
<h5>Le scripting ça sert à rien, c'est pour les newbies!</h5>
<p>Non! Si vous pensez ça c'est que vous n'avez rien compris. Les deux sont intimement liés. Si j'ai choisi l'exemple du locator ce n'est pas simplement parce que c'est "cool" mais aussi parce que c'est un exemple de chose qu'il n'est possible de faire qu'avec l'API...</p>
<p>Mais l'API ne fait pas tout! :cayMal:</p>
<p>Comme vous le savez surement déjà, l'interface de Maya est entièrement en MEL, ce qui permet à un scripteur de pouvoir rapidement créer une GUI pour ses scripts. Tous les softs ne propose pas cela, (qui à dis XSI?) et Maya est un des rares à pouvoir s'en vanter.</p>
<p>La plupart des appels aux fonctions Maya sont scriptés elles aussi. Ainsi, vous pouvez "batcher" vos opérations (quand vous avez 50 scènes à ouvrir pour changer la valeur d'un attribut, vous faites moins le malin! :hehe: )</p>
<p>Je ne peut que vous inviter à lire <a href="http://www.highend3d.com/boards/index.php?showtopic=242667" hreflang="en">le post</a> de mon mentor sur le sujet (On peut y voir l'équivalent d'un code MEL en C++)</p>
<p>Dernière chose: Python, c'est du script! Même si il peut être "précompilé" par l'interpréteur, ça reste du script.</p>
<p>Cela dis, je pense que ce débat est un peu stérile car même si on "script" en utilisant le binding Python de l'API Maya, une fois les fondamentaux compris on se rend compte que, entre un code C++ et son équivalent en Python, la différence de syntaxe est minime.</p>
<p>Petit exemple de déclaration de variable:</p>
<center><I>C++</I></center>
<pre class="cpp cpp">MFnPlugin fnPlugin<span style="color: #008000;">(</span>obj<span style="color: #008000;">)</span><span style="color: #008080;">;</span></pre>
<center><I>Python</I></center>
<pre class="python python">fnPlugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span></pre>
<p>Le C++ reste plus propre à l'utilisation je trouve (je ne dis pas ça en rapport avec l'exemple mais plus de manière général), c'est logique d'une certaine façon vue qu'il s'agit de l'implémentation historique ajouté au fait qu'il reste un langage assez proche de la machine.</p>
<p>Après, tout dépend si vous voulez obtenir de la vitesse. Un code compilé (C++) ira surement plus vite mais je suppose que si vous voulez aller vite, soit vous êtes nul et vous faite des codes pourris qui ne vous laisse pas d'autres solutions que de passer par un langage plus bas niveau pour l'accélérer, soit vous êtes un "vrai" développeur... Si c'est le cas, je vois pas ce que vous faites sur le blog d'un codeur du dimanche! (Moi! :aupoil: ).</p>
<p>Allé, un de ces quatre je me fait un bench entre mel, pythonForMel, C++ et pythonForMayaApi :sourit: .</p>
<center>:longBar:</center>
<h5>Les bases du code</h5>
<p>Avant de commencer à tripatouiller du locator, il faut le code de base. C'est partie!</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>Bam! On fait moins le malin là! (Je faisais pas le malin non plus la première fois! :baffed: ).</p>
<p>Bon, en fait c'est pas très compliqué. Explication:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender</pre>
<p>Ici nous importons les principaux modules. Le premier ("sys") est le module système qui va nous servir à écrire les messages d'erreur d'initialisation du plugins (c'est tout).</p>
<p>Les trois suivants sont les modules Python de l'API Maya. Si ils ont été scindés en plusieurs modules de manière à clarifier leur utilisation:</p>
<ul>
<li>OpenMaya est le module des classes de définition des nodes et des commandes, et "d'assemblage" de ceux ci en plugins.</li>
<li>OpenMayaMPx est un module spécifique à Python. Il contient les bindings des proxy (ou classe MPx) de Maya.</li>
<li>OpenMayaRender est le module des classes relatives au rendu (hardware ou software).</li>
</ul>
<pre class="python python">nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span></pre>
<p>Cette variable (je la déclare au début) servira à donner un nom au type de node que l'on souhaite créer.</p>
<pre class="python python">nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span></pre>
<p>Cette variable (je la déclare au début également) servira à donner un ID (identifiant) à notre type de node.</p>
<pre class="python python">glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Là ça ce complique (surtout pour moi). Cette commande n'est pas référencé par la documentation (elle apparait pourtant dans des codes C++ d'exemple). Elle permet de récupérer un pointeur vers la classe chargé du rendu (hardware) des viewports. C'est grâce à elle qu'on va pouvoir "dessiner" notre locator (à l'aide de commandes OpenGL).</p>
<pre class="python python">glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>La méthode glFunctionTable() renvois un pointeur vers un objet contenant toute les instruction OpenGL géré par Maya.</p>
<p>Alors là, on tombe en pleins dans l'OpenGL (enfin pas tout à fait, vous allez voir :sourit: ):</p>
<center>:longBar:</center>
<h5>OpenGL, quelques information</h5>
<p>OpenGL c'est quoi? (<a href="http://fr.wikipedia.org/wiki/OpenGL" hreflang="fr">Wiki</a>) Pour résumé, c'est un moyen de communiquer avec la carte graphique. Il s'agit de commandes simples pour (par exemple) afficher des points, des lignes, des triangles, des quad, des textures, etc...</p>
<p>Avant de continuer, nous allons devoir comprendre comment tout cela fonctionne. Si vous voulez "dessiner" des locators, il faut que vous sachiez utiliser le stylo qui va pouvoir le faire.</p>
<p>Comme un code vaut mieux qu'un long discourt, voici comment "dessiner" une ligne en OpenGL (Ceci est un code C. Pour Maya, nous allons voir qu'il y a deux trois choses à savoir avant):</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>Cette technique est la technique "de base" dite: de "dessin immédiat".</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span></pre>
<p>Cette commande permet de "passer en mode dessin de primitive" (Dans un espace 3D). On pose le crayon en quelque sorte. L'argument (GL_LINES) permet de dire comment vont être interprété les commandes suivantes, ici, en ligne. Tous les deux vertices, on écrit une autre ligne. Une ligne par paire de vertice.</p>
<pre class="c c">glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span></pre>
<p>Ces deux commandes sont les commandes qui "place" les vertices dans l'espace. La commande est glVertex, le numero est le nombre d'argument (ici trois: XYZ), et la dernière lettre est le <a href="http://www.commentcamarche.net/contents/c/ctype.php3" hreflang="fr">type des données</a> (ici "f" pour float).</p>
<p>En gros, nous plaçons deux vertices dans l'espace. Et vu que nous nous somme mis en mode "dessin de ligne" (par pair de vertice), nous venons de dessiner une ligne.</p>
<pre class="c c">glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>Et bien c'est une manière de "fermer la communication" avec la carte graphique et de retourner au programme principal.</p>
<p>Vous voyez ce n'est pas si dur que ça OpenGL! C'est aussi comme ça que j'ai compris que c'est le processeur qui calcule toute les positions des vertices à chaque image (Bon, c'est l'ancienne méthode ça, il y en à d'autres, notamment pour les objets statiques qui eux, sont stocké dans la mémoire de la carte graphique et appelé par une seul instruction. Cela dis, une grosse partie du travail est fait par le processeur).</p>
<p>Avec ça, vous pouvez presque dessiner votre propre locator. Il me reste un dernier point à aborder.</p>
<center>:longBar:</center>
<h5>OpenGL version Maya! <a name="openGLVersionMaya"></a></h5>
<p>En effet, le code que je vous ai montré plus haut est un code C. Je vais maintenant vous expliquer les quelques subtilités OpenGL propre à Maya.</p>
<p>Maya, dans un soucis d'interopérabilité (je suppose qu'il y a d'autres raisons), dispose de sa propre implémentation OpenGL. lorsque on souhaite dessiner dans le viewport, nous n'utilisons donc pas directement l'implémentation de Windows ou Mac mais bien celle de Maya (On peut le faire en C++, cela permet de s'affranchir du wrapper de Maya mais vous devez gérer l'interopérabilité dans votre code. En Python, on doit pouvoir le faire aussi mais je n'ai pas essayé). Cela ne change pas grand chose dans le code mais les constantes (GL_LINES par exemple) ont un autre nom.</p>
<p>Comme dis dans la documentation de l'API de Maya:</p>
<blockquote><p>The naming convention used in this file is to take the regular GL* definitions and add a "M" prefix</p></blockquote>
<p>Traduisez: "On à mis un "M" devant toute les constantes OpenGL. :seSentCon:</p>
<p>Donc, Maya ne comprendra pas GL_LINES mais OpenMayaRender.MGL_LINES :baffed:</p>
<p>Et voila comment on écrira une ligne en OpenGL dans Maya:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Pas de grandes différences donc ^^. Un habitué d'OpenGL fera ça les doigts dans le nez!</p>
<center>:longBar:</center>
<h5>On l'écrit ce locator?</h5>
<p>Non! Pas encore! Il y a encore des choses à savoir! Après cette petite introduction à OpenGL, revenons à notre script Python.</p>
<p>Pour pouvoir dessiner le locator, il faut dériver la méthode "draw" (oui parce que Maya vous permet de dériver des méthode... Il y en a qui ne peuvent pas en dire autant...XSI? :siffle: ). Cette méthode est utilisé pour... dessiner des géométries personnalisés en utilisant les fonction OpenGL.</p>
<p>Voici le code minimal de la méthode "draw":</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Les fonctions beginGL() et endGL() permettent (enfin ça c'est mon interprétation) de dire à Maya que nous allons utiliser, entre celles ci, des commandes OpenGL. D'après la documentation, si elles ne sont pas placés, le programme peut déborder de sa mémoire et donc, planter.</p>
<p>Je vais volontairement oublier quelques fonctions pour l'instant, nous les verrons plus tard.</p>
<center>:longBar:</center>
<h5>Go pawaaaa!</h5>
<p>Notre code donc pour l'instant ceci:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>Vous pouvez le télécharger ici:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode001.7z">CustomLocatorNode001.7z</a></p>
<p>Chargez le (comme un plugins), tapez:</p>
<pre class="mel mel">createNode myCustomLocator<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocLineFirstLocator.png" alt="pythonLocLineFirstLocator.png" style="display:block; margin:0 auto;" title="pythonLocLineFirstLocator.png, avr. 2009" height="194" width="222" /></p>
<p>Nous venons de créer notre premier locator! :sourit: Avec ça on peut déja faire deux trois choses... En s'enflammant un peu, ça peut donner ça:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">random</span></pre>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode002.7z">CustomLocatorNode002.7z</a></p>
<p>Vous aurez un locator fou! ^^</p>
<p>Bon, c'est bien beau de faire des lignes mais il n'y a pas que ça... Je vous propose de faire... Un quad! :laClasse:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode003.7z">CustomLocatorNode003.7z</a></p>
<p style="font-style:italic; text-align:center;">Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuad.png" alt="pythonLocQuad.png" style="display:block; margin:0 auto;" title="pythonLocQuad.png, avr. 2009" height="276" width="405" /></p>
<p style="font-style:italic; text-align:center;">Paf le locator! <img class="smiley" alt=":baffed:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/baffed.gif"/></p>
<p>Bon, ça flash un peu mais on va changer ça. :reflechi: Nous allons changer la couleur et ajouter de la transparence.</p>
<p>D'abord, la couleur. Par défaut, Maya utilise les couleurs de l'interface (bleu quand non sélectionné, vert quand sélectionné, rose quand "templaté" etc...). Pour changer la couleur, il suffit d'ajouter la commande glColor3f()</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#on change la couleur</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode004.7z">CustomLocatorNode004.7z</a></p>
<p style="font-style:italic; text-align:center;">Re-Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColor.png" alt="pythonLocQuadColor.png" style="display:block; margin:0 auto;" title="pythonLocQuadColor.png, avr. 2009" height="342" width="507" /></p>
<p style="font-style:italic; text-align:center;">Re-Paf le locator! <img class="smiley" alt=":aupoil:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/aupoil.gif"/></p>
<p>Toujours aussi flash hein? Vous remarquerez qu'il n'y a que la ligne qui change de couleur en fonction de l'état de sélection. Pour "aérer" un peu notre locator, nous allons ajouter de la transparence à notre quad.</p>
<center>:longBar:</center>
<h5>L'Alpha</h5>
<p>Nous allons "activer" une des capacités d'OpenGL: GL_BLEND (Ne pas oublier de la désactiver à la fin):</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#On active la capacitée</span>
glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#on change la couleur</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#Ne pas oublier de la désactiver à la fin</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>Quand cette capacité est activé, OpenGL mélange la couleur donné par la couleur résultante (le fond) suivant un dernier facteur (l'alpha). Si vous lancez le code telle quelle, vous ne verriez pas de changement. En effet, nous devons donc ajouter un dernier composant à notre couleur:</p>
<pre class="python python">glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#on change la couleur et on ajoute l'alpha</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode005.7z">CustomLocatorNode005.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColorAlpha.png" alt="pythonLocQuadColorAlpha.png" style="display:block; margin:0 auto;" title="pythonLocQuadColorAlpha.png, avr. 2009" height="298" width="435" /></p>
<p style="font-style:italic; text-align:center;">Et voila! C'est déjà mieux ;)</p>
<p>Nous arrivons à la dernière partie: Les états de sélection.</p>
<center>:longBar:</center>
<h5>La Selection</h5>
<p>En effet nous pouvons changer des choses dans notre locator suivant l'état de selection de celui ci. Ici, nous allons changer... La couleur! (mega original...).</p>
<p>Tout d'abords, il y a plusieurs "états" d'un objets "dessiné" (Énuméré dans M3dView::DisplayStatus). Je vais vous donner les deux principaux, ceux qu'on va utiliser:</p>
<ul>
<li>kActive -> Les objets actifs (sélectionnés). (Attention, trompeur!)</li>
<li>kLead -> Le dernier objets sélectionné.</li>
<li>kDormant -> Les objets Inactifs.</li>
</ul>
<p>Alors, expliquons la subtilité:</p>
<p>Dans Maya, quand vous sélectionnez un objet, il devient vert. Et quand vous ajoutez un autre objet à la sélection, ce dernier deviens vert et l'ancien: Blanc. En fait l'objet vert est "en DisplayStatus" kLead et l'objet blanc, en kActive. Une image pour expliquer (rappeler?) le principe:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelectionState.png" alt="pythonLocSelectionState.png" style="display:block; margin:0 auto;" title="pythonLocSelectionState.png, avr. 2009" height="160" width="418" /></p>
<p>Nous allons donc pouvoir changer la couleur en fonction de l'état de sélection.</p>
<p>Comme vu plus haut, la méthode dérivé est draw, et l'argument qui nous sert à connaitre l'état du locator est "status". Et pour utiliser tout ça, rien de plus simple:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kLead</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.3</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#rouge</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kActive</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#jaune</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kDormant</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#mauve</span></pre>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode006.7z">CustomLocatorNode006.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection001.png" alt="pythonLocSelection001.png" style="display:block; margin:0 auto;" title="pythonLocSelection001.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection002.png" alt="pythonLocSelection002.png" style="display:block; margin:0 auto;" title="pythonLocSelection002.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection003.png" alt="pythonLocSelection003.png" style="display:block; margin:0 auto;" title="pythonLocSelection003.png, avr. 2009" height="216" width="382" /></p>
<p>Bon, ok, c'est moche mais ça marche... :baffed:</p>
<p>En espérant que ce tutorial vous éclairera sur quelques points de l'API Maya en Python... Sachez cependant que je n'ai pas choisi le plus simple pour commencer (si on ne connait rien à l'API... Sinon, c'est assé facile je pense). Mais avec ça, on a déjà un petit bagage qui vous permet de faire vos premiers pas... :sourit:</p>
<p>N'hésitez pas si vous avez des questions sur ce billet, (si les exemple manque de clarté ou que vous avez du mal à les faire fonctionner)</p>
<p>A bientôt!</p>
<p>Dorian</p>Seul avec Python, Maya et son API...urn:md5:6366b676ff5eb4d740d46750831100ff2009-03-29T23:12:00+02:002013-07-26T22:41:15+02:00NarannScript et codeapiautodeskcppfrmayapythonscript<p><img src="https://www.fevrierdorian.com/blog/public/logos/Rpyc3-logo-medium75.png" alt="Rpyc3-logo-medium75.png" style="float:left; margin: 0 1em 1em 0;" title="Rpyc3-logo-medium75.png, mar. 2009" height="75" width="75" />Ça fait plusieurs week ends que je regarde comment fonctionne l'API Maya avec Python. Je commence à avoir un petit aperçu des avantages et inconvénients... Je m'y suis mis et je compte faire, un de ces quatres, quelques tutoriaux et scripts car la documentation manque un peu...
<img src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/reflechi.gif" alt="reflechi.gif" style="display:block; margin:0 auto;" /></p> <p>En gros, les avantages:</p>
<ul>
<li>Pas besoin de recompiler :laClasse: . De ce fait, les plugins sont multiplateforme et on gagne un peut en vitesse (de développement) quand on passe sont temps à faire des allé retour entre sont code et Maya car on saute une étape intermédiaire (la compilation)</li>
<li>Pas besoin de tout le tralala technique sur les le linkage des libs (Même si il faut importer les "modules", ça reste beaucoup moins lourd et plus "visuel" pour un non développeur). Vous pouvez, si vous le voulez, ouvrir un fichier texte et coder un plugins ou débugger directement (<a href="https://www.fevrierdorian.com/blog/post/2009/11/04/Un-debugger-dans-Python-pour-voir-pr%C3%A9cis%C3%A9ment-ce-qui-ce-passe-dans-son-code">avec pdb</a>) depuis un autre poste. Pas besoin d'un IDE d'installé, <a href="http://notepad-plus.sourceforge.net/uk/site.htm" hreflang="en">Notepad++</a> fait largement l'affaire)</li>
<li>C'est tout mais c'est déjà pas mal!</li>
</ul>
<p>Et les inconvénients:</p>
<ul>
<li>Chez Maya: Pointeur + Script = MScriptUtil() : une classe qui permet de faire des pointeurs en script! :baffed: . C'est les joies du <a href="http://fr.wikipedia.org/wiki/Binding" hreflang="fr">binding</a>! (Je plains les gars de chez Autodesk, bien qu'ils doivent surement avoir des outils pour ça :seSentCon: ) Donc oui, l'API Maya est quand même bien balèze (C'est du C++ pure). Et comme la plupart des binds d'API balèze, on se retrouve avec des choses plus compliqué (au niveau de la syntax) à faire en Python qu'en C++ (Pointeur paweur...).</li>
<li>La documentation... Enfin disons que la doc C++ est très bien faite mais qu'il n'y a qu'elle et il faut faire un jonglage cérébrale constant pour réussir à transposer les méthodes des class vers sont code Python (C'est dur au début mais ça devient assez facile au bout d'un moment et on ne voit même plus la forme C++ du code...).</li>
<li>Pleins d'autres, découlant des deux premiers désavantages...</li>
</ul>
<p>Enfin bon, mon objectif est surtout de survoler l'API, de savoir comment Maya fonctionne. Je me fais toute sorte de tutoriaux en C++ que je "traduis" en Python (Un mec comme <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/" hreflang="en">Rob Bateman</a> est une bénédiction. J'ai appris le MEL grâce à ses tutoriaux, et voila que j'apprends maintenant à utiliser l'API grâce à lui...). Plus le temps passe et plus ça devient facile, preuve que j'avance. Il n'y a rien de plus frustrant que je stagner devant un script qui se lance pas avec pour seul message:</p>
<center><I>"Failed to run"</I></center>
<p><img src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/injures.gif" alt="injures.gif" style="display:block; margin:0 auto;" />
Bref, donc j'apprends des choses et surtout, je compte faire, comme j'ai pu le faire avant, des petits tutos (ou scripts expliqués) car la documentation sur le bind de l'API Maya en Python est quasi inexistante (Beaucoup d'éléments sont manquant ou écrits à demi-mot). Il faut chercher et ne pas hésiter à demander (la plupart des informations que j'ai pu obtenir, je les ais eus soit sur les blog, soit (dans des cas plus précis) en écrivant directement au blogueur!</p>
<p>Je vous ferai donc part de mes trouvailles en espérant que ça vous aide dans vos recherches...</p>
<p>Dorian</p>