Dorian Fevrier's blog - Mot-clé - voxelJe 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:695d9c73474c33ce3dab043823509c4bDotclearGeometry 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>