tag:blogger.com,1999:blog-46650368730520189752024-03-12T23:21:38.837-07:00Luiz Moreira's Tech ArtAnonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.comBlogger80125tag:blogger.com,1999:blog-4665036873052018975.post-64062979553519745732013-11-15T16:23:00.000-08:002013-11-15T16:24:37.440-08:00Friend in need<span style="background-color: white; color: #37404e; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; line-height: 18px;">Today's post isn't about anything technical, today I want to reach out to ask for help for a fellow artist in need.</span><br />
<span style="background-color: white; color: #37404e; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; line-height: 18px;"><br /></span>
<span style="background-color: white; color: #37404e; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; line-height: 18px;">Ana Kessel (</span><span style="color: #37404e; font-family: lucida grande, tahoma, verdana, arial, sans-serif;"><span style="line-height: 18px;"><a href="http://anakessel.weebly.com/">http://anakessel.weebly.com/</a>) </span></span><span style="background-color: white; color: #37404e; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; line-height: 18px;">3D modeler, was struck by a hit-and-run driver while riding her moped. Her leg has been amputated from the injuries. She is doing OK, </span><span class="text_exposed_show" style="background-color: white; color: #37404e; display: inline; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; line-height: 18px;">but she is going to need as much support as she can in order to get through this. A fundraiser has been started for her to try and raise money to help pay for the medical bills. Any donations will help and they can be submitted at </span><br />
<br />
<span class="text_exposed_show" style="background-color: white; color: #37404e; display: inline; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; line-height: 18px;"><a href="http://www.gofundme.com/58oak4">http://www.gofundme.com/58oak4</a></span><br />
<br />
<span style="color: #37404e; font-family: lucida grande, tahoma, verdana, arial, sans-serif;"><span style="line-height: 18px;">Please share the link and help more people get involved.</span></span><br />
<span style="color: #37404e; font-family: lucida grande, tahoma, verdana, arial, sans-serif;"><span style="line-height: 18px;"><br /></span></span>
<span style="color: #37404e; font-family: lucida grande, tahoma, verdana, arial, sans-serif;"><span style="line-height: 18px;">Thank you</span></span>Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com2tag:blogger.com,1999:blog-4665036873052018975.post-40197043158574709562013-10-31T19:00:00.002-07:002013-10-31T22:23:56.476-07:00Spidey rig update<div class="separator" style="clear: both; text-align: left;">
<span style="background-color: white; color: #333333; font-family: 'lucida grande', tahoma, verdana, arial, sans-serif; font-size: 13px; line-height: 17px;">The Spidey rig has evolved! New look, more intuitive controls, and more advance features. More updates soon.</span></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMHFhJofRVDkwjucE6labJ-_BcTzO-oo8DTl5xPgJ_kR36Ify6aQiiKQmnBK-pkXJTCcthrBvuYlM29RgICzhputDEceej3CFZ2NdJhnQZa8skXwAbFmLZicI3d_OAs4k_gtUPDWVB0fq6/s1600/persp_curves.png" imageanchor="1" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiMHFhJofRVDkwjucE6labJ-_BcTzO-oo8DTl5xPgJ_kR36Ify6aQiiKQmnBK-pkXJTCcthrBvuYlM29RgICzhputDEceej3CFZ2NdJhnQZa8skXwAbFmLZicI3d_OAs4k_gtUPDWVB0fq6/s320/persp_curves.png" width="264" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVEIp9TkJHnPrOaR0Ke89-Dl4eCcaPZkA5LXPxzQ5K13szvexgTzfOQ1E3F44p1NKQYdrKDJcVoqAYgQ7nfbFBjSB4A_BnXslTslkUvxTdINiPKYfcVQ6PRONNYMr9znNFw7kYhLCa1MGs/s1600/bacK_curves.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiVEIp9TkJHnPrOaR0Ke89-Dl4eCcaPZkA5LXPxzQ5K13szvexgTzfOQ1E3F44p1NKQYdrKDJcVoqAYgQ7nfbFBjSB4A_BnXslTslkUvxTdINiPKYfcVQ6PRONNYMr9znNFw7kYhLCa1MGs/s320/bacK_curves.png" width="287" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUfq-OMHZ9tOcR0thOKkFvGU-htQC7UiluPF1OwmvLQGNMMqPoImtKib6TkD2RvYj0cP3o_17EW2HKgRBW5Fqfc0BRBtrwcBjmas2BQfs1wZGRI-c7yGZ3PNjItJuHigA5cYMY_VHnEpl1/s1600/back_plain.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgUfq-OMHZ9tOcR0thOKkFvGU-htQC7UiluPF1OwmvLQGNMMqPoImtKib6TkD2RvYj0cP3o_17EW2HKgRBW5Fqfc0BRBtrwcBjmas2BQfs1wZGRI-c7yGZ3PNjItJuHigA5cYMY_VHnEpl1/s320/back_plain.png" width="243" /></a> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvKUK9VXpqeGcLqML-iMVk89Mgoxpqp-F_MCwkjrJWltD1ewezP_pH3Yg7Fo2Ax5XEIE9Yhw7lUAXkp-SkSeJ13_BbRz6NBHZG8uLhyMofhQ-gHyoEHwQ2yE8KKU_3WrWY31LDGDs89YP1/s1600/back_wireframe.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjvKUK9VXpqeGcLqML-iMVk89Mgoxpqp-F_MCwkjrJWltD1ewezP_pH3Yg7Fo2Ax5XEIE9Yhw7lUAXkp-SkSeJ13_BbRz6NBHZG8uLhyMofhQ-gHyoEHwQ2yE8KKU_3WrWY31LDGDs89YP1/s320/back_wireframe.png" width="222" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik7irWCUjsVRbU9RuOQLlmvSyu2kNobqQv1RbCpS35zcTFUGyQFcMzIyDTj2I27HrrklApa6TAgjwDifA62_NAkeboCBZodwd4fSEhB46vCecuYHITopyrsG_IFhCG1N87duAzO55rOyRE/s1600/front_plain.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEik7irWCUjsVRbU9RuOQLlmvSyu2kNobqQv1RbCpS35zcTFUGyQFcMzIyDTj2I27HrrklApa6TAgjwDifA62_NAkeboCBZodwd4fSEhB46vCecuYHITopyrsG_IFhCG1N87duAzO55rOyRE/s320/front_plain.png" width="231" /></a> <a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEJ0uXEp4L7dXhX0ShLwQ8-3i5w_cEbk6_kPqofQVYmJTMC3cy3W7bU_XQ8YF6UL2-Bbhx7sTHfe1v1coCpSlJuXLjkKB1-AJGlUqxOHimIng_hkuAx5YLzpfnrixMFMvi9kl5qen5fGlp/s1600/front_wireframe.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEJ0uXEp4L7dXhX0ShLwQ8-3i5w_cEbk6_kPqofQVYmJTMC3cy3W7bU_XQ8YF6UL2-Bbhx7sTHfe1v1coCpSlJuXLjkKB1-AJGlUqxOHimIng_hkuAx5YLzpfnrixMFMvi9kl5qen5fGlp/s320/front_wireframe.png" width="218" /></a></div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ_VZPKptxgScATpysOTzuA7sPQ6DxfJPXFLhQukBV-VYkOLsU_46EMlu1BbC_73QA65srXbp8FFGge9BmJJRhXnw5yr_72CzXoF28WvnYa_vNfBoSsBdYLJPdvpt1Rqp2gtVW0ZB_oiCq/s1600/front_curves.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjZ_VZPKptxgScATpysOTzuA7sPQ6DxfJPXFLhQukBV-VYkOLsU_46EMlu1BbC_73QA65srXbp8FFGge9BmJJRhXnw5yr_72CzXoF28WvnYa_vNfBoSsBdYLJPdvpt1Rqp2gtVW0ZB_oiCq/s320/front_curves.png" width="306" /></a><br />
<br />
<div class="separator" style="clear: both; text-align: left;">
Little guy!</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY6JmFmERTalagtwlpLwqCUIaw3LTu-oSchLpo2EvK-Ec__eGx0Yw1GlPxvb3jC9cOviV4lpMhyphenhyphennznd9cAZ4HPLJAXJh6xEIszPeXnzASJxDyDkgQgFNW3ha0MX187nv_TygTbsJB9q5KP/s1600/front_plain_short.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiY6JmFmERTalagtwlpLwqCUIaw3LTu-oSchLpo2EvK-Ec__eGx0Yw1GlPxvb3jC9cOviV4lpMhyphenhyphennznd9cAZ4HPLJAXJh6xEIszPeXnzASJxDyDkgQgFNW3ha0MX187nv_TygTbsJB9q5KP/s320/front_plain_short.png" width="213" /></a></div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1GW8ee1iZFCuBOv5DrHqAXEjvASuS9n-hi7mZMAQAhvUTM46xYe2tRUMROQp0Te6pdZgfklbpAaMMENZZJwdnrtd8Fu33N894RLCcDGCxhT2gZghAnCc_yc1-MEafWSt0IJFYCvqCj8sr/s1600/front_wireframe_short.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh1GW8ee1iZFCuBOv5DrHqAXEjvASuS9n-hi7mZMAQAhvUTM46xYe2tRUMROQp0Te6pdZgfklbpAaMMENZZJwdnrtd8Fu33N894RLCcDGCxhT2gZghAnCc_yc1-MEafWSt0IJFYCvqCj8sr/s320/front_wireframe_short.png" width="255" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi97yrK76Mdmgv5t4PbWS-MvyNvThscLM0ajZEzwM6lbP6VtTCo_gJl779OFXHavbqHE2WesHOM2nViuY4q7qvWlXZZYbtQqewIDx-n-U8C4mHDiRYflXznpMwcDw2jCPxzXdc4dDrcutCj/s1600/back_wireframe_short.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi97yrK76Mdmgv5t4PbWS-MvyNvThscLM0ajZEzwM6lbP6VtTCo_gJl779OFXHavbqHE2WesHOM2nViuY4q7qvWlXZZYbtQqewIDx-n-U8C4mHDiRYflXznpMwcDw2jCPxzXdc4dDrcutCj/s320/back_wireframe_short.png" width="228" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMQ21089nat-kjOJrrL_CBKnADRW42vqp6aOnQlqiF27rOzS01Ax2Tj2a7G5JEIJyFygb8VLh5jDtZz3fw4uOpm1UAQZiB9MXWU3Syev3Vi4k4ZvofGwMIj9xUgtp3Vm6d1UCCfNZ6yweY/s1600/front_curves_short.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMQ21089nat-kjOJrrL_CBKnADRW42vqp6aOnQlqiF27rOzS01Ax2Tj2a7G5JEIJyFygb8VLh5jDtZz3fw4uOpm1UAQZiB9MXWU3Syev3Vi4k4ZvofGwMIj9xUgtp3Vm6d1UCCfNZ6yweY/s320/front_curves_short.png" width="285" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJqUuwEnCZYtrZtwrFeKFX508nN7FukHZbKrg7fL0A7vwNQRoC4pYU5-1t-F2Bydlnz6PriQCZLQkpf7xLss32Su1CIcVhgb14KBQ9D1wRk4ztYe-r6-RXWLE6FpBh5P2MJLS5QT003xb7/s1600/front_all_short.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJqUuwEnCZYtrZtwrFeKFX508nN7FukHZbKrg7fL0A7vwNQRoC4pYU5-1t-F2Bydlnz6PriQCZLQkpf7xLss32Su1CIcVhgb14KBQ9D1wRk4ztYe-r6-RXWLE6FpBh5P2MJLS5QT003xb7/s320/front_all_short.png" width="303" /></a></div>
</div>
<br />
<br />Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com4tag:blogger.com,1999:blog-4665036873052018975.post-140457203296774242013-10-16T21:04:00.003-07:002013-10-16T21:04:44.440-07:00Droid Game Rig: Collision to Animation testAn early test for a droid game rig. The setup includes a 'breakable' mode where the the rig's <i>'free ctrls'</i> can move individual body parts freely in world space. On top of that a system was needed that would allow us to simulate collisions for quick animations down onto the controls without weighing down the scene. Once done the simulation can be baked to the controls so that we could continue to animate the controls for cleanup and finessing.<br />
<br />
Model: Roberto Saavedra<br />
<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="//player.vimeo.com/video/77103393" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/77103393">Droid Game Rig - Collisions to Animation test</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-80112886417027140702013-10-14T06:25:00.001-07:002013-10-16T19:53:58.837-07:00Clicker Game Rig<div style="text-align: center;">
</div>
<div style="text-align: left;">
Early iteration of animation rig for a game project. Character based on the zombie creature from 'Last of Us'.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Contribution: Retopology and rigging.</div>
<div style="text-align: left;">
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1reb9OWKdvDGHwZbck95I8mW-LMcZKZ0xOGm1P1iUwHt4tzcJCv0eXx2S-iULLbwX3y3kd1gzVHWGcb8tuRMa6G4k7Jv_eka5_syi8oqKbc25qqna84zgna_H7H92XUi_kwn1QX7t0NOX/s1600/clicker_textured.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1reb9OWKdvDGHwZbck95I8mW-LMcZKZ0xOGm1P1iUwHt4tzcJCv0eXx2S-iULLbwX3y3kd1gzVHWGcb8tuRMa6G4k7Jv_eka5_syi8oqKbc25qqna84zgna_H7H92XUi_kwn1QX7t0NOX/s400/clicker_textured.jpg" width="315" /></a></div>
<br />
<br /></div>
<div style="text-align: left;">
Animation: Fabio Ribak</div>
<div style="text-align: left;">
Model: Roberto Saavedra</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="//player.vimeo.com/video/76866084" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/76866084">Clicker Game Rig</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-31351165936262673392013-09-30T12:00:00.000-07:002013-10-14T06:00:34.090-07:00lpPoser Tool<div class="separator" style="clear: both; text-align: left;">
Updates coming soon!</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_kHRF5C1cXQhb1_nwJ8ROLa-PDFWt-bW2BfA3cxcivqZ9QlZ4r6ywS2o1PPBytZ0Tllbx9bDKqrmOq8rjQpitFCr-KjaChZQFX6DFvNAMeOUjL2icNm-Sgiolk37tTkk6eGrbPqqiFr3v/s1600/Screen+shot+2013-09-30+at+2.54.30+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="238" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_kHRF5C1cXQhb1_nwJ8ROLa-PDFWt-bW2BfA3cxcivqZ9QlZ4r6ywS2o1PPBytZ0Tllbx9bDKqrmOq8rjQpitFCr-KjaChZQFX6DFvNAMeOUjL2icNm-Sgiolk37tTkk6eGrbPqqiFr3v/s400/Screen+shot+2013-09-30+at+2.54.30+PM.png" width="400" /></a></div>
<br />
<br />
<b>Proxy Pose</b><br />
<br />
<div style="text-align: center;">
<span style="font-weight: bold;"><br /></span></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="//player.vimeo.com/video/75927641" webkitallowfullscreen="" width="500"></iframe></div>
<br />
<div style="text-align: center;">
<a href="http://vimeo.com/75927641">lpPoser - Proxy Pose</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
<br />
<br />
<b>Motion Blur</b><br />
<div style="text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="//player.vimeo.com/video/76830527" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/76830527">lpPoser - Motion Blur</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-43491829327562991092013-09-30T11:39:00.000-07:002013-09-30T11:47:27.486-07:00Rotation Select Axis<div class="separator" style="clear: both; text-align: left;">
Handy little tool to select individual rotation axis handle quicker. Good also for when manipulator gets in the way of select correct axis, and when in gimbal lock.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="//player.vimeo.com/video/75799216" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/75799216">Rotation Select Axis</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Its a short one, so just quicker to copy and paste :)</div>
<div class="separator" style="clear: both; text-align: left;">
Here's the code</div>
<pre class="brush: python">import maya.cmds as cmds
from functools import partial
def rotateContext(axis, *args):
cmds.manipRotateContext( 'Rotate', e=True, ah=axis)
if cmds.window("rotateContextUI", exists=True):
cmds.deleteUI("rotateContextUI")
win = cmds.window("rotateContextUI", title="Rotate Axis UI", w=150, sizeable=False)
cmds.rowColumnLayout(nc=3, columnAttach=[(1,"both", 2), (3, "both", 2)])
cmds.button("X", w=30, bgc=[1,0,0], c=partial(rotateContext, 0))
cmds.button("Y", w=30, bgc=[0,1,0], c=partial(rotateContext, 1))
cmds.button("Z", w=30, bgc=[0,0,1], c=partial(rotateContext, 2))
cmds.showWindow(win)
</pre>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-11958463175401001232013-09-07T23:24:00.002-07:002013-09-08T16:18:35.699-07:00lpDynamicChains<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
This a reference page for the <b>lpDynamicChains</b> tool.</div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
<br /></div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
<i><b><span style="font-size: large;">DESCRIPTION</span></b></i><br />
<i><b><br /></b></i></div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
This tool allows for the creation of dynamic joint chains. User can specify joints to become dynamic. Can also turn any geometry into a collision object. Simulations can be cached to a file or baked as animation onto joints and dynamic nodes can be removed when done.</div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
<i><br /></i></div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
<i>Suggested use:</i> for tails, appendages, for secondary delayed movement - ex: jiggle<br />
<br /></div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
</div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="//player.vimeo.com/video/74035143" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/74035143">lpDynamicChains.py - Tool Demo</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
</div>
<div style="-qt-block-indent: 0; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; text-indent: 0px;">
<br /></div>
<div class="p1">
<i>File:</i> lpDynamicChains.py</div>
<div class="p1">
<br /></div>
<div class="p1">
<i>Versions:</i> 1.0 - 06/28/2013 - first version created</div>
<div class="p1">
1.1 - 07/02/2013 - changed curve degree to work with 2-3 joints chain. fixed</div>
<div class="p1">
complete removal of dynamic nodes for delete dynamic</div>
<div class="p1">
2.0 - 07/13/2013 - added create collision object option</div>
<div class="p1">
2.1 - 08/15/2013 - updated collision feature and entire tool to use nDynamics</div>
<div class="p1">
and the nucleus solver unifying all simulations under one solver</div>
<div class="p1">
2.2 - 08/19/2013 - added option to bake sim onto joint</div>
<div class="p3">
<br /></div>
<div class="p1">
<b><i><span style="font-size: large;">USAGE NOTES</span></i></b></div>
<div class="p1">
<b>The UI</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFRWq64Dwg9R6neys-vmZ1LOxb-nv7jhy-CKUBF3fah192KbRBCKA8LBxOmeXkWHw1RPTvimFi9WJ2kUnLXi6g0ohaRvpKPIEp8lnXaW6KOR_17Kyx6oAu2fiCUGVC4rRBZRGmclyFvugr/s1600/Screen+shot+2013-09-04+at+5.35.58+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgFRWq64Dwg9R6neys-vmZ1LOxb-nv7jhy-CKUBF3fah192KbRBCKA8LBxOmeXkWHw1RPTvimFi9WJ2kUnLXi6g0ohaRvpKPIEp8lnXaW6KOR_17Kyx6oAu2fiCUGVC4rRBZRGmclyFvugr/s320/Screen+shot+2013-09-04+at+5.35.58+PM.png" width="248" /></a></div>
<div class="p1">
1. Place script in</div>
<div class="p1">
Windows: C:/Program Files/Autodesk/Maya2013/Python/lib/site-packages/ </div>
<div class="p1">
Mac: Users/userName/Library/Preferences/Autodesk/maya/2013</div>
<div class="p1">
-x64/scripts/</div>
<div class="p1">
2. To open the UI:</div>
<pre class="brush: python">import lpDynamicChains
reload(lpDynamicChains)
lpDynamicChains.UI()</pre>
<div class="p1">
The UI allows for the creation and interaction of dynamic chains in the scene. It also allows access to lpDynamicChains commands<br />
<br /></div>
<div class="p1">
<b>Create Dynamic joints</b><br />
<b><br /></b></div>
<div class="p1">
Select the start joint, shift select the end joint and click the 'Make Joints Dynamic' button under the <b>Main </b>section.
<br />
<br />
Commands:<br />
<pre class="brush: python">from lpDynamicChains import *
dynChain = lpDynamiChain(startJoint, endJoint)
dynChain.create()
def lpDyanmicChain(startJoint="", endJoint="", node=""):
'''
Create the custom node
Parameters:
startJoint - Name of the first joint in the chain
endJoint - Name of the last joint in the chain
node - Name of the node to manage by the class
Returns:
Nothing
'''
def create(self):
'''
Create the actual dynamic joint chain node network
Parameters:
Nothing
Returns:
List of all new created nodes
'''
</pre>
A custom locator node will be created at the start joint's position holding the parameters to be tweaked to customize the dynamic behavior of the joint chain.<br />
<br />
All nodes created will be stored as meta data on the nodes themselves to allow for the tool to keep track of the nodes associated with every newly created dynamic chain.<br />
<br />
You will get a window prompting which nucleus to connect the dynamicChain to.<br />
<br />
A log window will display at the end of creation, listing all nodes created with the new dynamicChain.<br />
<br />
<div class="p1">
<b>Remove Dynamic Nodes</b></div>
<div class="p1">
<br />
Select any node created for the dynamic chain to be deleted or the dynamic chain locator at the start joint, and click the 'Remove Dynamic' button under the <b>Main </b>section.<br />
<br />
Commands:<br />
<pre class="brush: python">from lpDynamicChains import *
dynChain = lpDynamicChain(node=node)
dynChain.delete()
del(dynChain)</pre>
<i>Note: </i>Removing dynamic nodes will also check for any unused nucleus nodes and remove them as well.<br />
<br />
All nodes created for the dynamic chain to be deleted will be removed from the scene returning it to its original state before the creation of the dynamic chain.<br />
<br />
<b>Iterate Through All Dynamic Chain Nodes</b><br />
<br />
Commands:<br />
<pre class="brush: python"># create instance
dynChain = lpDynamicChain()
# create iterable for all dynamic nodes in the scene
dynChains = dynChain.Iter()
for d in dynChains:
print d
</pre>
<b>Adding Collision Objects</b><br />
<br />
Select the locator for the dynamic chain to be edited, shift select the geometry to be turned into a collision object and click the 'Create Collision Object' button under the <b>Options </b>section.<br />
<br />
Geometry object will be turned into a passive nRigid object to interact with the dynamic joints. A nRigid node will be created and stored in an array attribute keeping track of all collider nodes interacting with it.<br />
<br />
<b>Creating nCache</b><br />
<b><br /></b>Select the locator or multiple locators for the dynamic chain(s) to be cached and click the 'Cache Simulation' button under the <b>Advanced -> nCache </b>section.<br />
<br />
The nCache option window will display so you can choose how to save out the cache file (or multiple files) for the dynamic simulations. For more information please refer to the <a href="http://download.autodesk.com/global/docs/maya2014/en_us/index.html?url=files/nCache__Create_New_Cache.htm,topicNumber=d30e544511" target="_blank">Autodesk Maya Help Docs</a><br />
<br />
<b>Removing nCache</b><br />
<b><br /></b>
Select the locator for the dynamic chain to be cached and click the 'Delete Cache' button under <b>Advanced -> nCache </b>section.<br />
<br />
<i>Note: </i>Removing dynamic nodes for a given dynamic chain will not delete the cache file on disk.<br />
<br />
The Delete nCache option window will display. If there are multiple cache nodes for the dynamic chain, you will be able to select which ones to delete.<br />
<br />
The option to delete or keep the cache files on disk will also be available.<br />
<br />
<b>Baking Simulation to Joints</b><br />
<b><br /></b>
Select the locator for the dynamic chain to be bake the simulation for and click the 'Bake Simulation' button under <b>Advanced -> Animation</b><br />
<b><br /></b>
<b><span style="font-size: large;"><i>Utility Commands</i></span></b><br />
<b><br /></b>
In addition to the UI functions, you can also access utility commands available with the script. Below are some examples.<br />
<b><br /></b>
<b>Get and Set the Active Nucleus</b></div>
</div>
<pre class="brush: python">from lpDynamicChains import *
nucleus = getActiveNucleus()
setActiveNucleus(nucleus)
def getActiveNucleus():
'''
Query the active nucleus node
Parameters:
Nothing
Returns:
Name of active nucleus node
'''
def setActiveNucleus(nucleus):
'''
Set the active nucleus node
Parameters:
nucleus - Name of nucleus node to set as current active nucleus
Returns:
Nothing
'''</pre>
<b>Get Connected</b><br />
<b><br /></b>
Get commands to find the connected DynamicChain node or the nucleus node connected to the dynamicNode<br />
<pre class="brush: python">from lpDynamicChains import *
# get selected object
obj = cmds.ls(sl=True)[0]
dNode = getConnectedDynamicChain(obj)
nucleus = getConnectedNucleusNode(dNode)
def getConnectedDynamicChain(node):
'''
Look for a valid DynamicChain node connected to the given node
Parameters:
node - Name of the node to find connection to DynamicChain
Returns:
Name of DynamicChain node connected to given node
'''
def getConnectedNucleusNode(node):
'''
Look for the nucleus node connected to the given node
Parameters:
node - Name of the node to find connection to nucleus
Returns:
Name of the nucleus node connected to the given node
'''
</pre>
<b>Checks</b><br />
<br />
Check commands to verify if node is a DynamicChain node or node is a nucleus compatible nDynamics node.<br />
<pre class="brush: python">from lpDynamicChains import *
# get selected object
obj = cmds.ls(sl=True)[0]
if isDynamicChain(obj):
dynChain = lpDynamicChain(obj)
# access attribute
hairShape = dynChain.hairSystemShape[0]
return isNType(hairShape, "hairSystem")
def isDynamicChain(node):
'''
Look for the identifier to check if node is 'dynamic'
Parameters:
node - Name of node to check for 'nodeType'
Returns:
Boolean value if node is dynamic or not, True or False
'''
def isNType(node, nodeType):
'''
Check if the given node is a nucleus compatible nDynamics node
Parameters:
node - Name of node to check for 'NType' compatibility
nodeType - Node type to check
Returns:
Boolean value if node is compatible with nDynamics solver
'''
</pre>
<b>Number of Colliders</b><br />
<br />
Check how many collision objects are attached to the node<br />
<pre class="brush: python">from lpDynamicChains import *
# get selected object
obj = cmds.ls(sl=True)[0]
if isDynamicChain(obj):
numColliders = findNumberOfCollisionObjects(obj)
def findNumberOfCollisionObjects(node):
'''
Find the number of collision objects attached to the node
Parameters:
node - Name of the node to check for collision objects
Returns:
Integer number of collision objects attached to the node
'''
</pre>
<b>Delete Unused Nucleus Nodes</b><br />
<br />
Check nucleus nodes in the scene for their connections and remove any that are not being used.<br />
<pre class="brush: python">from lpDynamicChains import *
deletedNodes = deleteUnusedNucleusSolvers()
def deleteUnusedNucleusSolvers():
'''
Delete all nucleus nodes not being used
Parameters:
Nothing
Returns:
List of nucleus node deleted
'''
</pre>
<b>Create and Connect</b><br />
<br />
Creation and connection commands to create new nucleus nodes, nRigid nodes, connect DynamicChain node to nucleus or to connect a nRigid node to a nucleus.<br />
<pre class="brush: python">from lpDynamicChains import *
# get selected objects
selected = cmds.ls(sl=True)[0]
dNode = selected[0]
mesh = selected[1]
nucleus = createNucleus()
if isDynamicChain(dNode):
nucleus = connectToNucleus(dNode, nucleus)
nRigid = createNRigid(mesh, nucleus)
index = connectNRigidToNucleus(nRigid, nucleus)
def createNucleus(name="", setActive=True):
'''
Create nucleus node
Parameters:
name - Name for the new nucleus node
setActive - Boolean to set the new nucleus as the current active nucleus
Returns:
Name of new nucleus node
'''
def createNRigid(obj, nucleus=""):
'''
Create a nRigig node from the given obj
Parameters:
obj - Name of the geo to create nRigid from
nucleus - Name of the nucleus to connect nRigid to
Returns:
Name of new nRigid node
'''
def connectToNucleus(node, nucleus):
'''
Connect the given node to the nucleus node
Parameters:
node - Name of the node to connect to the nucleus solver
nucleus - Name of nucleus solver to connect to
Returns:
Name of nucleus node (debug)
'''
def connectNRigidToNucleus(nRigid, nucleus, newNucleus=True):
'''
Connect the given nRigid node to the nucleus node, maintaining prior connections to
other nucleus nodes
Parameters:
nRigid - Name of nRigid node to connect to nucleus
nucleus - Name of nucleus node to connect
newNucleus - Boolean to create a new nucleus node if the specified nucleus doesn't exist
Returns:
Integer index of next available passive nRigid for the given nucleus
'''
</pre>
<b>Access connections</b><br />
<br />
Command to get all nodes connected to custom node, like hairSystem, or the ikHandle. Access custom attributes through class _getattr_ method<br />
<div>
<br /></div>
<pre class="brush: python">from lpDynamicChains import *
# get selected object
obj = cmds.ls(sl=True)
if isDynamicChain(obj):
dNode = lpDynamicChain(node=obj)
nodes = dNode.listConnections()
# get hairSystemShape node
hairShape = dNode.hairSystemShape[0]
def listConnections(self):
'''
Wrapper to get all connections to the node to message attributes
Parameters:
Nothing
Returns:
List of connected nodes to all custom message attributes
'''
</pre>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com2tag:blogger.com,1999:blog-4665036873052018975.post-3766026328179187132013-09-04T20:36:00.001-07:002013-09-04T20:36:38.477-07:00Node Based Pivot SwitchingHello guys. I was going to write a post to show how you can setup pivot switching to be built in your own rigs using native maya nodes.<br />
<br />
However, this time, I thought just a video would cover what you need to know.<br />
<br />
As you will see, this is a fairly straightforward approach once you've done it once. It just requires a little creativity. The idea here obviously is to look for an alternative to setting pivot switching for your controls, so you can interactively switch the pivot of your controls, without having to rely on an external tool or script.<br />
<br />
If you need to refer back at some of the steps, here are some notes.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk6t5WMfvtjIxyn6bZSDkuvCw7IpuYED9IuqxzgZ6qHrvCXCGnNj_q0IY87ktGyQEpe4vALWVDcCYa6T9Ij8Y0coLVl8jHHTGqvHPpKtF-a0fCafJymd3adwTWsk-3lFrQLY3FQt2ZXih1/s1600/pivotSwitching.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjk6t5WMfvtjIxyn6bZSDkuvCw7IpuYED9IuqxzgZ6qHrvCXCGnNj_q0IY87ktGyQEpe4vALWVDcCYa6T9Ij8Y0coLVl8jHHTGqvHPpKtF-a0fCafJymd3adwTWsk-3lFrQLY3FQt2ZXih1/s400/pivotSwitching.jpg" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Here is the video.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="//player.vimeo.com/video/72768474" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/72768474">Node Based Pivot Switching - Tutorial</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
<div style="text-align: center;">
<br /></div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-81022912965658623942013-09-04T14:59:00.002-07:002013-09-04T15:01:22.554-07:00Dynamic Chains Comeback<div style="text-align: left;">
I did a video awhile back showing how to setup dynamic joint chains using Maya Hair. The idea was to show a quick and easy process to get those working. Since then I put together a tool to automate the setup process and to allow for easy management of multiple dynamic joint chains in the scene.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
This is the original video posted.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="281" mozallowfullscreen="" src="//player.vimeo.com/video/65481063" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/65481063">Dynamic Chains - Maya Hair - Tutorial</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
<div style="text-align: center;">
<br /></div>
<div style="text-align: left;">
The tool was planned out, and I've been updating it as I used it and as needed. Here is the original plan for the tool.</div>
<div style="text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvcgQtvBL7xanucYnCRDgJ4Nx-tJJrM_0j9kOmptTxAJUfkBWm58hD45OStgt0QGbbUdAs0L8sntIDCjmJdEFH5Z8Mp5AY4mJ7TQoVPk9sP0GA_GoL-yufhboHApn24EbPHTB1RzQa9D25/s1600/dynamicJointChain.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgvcgQtvBL7xanucYnCRDgJ4Nx-tJJrM_0j9kOmptTxAJUfkBWm58hD45OStgt0QGbbUdAs0L8sntIDCjmJdEFH5Z8Mp5AY4mJ7TQoVPk9sP0GA_GoL-yufhboHApn24EbPHTB1RzQa9D25/s400/dynamicJointChain.jpg" width="400" /></a></div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
In the process, the setup also got update to utilize nDynamics and the nucleus solver, unifying all dynamic simulations under one solver.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
In summary, the tool will allow the creation of multiple dynamic joint chain driven by nHair. The user will also be able to turn any geometry into a collision object, in order to have the joint interact with it. The simulations created with the setup can also be cached to a file or baked as animation onto joints, through the tool, and the dynamic nodes can be removed at any point in time.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
I'll be making the tool script available soon in the Downloads section, and I will be posting a reference page with instructions on using the tool's UI.</div>
<div style="text-align: left;">
<br /></div>
<div style="text-align: left;">
Hope you guys enjoy it. Any feedback or comments will be very appreciated.</div>
<div style="text-align: left;">
<br /></div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-5354563173070025522013-08-30T17:42:00.002-07:002013-10-11T16:48:25.570-07:00Lion Rig UpdateUpdate on a quadruped rig for a Lion model.<br />
Model credit: Attakarn Vachiravuthichai<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihLpwoMF99RJkhYUUXA-XaN-jHdO38S3qtKf8yJSq4DQSDXChyphenhyphen_C3t3Jt8I0Hg_a66vP0jio13wJkft2ZauMDX4VgGKUP3VS_L8C3aeUj-KwYHSsYYAMgCGBq9ziMdrpxksB1COCig-ign/s1600/Screen+shot+2013-08-30+at+8.28.26+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEihLpwoMF99RJkhYUUXA-XaN-jHdO38S3qtKf8yJSq4DQSDXChyphenhyphen_C3t3Jt8I0Hg_a66vP0jio13wJkft2ZauMDX4VgGKUP3VS_L8C3aeUj-KwYHSsYYAMgCGBq9ziMdrpxksB1COCig-ign/s320/Screen+shot+2013-08-30+at+8.28.26+PM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBuaktYFcE-BLaoZXulS7IGRzUN8XC011bDjS9ihvlS0lr98H-y1Y0rmrU4nyRl6EMP5hEp1w30h9jUCHY6qqfTt0JJ5Rf2wi_YaZAJI0A1OHZL3NCvIbfXMvCyWLX4uyHGzjSjd8KebFP/s1600/Screen+shot+2013-08-30+at+8.29.15+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="166" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiBuaktYFcE-BLaoZXulS7IGRzUN8XC011bDjS9ihvlS0lr98H-y1Y0rmrU4nyRl6EMP5hEp1w30h9jUCHY6qqfTt0JJ5Rf2wi_YaZAJI0A1OHZL3NCvIbfXMvCyWLX4uyHGzjSjd8KebFP/s320/Screen+shot+2013-08-30+at+8.29.15+PM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqozeE0QnVWh79vH56txq6nrNjP8ypPM3XW3tkEXqg8i6MW3DsOhzSQkEpeDb6q-JES8wdSQXEWy8VGAkJnM7jrZqy20xURra4KePGAEoiTIVzjcx5bFdHJQIhFRZpAd8vvKQTpyxyoO1A/s1600/Screen+shot+2013-08-30+at+8.26.31+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="167" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqozeE0QnVWh79vH56txq6nrNjP8ypPM3XW3tkEXqg8i6MW3DsOhzSQkEpeDb6q-JES8wdSQXEWy8VGAkJnM7jrZqy20xURra4KePGAEoiTIVzjcx5bFdHJQIhFRZpAd8vvKQTpyxyoO1A/s320/Screen+shot+2013-08-30+at+8.26.31+PM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="260" mozallowfullscreen="" src="//player.vimeo.com/video/73501235" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/73501235">Lion Rig Walk Cycle Test 1</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com4tag:blogger.com,1999:blog-4665036873052018975.post-68782237419705636932013-08-11T20:43:00.001-07:002013-08-11T20:55:27.115-07:00Joint RelocationIn this post I want to show you a quick and easy way of reposing your joint layout to make it easier for you to reuse a skeleton if you already have one laid out that you want to reuse. The idea here is to be able to just worry about where the joint is positioned, and not have to worry about maintaining joint orientation.<br />
<br />
To accomplish this we will rely mainly on one thing, and that is an aim constraint, to allow us to aim the rotation axis we want down the joint chain.<br />
<br />
Ok, so to break it down, lets just start with a 3 joint chain. To visualize this better lets go ahead and turn <b>Local Rotation Axis </b>on for all joints.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCxM4FtXuO_PWtdp4O1U5nFoPtZVK-rTrlyJO0XN8iEc04oYU3A3DXNbQ-q414G2iVqA76lnmyakGtUd7YAmP0iJdnXV0vB95E8vd9LhAizjWnSea-YCxNQ6k19v4KK9OvIpl_ZvVYsY0H/s1600/LRA.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="153" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCxM4FtXuO_PWtdp4O1U5nFoPtZVK-rTrlyJO0XN8iEc04oYU3A3DXNbQ-q414G2iVqA76lnmyakGtUd7YAmP0iJdnXV0vB95E8vd9LhAizjWnSea-YCxNQ6k19v4KK9OvIpl_ZvVYsY0H/s320/LRA.png" width="320" /></a></div>
<br />
By default we have the <i>x </i>axis going down the joints. To keep our joint intact we will use locators to actually move around the joints to reposition them. The aim constraints will be applied to these locators.<br />
<br />
So we need to start by breaking up the joint hierarchy. Parent each to world, you can do that by hitting <i>Shift + P</i> with the joint selected.<br />
<br />
We will then create a locator at each joints position. In fact we want to maintaing the joints orientation as well. So for each joint, select the joint, select the locator, and goto <b>Constraint -> Parent Constraint. </b>We want to uncheck <i>maintain offset</i>. That will position and orient the locators to match the joints. We can then delete the constraint, and parent the joint under the locator.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2sY-VhOjjuaHbiN6ywqVIahDWoTymPfxuQ20a8Qym3A5z1X4zsUyDHshq6N5A-8c3vSp9rIA4BXTL8oBme8saLuoVmjA-JN2pGHngOHmQrEB-lU_AgqU4sdZRYQlbUXLfEB0tuaVGt8VH/s1600/parentconstraint.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="222" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj2sY-VhOjjuaHbiN6ywqVIahDWoTymPfxuQ20a8Qym3A5z1X4zsUyDHshq6N5A-8c3vSp9rIA4BXTL8oBme8saLuoVmjA-JN2pGHngOHmQrEB-lU_AgqU4sdZRYQlbUXLfEB0tuaVGt8VH/s320/parentconstraint.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2qbtT53shFRxFPYIq7cdpMGiMKTGrhHQX4IrVAmTCxdUvfLIg58b-dH2U0gLIB3c4svac10Jx9yWKTVdtXz3nx8QPOCRSmS6Z2S45W6DX50Mbzb3aiFG50-bFrIFBauB3umXoHGwUjVZc/s1600/parent+joints.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi2qbtT53shFRxFPYIq7cdpMGiMKTGrhHQX4IrVAmTCxdUvfLIg58b-dH2U0gLIB3c4svac10Jx9yWKTVdtXz3nx8QPOCRSmS6Z2S45W6DX50Mbzb3aiFG50-bFrIFBauB3umXoHGwUjVZc/s1600/parent+joints.png" /></a></div>
Now we go ahead and set the aim constraints. This will mimic our original hierarchy and allow us to keep the joint orientation.<br />
<br />
Select the second locator and shift select the first locator. And goto <b>Constraint -> Aim Constraint.</b><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6CLPnwjbbmS2kjaNd36jp1_0OguC0qIDHwJ2Y9zJa-pX6J1PYBxS-kLTuSwZtKCF3FoBy8ts4BNFRLC0yw5lescpIbQdifgvqA9i2gMVCgpcCJkfGS7bpqQBAjala74H-WtBgHTUvu_kb/s1600/aim+constraint.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6CLPnwjbbmS2kjaNd36jp1_0OguC0qIDHwJ2Y9zJa-pX6J1PYBxS-kLTuSwZtKCF3FoBy8ts4BNFRLC0yw5lescpIbQdifgvqA9i2gMVCgpcCJkfGS7bpqQBAjala74H-WtBgHTUvu_kb/s320/aim+constraint.png" width="320" /></a></div>
We can repeat the steps for the third and second locators.<br />
We can now relocate the joints. When we are ready we can unparent the joints (<i>Shift + P</i>) and parent the joints back into the hierarchy.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhYVaAKLx_qZkeS_3ncN80WLJVOsI7R2QRmJrSE2gys6fGNoD1UA-WgbHQvBSd226cF9mrAiGR9J9aKzD2ypnxdkI1VtTg5xZ5qmIx3fQk7lYJwSA45Q0Zj6_0m1JlYE8ngj8BjIHC5Egp/s1600/reposition.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhhYVaAKLx_qZkeS_3ncN80WLJVOsI7R2QRmJrSE2gys6fGNoD1UA-WgbHQvBSd226cF9mrAiGR9J9aKzD2ypnxdkI1VtTg5xZ5qmIx3fQk7lYJwSA45Q0Zj6_0m1JlYE8ngj8BjIHC5Egp/s320/reposition.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyRpIA9ljWItBpY6eBklK3eyZsznNNIwrM_t3WRnYiZvbDcj9VWj6KRnlkDlonLSGpfFzJE0awuIZR52jc1oUNYJKr4nMG0x_dUC3JoKvkmEovwJOB5OpVbVXJzaXhKn5Lf35XVdR-TKIy/s1600/unparent+joints.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjyRpIA9ljWItBpY6eBklK3eyZsznNNIwrM_t3WRnYiZvbDcj9VWj6KRnlkDlonLSGpfFzJE0awuIZR52jc1oUNYJKr4nMG0x_dUC3JoKvkmEovwJOB5OpVbVXJzaXhKn5Lf35XVdR-TKIy/s1600/unparent+joints.png" /></a></div>
<br />
We can then delete the locators, and everything we be gone with them.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimty1YYgs3SabYrnW_iiRDA4xVUtICCbn_DQCFUFMkRqmNtU6IP2jXP4U6ebytqxmT1PDZZak73dVx5X7vP93JfAeqQ9Z_gcsiaU_2IIEA2TidogT6FdXttIuNjLyDJT4ljOEm7FIUJsdq/s1600/reparent.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="248" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEimty1YYgs3SabYrnW_iiRDA4xVUtICCbn_DQCFUFMkRqmNtU6IP2jXP4U6ebytqxmT1PDZZak73dVx5X7vP93JfAeqQ9Z_gcsiaU_2IIEA2TidogT6FdXttIuNjLyDJT4ljOEm7FIUJsdq/s320/reparent.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
And there we go! Now, obviously it wouldn't be too efficient to have to redo this every single time. So if you want, check out <i>lpJointRelocation.py</i><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJdPnicDHxOYM7jdNZ7QtNZQZ_gUOX1w0QEGQpHYSGOdIY-pcs-T4a9uo6wJmNTyoDCm6p2RUd9jLlJF9L85f4d4CHNeXdCBRVjLQO5WPgcJweP0YDKWUCp6OO7hLD_vBF604tm4EX09iP/s1600/interface.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhJdPnicDHxOYM7jdNZ7QtNZQZ_gUOX1w0QEGQpHYSGOdIY-pcs-T4a9uo6wJmNTyoDCm6p2RUd9jLlJF9L85f4d4CHNeXdCBRVjLQO5WPgcJweP0YDKWUCp6OO7hLD_vBF604tm4EX09iP/s1600/interface.png" /></a></div>
<i><br /></i>
And here is a quick overview of how to use it.<br />
<br />
<b>Break Hierarchy:</b> This will break down the hierarchy, create locators and constraints, and hook up message attributes to manage the connections with the tool. Select the first joint in the hierarchy you want to adjust.<br />
<br />
<b>Rebuild Hierarchy: </b>To put the joints back into the hierarchy in the new position. This will also get rid of the created nodes that are no longer required. Select the first locator.<br />
<br />
<b>Select Hierarchy: </b>To make it easier to select all the joint in the hierarchy starting from the selected joint.<br />
<b><br /></b>
<b>Toggle Local Axis: </b>To toggle on and off the local rotation axis to help with some visual feedback.<br />
<br />
Hope you guys find it useful!Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-55300371188550599752013-08-06T21:57:00.000-07:002013-08-06T22:04:41.098-07:00Turning Off Local Rotation AxisIf you ever have been frustrated with the local axis toggle in maya like I once was you might find this little script useful. Sometimes you forget or loose track of what you've toggled on. Today I saw a friend spend so much time trying to figure out what node has local axis on, that while it was kinda of funny, I felt it couldn't be a bad thing to share something that can fix it in just a few lines. As I'm sure there are more people who have struggled with this at some point. So, using good old fashion MEL, you can just create a shelf button and copy these lines<br />
<br />
<pre class="brush: python">// Turn off LRA for all the selected objects<br />
$sl = `ls -sl`;
for ($s in $sl)
{
$on = `toggle -q -localAxis $s`;
if ($on == 1)
{
toggle -localAxis $s;
}
};
</pre>
You can now select the objects you want and click the shelf button to run the code, and it will toggle only the ones that are on. Essentially making sure everything is off.Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-69465296163687901252013-08-03T14:56:00.001-07:002013-08-03T14:59:30.080-07:00Extracting Twist Controls for IK Spline<div class="separator" style="clear: both; text-align: left;">
Being able to have twisting propagate down a joint chain is a common feature for character rigs. An automated approach over a manual approach might be a quicker and easier solution, however as we'll discuss below might end up presenting problems. A manual method of animating twisting in the other hand while giving exactly what the animator want, is also more work for the animator. There are many ways to setup twisting, each with their own advantages and issues. However, this post won't be about the best solution for twisting, so much as it will be a practical solution for providing the animator with more control over the twist action of the joints by having both an automated and a manual solution.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
I've tried many different setups and upon arriving at this solution I found it to be the most stable and practical for when dealing with twisting.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Watch the video demonstrating the setup <a href="https://vimeo.com/71650524" target="_blank">here</a>.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
So let's get started. Before we start lets create a quick setup to try this out. The first thing we will need is a Spline IK driven joint chain. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIBZ9lBr7HE6SI4tJEGEKTUPVJKcvTj4NKvt6uSugR0zS5b0505of4Yxh6EJDOm9dNqakk9PNPuLyEo1TOJk165HfCehku1M48kHJxE6EtbfGPoeAKTvjej257l30u_BuHolgnYjq1xHVK/s1600/spline+ik.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="115" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiIBZ9lBr7HE6SI4tJEGEKTUPVJKcvTj4NKvt6uSugR0zS5b0505of4Yxh6EJDOm9dNqakk9PNPuLyEo1TOJk165HfCehku1M48kHJxE6EtbfGPoeAKTvjej257l30u_BuHolgnYjq1xHVK/s320/spline+ik.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Next, we will need to make our IK controls. To keep everything simple for now, lets just use empty transform nodes for our controls. As an observation, this step isn't really necessary, we just want to have a way of driving the curve shape to drive the joint's position.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Let's start by making a single joint, group it twice and name the nodes,</div>
<div class="separator" style="clear: both; text-align: left;">
<b><i>GRP_Start_IK, CTRL_Start_IK, jnt_StartCurveDriver</i></b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Duplicate the top group and rename the new nodes,</div>
<div class="separator" style="clear: both; text-align: left;">
<b><i>GRP_End_IK, CTRL_End_IK, jnt_EndCurveDriver</i></b></div>
<div class="separator" style="clear: both; text-align: left;">
<b><i><br /></i></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5RAqh-SRrqpP-9O-XBzcHtABlXVZjuIVAuo_DMoJEQD8NUgXCm01RnXoStuhraG3xhbShH4WgBT0lVKfvuKuUx8CsxnsVdp_L2EFiGywi5PSU6Bvx56o8iAQvJqdiMByQYoEiz8kVfztm/s1600/control+hierarchy.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi5RAqh-SRrqpP-9O-XBzcHtABlXVZjuIVAuo_DMoJEQD8NUgXCm01RnXoStuhraG3xhbShH4WgBT0lVKfvuKuUx8CsxnsVdp_L2EFiGywi5PSU6Bvx56o8iAQvJqdiMByQYoEiz8kVfztm/s200/control+hierarchy.png" width="168" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Now we can move the corresponding groups to the start and end of the joint chain.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7X5fMsy9W9vlvOauANHwWgP7hQ6YzjpulexMIyaIiCzUOlmTuTTZoiGSL4h2nLXAvC5R7DBsp-kVj-29GvputYVd23T_Md0QTwHU1NOLjw4EnMI0Q0GcVDavS09zoMlwCqQ5eNxobhN6Z/s1600/driver+joints.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="108" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7X5fMsy9W9vlvOauANHwWgP7hQ6YzjpulexMIyaIiCzUOlmTuTTZoiGSL4h2nLXAvC5R7DBsp-kVj-29GvputYVd23T_Md0QTwHU1NOLjw4EnMI0Q0GcVDavS09zoMlwCqQ5eNxobhN6Z/s320/driver+joints.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Finally we can select the driver joints and select the curve last, and goto <b>Skin -> Bind -> Smooth Bind</b></div>
<div class="separator" style="clear: both; text-align: left;">
we can then choose the following options</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFEShJdkcXu-ZOgWgmNa2AqK3_4_PgsADFe2eGJLT0Asbtyw8W-WrQ7N3iUyzOZw_75ZBbWuYvCscoe8yTTI47f3lflIJmwbQBLacJJKDk-EPHGK6BNLzmQ5-aqYqRO5-pJ7RTJN_A_vAB/s1600/smooth+bind.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="206" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFEShJdkcXu-ZOgWgmNa2AqK3_4_PgsADFe2eGJLT0Asbtyw8W-WrQ7N3iUyzOZw_75ZBbWuYvCscoe8yTTI47f3lflIJmwbQBLacJJKDk-EPHGK6BNLzmQ5-aqYqRO5-pJ7RTJN_A_vAB/s320/smooth+bind.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Now, we can drive the Spline IK with our controls. So this will be our starting point.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Notice that we can move and rotate our controls to drive the curve that is driving the joint chain. However, we can't seem to rotate the controls to get twisting. The reason for this is that components, such as the CVs on the curve only represent points in space, and because we don't get normals with curves, we can't get rotation information from the curve points. Now, some of you may be familiar with the <i>Advance Twist Controls</i> setup for spline IK. If not, you can check out the post here that goes over the setup.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
The advance twist setup is nice to quickly get twisting with your Spline IK. However, we mentioned that we want to have not only the option to toggle auto twisting on and off, but to also offset the twisting. In addition we will also look at how this new setup will give us a nice solution for a common problem that comes with using the advance twist controls. Lets take a look now at how we can extract the twist from the spline IK solver and give it to the animator to control and give them the option to automate or not the twisting.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Lets start by creating some attributes on our end control to drive the twisting. Lets create an <i>autoTwist </i>and a <i>twistOffset</i> attribute.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgfNUIjonREYuiuE4UodRmNrpsdmOJECnhmJLXkPofQdInwC7sXrh64wigFTBtaIHsnTwxw1QgLH3gB2piuZpTxBTzE7oM5WqwUPC94mXJZE5ydta8YlwJYUHJPEHqlYLRKFPc1eaJe65t/s1600/autoTwist.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhgfNUIjonREYuiuE4UodRmNrpsdmOJECnhmJLXkPofQdInwC7sXrh64wigFTBtaIHsnTwxw1QgLH3gB2piuZpTxBTzE7oM5WqwUPC94mXJZE5ydta8YlwJYUHJPEHqlYLRKFPc1eaJe65t/s320/autoTwist.png" width="241" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR6zD-y0pcXG6aYGejTw8-u5qcdCpwcmCnREESUG9Z9ZWXSEZdArEM4dsyp9RDk67CSY7-Y3P8Famucp1ij5oouiwGaUdSbxo7bMLRqT96rufIRstU_TmOkp85NZVNaxsHyxw4M94nFPyt/s1600/twistOffset.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjR6zD-y0pcXG6aYGejTw8-u5qcdCpwcmCnREESUG9Z9ZWXSEZdArEM4dsyp9RDk67CSY7-Y3P8Famucp1ij5oouiwGaUdSbxo7bMLRqT96rufIRstU_TmOkp85NZVNaxsHyxw4M94nFPyt/s320/twistOffset.png" width="244" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyr3gLdze60-SnOsGyK2Dv1j6bFj1hexuGKreAefv0bjp-WtDAsFkAaFtOFahdci0ZH1EOjj4cfM1p3HlU18_lYrR41xA1jVx0FrQr14SQjvAUSYkN3iwWKy4NiC-_mT8D94s7xmWWTz71/s1600/attributes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="184" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgyr3gLdze60-SnOsGyK2Dv1j6bFj1hexuGKreAefv0bjp-WtDAsFkAaFtOFahdci0ZH1EOjj4cfM1p3HlU18_lYrR41xA1jVx0FrQr14SQjvAUSYkN3iwWKy4NiC-_mT8D94s7xmWWTz71/s320/attributes.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
We can now start creating and hooking up the math nodes to help us with this setup. The first thing we want to create is a <b>multiplyDivide</b> node to multiply the <i>rotationX </i>(that is our twist axis for our control in this case) of our end control by our <i>autoTwist</i> attribute value. Lets rename this <b>md_autoTwist </b>and hook up our attributes.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>CTRL_End_IK.rotateX</b> to <b>md_autoTwist.input1X</b></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>CTRL_End_IK.autoTwist </b>to <b>md_autoTwist.input2X</b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9dAvF40oxRh7C6kuA-0j08OLAG9XBTzQ_EwbkGzy8I93fJRqM8jsS0Yzj-sMFEevcBTHjbwgzgSICrDaNpaIFJwEgckLMUqtMiDQxmjUIfVbb42zF27VJXi7AOXKsbc41PNShxSacF5sT/s1600/autoTwist+conn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="203" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj9dAvF40oxRh7C6kuA-0j08OLAG9XBTzQ_EwbkGzy8I93fJRqM8jsS0Yzj-sMFEevcBTHjbwgzgSICrDaNpaIFJwEgckLMUqtMiDQxmjUIfVbb42zF27VJXi7AOXKsbc41PNShxSacF5sT/s400/autoTwist+conn.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Next we want to create a <b>plusMinusAverage </b>node to sum our <i>twistOffset </i>value to the output of the <b>multiplyDivide </b>node. So lets do that and rename this <b>pma_twistOffset</b>.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>md_autoTwist.outputX</b> to <b>pma_twistOffset.input1D[0]</b></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>CTRL_End_IK.twistOffset </b>to <b>pma_twistOffset.input1D[1]</b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC2jzqSD_sAES-E8Eb3qKOPNm6KdMZhk53vSV247-duxQApGL3F4mMBFUYsMKcRR2hwrvCrlBjyqtQrOQbfEsKg4O_ouSAC8S1nSnl8bqhITwg0BZdF2cFVUFHalur_5Y79xNlz9rrj2ZP/s1600/twistoffset+conn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="216" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhC2jzqSD_sAES-E8Eb3qKOPNm6KdMZhk53vSV247-duxQApGL3F4mMBFUYsMKcRR2hwrvCrlBjyqtQrOQbfEsKg4O_ouSAC8S1nSnl8bqhITwg0BZdF2cFVUFHalur_5Y79xNlz9rrj2ZP/s400/twistoffset+conn.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Finally, we are ready to connect it to our <b>ikHandle</b>.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>pma_twistOffset.output1D </b>to <b>ikHandle1.twist</b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8xF8IL270suW04MFv2jaoHhR15fXiN6ybBY78gbvDpXj36flA-zt8QGUifvgYsqV0feup4d8IAXzVkJpgJlt6ZFxvd990WxNCefV3fA5-Qf7LpbuFvlmnSPN2YNVwK3IabwfcmUYM5mck/s1600/ikHandle+conn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="161" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi8xF8IL270suW04MFv2jaoHhR15fXiN6ybBY78gbvDpXj36flA-zt8QGUifvgYsqV0feup4d8IAXzVkJpgJlt6ZFxvd990WxNCefV3fA5-Qf7LpbuFvlmnSPN2YNVwK3IabwfcmUYM5mck/s400/ikHandle+conn.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
There we go! So with not a lot of extra work, just 2 extra nodes we get a clean setup for twisting. And that is actually not the best part. I mentioned earlier that this setup would also help solve an issue of the advance twist control. Or with most automated solutions. And that is the flip that occurs at 180 degrees. This problems occurs because of how rotations are being calculated (Euler rotations). This ends up leading us into problems such as Gimbal locking and making the setup fragile if we are relying on any one rotation axis to get our twisting. So as it turns out its not so advance after all! :)</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
As this is another whole discussion in itself, I'll just leave it at that. For now, be assured that this setup will handle with no problems rotations beyond 180 degrees or -180 degrees for that matter with no problems because of how the spline IK is solving for twisting.</div>
<div class="separator" style="clear: both; text-align: left;">
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: left;">
The setup in its current state is an example in which the twist propagates <i>up</i> the joint chain. However, what if we need the <i style="font-weight: bold;">CTRL_Start_IK </i>to also be able to control the twisting. To have the twisting propagate <i>down</i> the joint chain. Let's take a look at that.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
If we go ahead and use the <i>roll</i> attribute on the <b>ikHandle </b>notice that the entire joint chain rotates. The obvious thing to do is to counter rotate using the <i>twist </i>and we then get the effect the rotation came from the start of the joint chain. </div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
So let's start with that first.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>CTRL_Start_IK.rotateX </b>to <b>ikHandle1.roll</b></div>
<div class="separator" style="clear: both; text-align: left;">
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: left;">
Now, for the start control to work with the end control at the same time and for our <i>autoTwist</i> attribute to work on <i style="font-weight: bold;">CTRL_Start_IK</i> as well, we will create another <b>multiplyDivide </b>node, we can call this <b>md_autoTwist2</b>.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>CTRL_End_IK.autoTwist </b>to <b>md_autoTwist2.input1X</b></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>CTRL_Start_IK.rotateX </b>to <b>md_autoTwist2.input2X</b></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
To counter rotate we will use a <b>plusMinusAverage</b> node. <i>W</i>e will call this <b>pma_twistOffset2</b>. However, we also need to account for whatever twist offset already exists from the<i> <b>CTRL_End_IK</b>.</i></div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>pma_twistOffset.output1D </b>to <b>pma_twistOffset2.input1D[0]</b></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>md_autoTwist2.outputX </b>to <b>pma_twistOffset2.input1D[1]</b></div>
<div class="separator" style="clear: both; text-align: left;">
Connect <b>pma_twistOffset2.output1D </b>to <b>ikHandle1.twist</b></div>
<div class="separator" style="clear: both; text-align: left;">
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: left;">
And we can set the operation to <i>Subtract</i>.</div>
<div class="separator" style="clear: both; text-align: left;">
<b><br /></b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhubzHgZ42bb6IFa6thYW1szmeXet2OfW0e8m8vyxRdVV9A1NTNXi4MDo3B3zAKVzEJnoTUQoCNJ-9moaQ4fc26QheHwKXVAmn-qWdhH8BXd1zs2oce2qeyNVR-48r5il5yA0GA5EGrMyiA/s1600/final+connection.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="211" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhubzHgZ42bb6IFa6thYW1szmeXet2OfW0e8m8vyxRdVV9A1NTNXi4MDo3B3zAKVzEJnoTUQoCNJ-9moaQ4fc26QheHwKXVAmn-qWdhH8BXd1zs2oce2qeyNVR-48r5il5yA0GA5EGrMyiA/s400/final+connection.png" width="400" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
And there it is! Now go and have fun making your joints twist!</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
I realize although its not too complicated you probably don't want to have to setup it every single time. Hopefully you still were able to understand it so that you could :)</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
But to speed up the process here's a script to set it up for you. It takes in the start control, the end control, the control object, the ikHandle, and the twist axis for the control rotation (this can be negative as well).</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
</div>
<pre class="brush: python">import maya.cmds as cmds
def twist(startCtrl, endCtrl, ctrlObj, ikHandle, axis):
'''
Create twist control for the spline IK setup
startCtrl : node controlling the start of the joint chain
endCtrl : node controlling the end of the joint chain
ctrlObj : control object to host the twist attributes
ikHandle : the spline IK handle
axis : the rotation axis for twisting on the controls : "x", "y", "z", "-x", "-y", "-z"
'''
# check nodes
for node in [startCtrl, endCtrl, ctrlObj, ikHandle]:
if not cmds.objExists(node):
cmds.error("%s does not exist." % node)
# check axis
if axis not in ["x", "y", "z", "-x", "-y", "-z"]:
cmds.error("Invalid axis.")
# check if our twist axis is on the negative side
neg = False
splitString = axis.split("-")
if splitString[0] == "":
axis = splitString[1]
neg = True
axis = ".r" + axis
# create new attributes
cmds.addAttr(ctrlObj, ln="autoTwist", at="double", dv=1, min=0, max=1, k=True)
cmds.addAttr(ctrlObj, ln="twistOffset", at="double", dv=0, k=True)
# create nodes for autoTwist and twistOffset
autoTwist1 = cmds.createNode("multiplyDivide", n="md_" + ctrlObj + "_autoTwist1")
autoTwist2 = cmds.createNode("multiplyDivide", n="md_" + ctrlObj + "_autoTwist2")
twistOffset1 = cmds.createNode("plusMinusAverage", n="pma_" + ctrlObj + "_twistOffset1")
twistOffset2 = cmds.createNode("plusMinusAverage", n="pma_" + ctrlObj + "_twistOffset2")
# connect the end control
cmds.connectAttr(endCtrl + axis, autoTwist1 + ".input1X")
cmds.connectAttr(ctrlObj + ".autoTwist", autoTwist1 + ".input2X")
cmds.connectAttr(autoTwist1 + ".outputX", twistOffset1 + ".input1D[0]")
cmds.connectAttr(ctrlObj + ".twistOffset", twistOffset1 + ".input1D[1]")
# connect the start control
cmds.connectAttr(startCtrl + axis, autoTwist2 + ".input1X")
cmds.connectAttr(ctrlObj + ".autoTwist", autoTwist2 + ".input2X")
cmds.connectAttr(twistOffset1 + ".output1D", twistOffset2 + ".input1D[0]")
cmds.connectAttr(autoTwist2 + ".outputX", twistOffset2 + ".input1D[1]")
cmds.setAttr(twistOffset2 + ".operation", 2) # subtract
# connect to the ikHandle
cmds.connectAttr(startCtrl + axis, ikHandle + ".roll")
cmds.connectAttr(twistOffset2 + ".output1D", ikHandle + ".twist")
# make changes if the twist axis is on the negative side
if neg:
invTwist1 = cmds.createNode("multiplyDivide", n="md_" + ctrlObj + "_invTwist1")
invTwist2 = cmds.createNode("multiplyDivide", n="md_" + ctrlObj + "_invTwist2")
cmds.setAttr(invTwist1 + ".input2X", -1)
cmds.setAttr(invTwist2 + ".input2X", -1)
cmds.connectAttr(endCtrl + axis, invTwist1 + ".input1X")
cmds.connectAttr(startCtrl + axis, invTwist2 + ".input1X")
cmds.connectAttr(invTwist1 + ".outputX", autoTwist1 + ".input1X", f=True)
cmds.connectAttr(invTwist2 + ".outputX", autoTwist2 + ".input1X", f=True)
cmds.connectAttr(invTwist2 + ".outputX", ikHandle + ".roll", f=True)
</pre>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Hope you guys liked it! Feel free to share!</div>
<div class="separator" style="clear: both; text-align: left;">
And to leave any comments or questions!</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Enjoy!</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-6465220644391090032013-08-02T21:52:00.004-07:002013-08-02T21:55:41.289-07:00Rigging the Eye BrowsCheck out the video tutorial on rigging eye brow controls for your characters. For my friend Rafael Salvador who asked me to put this together.<br />
Im also going to start embedding the videos here in the website and see how that goes.<br />
<br />
<div style="text-align: center;">
<iframe allowfullscreen="" frameborder="0" height="313" mozallowfullscreen="" src="http://player.vimeo.com/video/71620108" webkitallowfullscreen="" width="500"></iframe> </div>
<div style="text-align: center;">
<a href="http://vimeo.com/71620108">Rigging the Eyebrows - Tutorial</a> from <a href="http://vimeo.com/user10938678">Luiz Philippe Moreira</a> on <a href="https://vimeo.com/">Vimeo</a>.</div>
<div style="text-align: center;">
<br />
<div style="text-align: left;">
This is the concept shown in the video.</div>
<div style="text-align: left;">
<br /></div>
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwuqc5c92_ReWxpp_JuPDt4RzElRovdVCNXcvfubM2nbNgwNd00V5oDMqVnqYvSJ1C8rvSA0O6_Hs8D-rp09lWlTQ166K0LWKg0rZuulCmbtfFE3_T7-MoUg7rpoHc86EbbHlutYedXrvh/s1600/eyebrowdesing.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="281" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgwuqc5c92_ReWxpp_JuPDt4RzElRovdVCNXcvfubM2nbNgwNd00V5oDMqVnqYvSJ1C8rvSA0O6_Hs8D-rp09lWlTQ166K0LWKg0rZuulCmbtfFE3_T7-MoUg7rpoHc86EbbHlutYedXrvh/s400/eyebrowdesing.jpg" width="400" /></a></div>
<div style="text-align: left;">
<br />
Enjoy!</div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-43151128016124552672013-07-30T19:18:00.001-07:002013-08-12T14:49:40.342-07:00Practical Facial RiggingIn this post I'll be going over a face setup Im working on for a personal project. The idea behind this setup is to allow for quick and efficient facial rigging, by allowing a large level of control, from a large library of preset face shapes and individual face controls. This setup will also allow for easy transferability of the the rig across characters and with that, animation data across multiple face rigs.<br />
<br />
Some of the concepts I'll be touching on were introduced by Jeremy Ernst for the <i>Gears of War 3</i> game.<br />
<br />
I mentioned a large library of preset face shapes. Yes, this means blendshapes, or morph targets, or whatever the software you use calls them. And lots of them. The idea is to make the facial animation easier and cleaner. By having many face poses broken down into separate groups, we can narrow down the animation of these poses to just one attribute control, instead of having to animate translations or rotations (and sometimes even scale depending on how little joints we are using on the face) to get the desired pose. Again, this keeps things a lot cleaner and simpler to animate (single animation curves in the graph editor).<br />
<br />
This takes us next to how the poses are made. The pose library for the face will be built following the <i style="font-weight: bold;">Facial Action Coding System</i>. Individual poses will be sculpted to represent each action unit, and then used as blendshapes. Below are some links with more information if you want to learn more about it.<br />
<br />
<a href="http://en.wikipedia.org/wiki/Facial_Action_Coding_System">http://en.wikipedia.org/wiki/Facial_Action_Coding_System</a><br />
<a href="http://www.cs.cmu.edu/~face/facs.htm">http://www.cs.cmu.edu/~face/facs.htm</a><br />
<a href="http://face-and-emotion.com/dataface/facs/description.jsp">http://face-and-emotion.com/dataface/facs/description.jsp</a><br />
<br />
To summarize, F.A.C.S outlines a series of action units that describe the face's range of motion through the contraction or relaxation of muscles in the face.<br />
<br />
Although the current setup offers the animator a lot of intuitive control to drive the character's facial expressions, its still desirable to have individual controls to offset the preset poses. To allow for that, on top of the current setup there will also be a control rig, with individual controls for joints that are weighted on the face skin cluster. The interesting part however is to figure out how to have the controls follow the surface of the face while we are changing between different face poses. And this is where the concept of rivets comes in.<br />
<br />
In essence, rivets allow you to pin something to something else, much like a point constraint, but at a component level. Meaning, the object will move with the point it is pinned to. Therefore, you can deform the object and the rivet will move to follow the deformation. Places for using rivets include surfaces (NURBS and Poly) and curves. Below I included a script for creating a locator pinned to a point on a surface or a point on a curve.<br />
<pre class="brush: python"># import python modules
try:
import maya.cmds as cmds
import maya.OpenMaya as om
except Exception, e:
print "Error while trying to import python modules."
print "Exception: ", e
def rivetAtPointOnSurface():
''' Pin the object to selected points on the NURBS surface '''
# get the selected components
sel = cmds.ls(sl=True, fl=True)
if not sel:
cmds.error("Nothing selected")
# get the surface name
surface = sel[0].split(".")[0]
# get the shape
shape = cmds.listRelatives(surface, shapes=True)[0]
# create rivet for each selected component
for each in sel:
# get the position to pin the object to on the surface
pos = cmds.xform(each, q=True, ws=True, t=True)
# define the point
mPoint = om.MPoint(pos[0], pos[1], pos[2])
# get the surface's dagPath
selectionList = om.MSelectionList()
selectionList.add(shape)
mNode = om.MDagPath()
selectionList.getDagPath(0,mNode)
if cmds.objectType(shape) == "nurbsSurface":
surfaceFn = om.MFnNurbsSurface(mNode)
util = om.MScriptUtil()
uParamPtr = util.asDoublePtr()
vParamPtr = util.asDoublePtr()
if not surfaceFn.isPointOnSurface(mPoint):
surfaceFn.getParamAtPoint(mPoint, uParamPtr, vParamPtr, False, om.MSpace.kObject, 0.001)
else:
mPoint = surfaceFn.closestPoint(mPoint, False, uParamPtr, vParamPtr, False, 0.001, om.MSpace.kObject)
surfaceFn.getParamAtPoint(mPoint, uParamPtr, vParamPtr, False, om.MSpace.kObject, 0.001)
# get the U and V paramters
u = util.getDouble(uParamPtr)
v = util.getDouble(vParamPtr)
# create rivet locator
rivet = cmds.spaceLocator(n="rivet")[0]
# create point on surface node
psi = cmds.createNode("pointOnSurfaceInfo", n="psi_" + surface)
cmds.setAttr(psi + ".parameterU", u)
cmds.setAttr(psi + ".parameterU", v)
cmds.connectAttr(shape + ".ws", psi + ".is", f=True)
cmds.connectAttr(psi + ".position", rivet + ".t", f=True)
# constraint the rivet locator
cmds.orientConstraint(surface, rivet, mo=True)
else:
cmds.error("Not a NURBS surface component")
</pre>
<br />
<pre class="brush: python">def rivetAtPointOnCurve(curve):
''' Pin the object to the selected CVs '''
# get the selected components
sel = cmds.ls(sl=True, fl=True)
if not sel:
cmds.error("Nothing selected")
# get the curve name
curve = sel[0].split(".")[0]
# get the shape
shape = cmds.listRelatives(curve, shapes=True)[0]
# create rivet for each selected component
for each in sel:
# get the position to pin the object to on the surface
pos = cmds.xform(each, q=True, ws=True, t=True)
# define the point
mPoint = om.MPoint(pos[0], pos[1], pos[2])
# get the surface's dagPath
selectionList = om.MSelectionList()
selectionList.add(shape)
mNode = om.MDagPath()
selectionList.getDagPath(0,mNode)
if cmds.objectType(shape) == "nurbsCurve":
curveFn = om.MFnNurbsCurve(mNode)
util = om.MScriptUtil()
uParamPtr = util.asDoublePtr()
if curveFn.isPointOnCurve(mPoint):
curveFn.getParamAtPoint(mPoint, uParamPtr, 0.001, om.MSpace.kObject)
else:
mPoint = curveFn.closestPoint(mPoint, uParamPtr, 0.001, om.MSpace.kObject)
curveFn.getParamAtPoint(mPoint, uParamPtr, 0.001, om.MSpace.kObject)
# get the U paramter
u = util.getDouble(uParamPtr)
# create rivet locator
rivet = cmds.spaceLocator(n="rivet")[0]
# create pointOnCurveInfo node
pci = cmds.createNode("pointOnCurveInfo", n="pci_" + curve)
cmds.setAttr(pci + ".parameter", u)
cmds.connectAttr(shape + ".ws", pci + ".ic", f=True)
cmds.connectAttr(pci + ".position", rivet + ".t", f=True)
# constraint the rivet locator
cmds.orientConstraint(curve, rivet, mo=True)
else:
cmds.error("Not a NURBS Curve component")</pre>
Unfortunately with Maya's default nodes we can't attach an object to a point on a polygon. An idea by Michael Bazhutkin was to use the node <i style="font-weight: bold;">curveFromMeshEdge </i>to extract two curves from edges on a surface and use them to create a <i>loft</i> surface (a NURBS patch) and pin the rivet to the middle point on this surface.<br />
<pre class="brush: python">def rivetAtPointOnMesh():
''' Pin the object to selected face on the NURBS surface '''
# get the selected component
sel = cmds.ls(sl=True, fl=True)
if not sel:
cmds.error("Nothing selected")
# get the surface name
surface = sel[0].split(".")[0]
# get the shape
shape = cmds.listRelatives(surface, shapes=True)[0]
# create rivet for each selected component
for each in sel:
# convert selection to edges
cmds.polyListComponentConversion(each, ff=True, te=True)
# create curves from edges
edges = cmds.ls(sl=True, fl=True)
if cmds.objectType(shape) == "mesh":
c1 = cmds.createNode("curveFromMeshEdge")
cmds.setAttr(me1 + ".ihi", 1)
cmds.setAttr(me1 + ".ei[0]", edges[0])
c2 = cmds.createNode("curveFromMeshEdge")
cmds.setAttr(me2 + ".ihi", 1)
cmds.setAttr(me2 + ".ei[0]", edges[1])
# create a lofted surface from curves
loft = cmds.createNode("loft")
cmds.setAttr(loft + ".ic", s=2)
cmds.setAttr(loft + ".u", True)
cmds.setAttr(loft + ".rsn", True)
# create a pointOnSurfaceInfo node for center point on lofted surface
psi = cmds.createNode("pointOnSurfaceInfo")
cmds.setAttr(psi + ".turnOnPercentage", 1)
cmds.setAttr(psi + ".parameterU", .5)
cmds.setAttr(psi + ".parameterV", .5)
cmds.connectAttr(loft + ".os", psi + ".is", f=True)
cmds.connectAttr(c1 + ".oc", loft + ".ic[0]")
cmds.connectAttr(c2 + ".oc", loft + ".ic[[1]")
cmds.connectAttr(shape + ".w", c1 + ".im")
cmds.connectAttr(shape + ".w", c2 + ".im")
# create rivet locator
rivet = cmds.spaceLocator(n="rivet")[0]
cmds.connectAttr(psi + ".position", rivet + ".t", f=True)
# constraint the rivet locator
cmds.orientConstraint(surface, rivet, mo=True)
else:
cmds.error("Not a polygon mesh")</pre>
This works great, but doesn't let us create a rivet on a specific point on a polygonal mesh. For that we would need a plugin that works like the <i style="font-weight: bold;">pointOnSurfaceInfo</i>. But that's a subject for later.<br />
<br />
Now that we have a way to <i>track</i> different positions on the face, we can use that information to move our controls. And yes, I did say <i>track </i>(with italics). Because in essence that is what we are doing, much like motion capture, we are tracking different positions on that face to be able to drive or controls, which are driving the face joints.<br />
<br />
With that in mind, we just need to understand how all these things will be hooked up together. Let break this down.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgto3iR7vchoTPxP5QolhXCcnhX1pLynbeHUAlFJoF8aeta2EH6cU_9CBl_bJn2dg69js0-v9m_yUFzwWc4LPrIvvgKf3C2-dYRtSioiXikjo58t-x2n1vPC2x_Rj7vhnKP5ezqLxM4yEJn/s1600/face+controls.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="280" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgto3iR7vchoTPxP5QolhXCcnhX1pLynbeHUAlFJoF8aeta2EH6cU_9CBl_bJn2dg69js0-v9m_yUFzwWc4LPrIvvgKf3C2-dYRtSioiXikjo58t-x2n1vPC2x_Rj7vhnKP5ezqLxM4yEJn/s400/face+controls.jpg" width="400" /></a></div>
<br />
To keep our face rig as clean as possible, we will have one blendshape target (Master Blendshape) piped into the final face mesh. All of the different blendshapes (from the F.A.C.S pose library) will be driving the Master Blendshape, and hence the final mesh. The rivet locators will be pinned to the Master Blendshape face. Because the rivets are only pinned to a position, we also need to constraint its rotation to that of the head so it follows it correctly. We can then drive the control's parent space with the rivet. We can do that with a parent constraint on a group above the control. And finally the joints are parent constrainted to the controls.<br />
<br />
Cool. Now that we have covered the setup lets look at what we can further achieve with this setup.<br />
<br />
<b>Transferring Rigs</b><br />
<b><br /></b>
The idea is that you can use the same topology for various face meshes, and by applying the initial rig as a blendshape, we now have the rig working with a new face model.<br />
<br />
<b>Transferring Animation</b><br />
<b><br /></b>
Transferring animation data also becomes really easy. With the the pose library the animator will be animating attributes, and with the controls, offset transforms, which are relative and not based on world or parent space. Therefore we can transfer animation really easily between multiple face rigs with the same setup.<br />
<br />
So that was an overview of the design. Hopefully that can inspire you to do something cool!Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com2tag:blogger.com,1999:blog-4665036873052018975.post-60699780327745328912013-07-25T10:50:00.004-07:002013-10-12T17:10:48.278-07:00Connect Attribute: Maintain OffsetConnecting attributes is one way to drive a node's attribute using another in Maya. When connecting numeric values, such as translate or rotate, sometimes an offset exists prior to the connection which you might not want to lose. It would be nice if we could maintain that offset when doing the connection between attributes, like we can with our standard constraints.<br />
<br />
A simple way to do that is using a math node <b>plusMinusAverage </b>as a buffer. We can first calculate the difference between the two attributes we are connecting. For example, if we have <b>A = < 0, 0, 0 > </b>and <b>B = < 3, 1, 3 > </b> if we connect <b>A </b>to <b>B</b> directly, now <b>B = < 0, 0, 0 ></b>. What we can do to get around this is to first find the difference between A and B as <b>A-B</b> and then use our <b>plusMinusAverage</b> node to subtract this difference from A. We can first store the current value for the source attribute, <b>A</b>, in the first <i>input</i>, and set the other <i>input </i>to the difference we have just computed, in this case <b>< -3, -1, -2 ></b>. By default the node's <b>operation </b>is set to <i>Sum</i>, we want <i>Subtract</i>. We then connect the <i>output </i>to the destination attribute, and now the attributes are connecting but no 'snap' happened.<br />
<br />
In this example I want to connect <b>pCube2.translateY</b> to <b>pCube1.translateZ</b>, but notice the <b>pCube2.translateY </b>is 0 while <b>pCube1.translateZ </b>is 3.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn870MwoRWIrbkdE8n66tMtgdlIBRxfa2TkVqLhpoOvKOMLtJaHJOwFzu-zksujoUqiV6VIKe-HDEuuDfcIT2HpiycdVAgI3wF0SSOGByYpZFjWlUFqF7pHIWltnNwd4IaY5sL4l5qAVDN/s1600/showing+pos.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjn870MwoRWIrbkdE8n66tMtgdlIBRxfa2TkVqLhpoOvKOMLtJaHJOwFzu-zksujoUqiV6VIKe-HDEuuDfcIT2HpiycdVAgI3wF0SSOGByYpZFjWlUFqF7pHIWltnNwd4IaY5sL4l5qAVDN/s320/showing+pos.png" width="320" /></a></div>
<br />
If I do a simple connection, <b>pCube1 </b>will 'snap' to the origin.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDwXuecf9Z3rELJJS59Z2g2uhD7bdnOvtlyMTpipH4u7ndFyiHyX8kIzhgoo8jR_j74d-LgK8rtkvj4yVhuIIB3p0t9NdZIJs9xDOw0f7jQOs48SCWXiq7z9_MbrZtKGp4y_Z3tkKnfSR9/s1600/snap.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="198" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDwXuecf9Z3rELJJS59Z2g2uhD7bdnOvtlyMTpipH4u7ndFyiHyX8kIzhgoo8jR_j74d-LgK8rtkvj4yVhuIIB3p0t9NdZIJs9xDOw0f7jQOs48SCWXiq7z9_MbrZtKGp4y_Z3tkKnfSR9/s320/snap.png" width="320" /></a></div>
<br />
We might not want that. So we do this setup to keep that offset.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQeCqXZZo4h6_mOot-fdTxKtSesjsS2E5hBZuirRu78Vew67zwYE0Ma68QBli_0XX42mRA4y55wkAZG03WcWZOIQCfq_tH5CNA-1u_2mUsM816rVxjzk59GTL9NJH5-stKvAJuADzw_pKb/s1600/Screen+shot+2013-10-12+at+7.36.08+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="170" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhQeCqXZZo4h6_mOot-fdTxKtSesjsS2E5hBZuirRu78Vew67zwYE0Ma68QBli_0XX42mRA4y55wkAZG03WcWZOIQCfq_tH5CNA-1u_2mUsM816rVxjzk59GTL9NJH5-stKvAJuADzw_pKb/s320/Screen+shot+2013-10-12+at+7.36.08+PM.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLbsXW_HjC14ZdGJ65g03EOICLh3Z9Vtn8FAWcLntj2mlfCErKpG4OnIk1-8aD5U-MGInR4_umPwYzCY6IjmMEUfrq_UaC9Cf8LK7Y9SrQoyU5XymeKo3TJlXhIOvv8YIbjdADd41HbLc0/s1600/Screen+shot+2013-10-12+at+7.36.18+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="232" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhLbsXW_HjC14ZdGJ65g03EOICLh3Z9Vtn8FAWcLntj2mlfCErKpG4OnIk1-8aD5U-MGInR4_umPwYzCY6IjmMEUfrq_UaC9Cf8LK7Y9SrQoyU5XymeKo3TJlXhIOvv8YIbjdADd41HbLc0/s320/Screen+shot+2013-10-12+at+7.36.18+PM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNChu0HXL24aHFHk5dZ_YyIdVRIvJQUnidn2S6k3QTxHWOIrjXHY9dnOOy3N1nQeWRtZneWKMLhq8CQDvxSfpxC2xVjUrEi2owfQ8HGo6Y4FktIPROrE10fZMEWb437sU6WDuWydiDBFkd/s1600/Screen+shot+2013-10-12+at+7.35.27+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="190" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhNChu0HXL24aHFHk5dZ_YyIdVRIvJQUnidn2S6k3QTxHWOIrjXHY9dnOOy3N1nQeWRtZneWKMLhq8CQDvxSfpxC2xVjUrEi2owfQ8HGo6Y4FktIPROrE10fZMEWb437sU6WDuWydiDBFkd/s320/Screen+shot+2013-10-12+at+7.35.27+PM.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
Here is a wrapper for Maya's <i style="font-weight: bold;">connectAttr</i> command allowing to maintain the current offset by using a <b>plusMinusAverage</b> node in between.<br />
<br />
<span style="color: red;">UPDATED</span><br />
<pre class="brush: python">def connectAttr(srcPlug, destPlugs, maintainOffset=False, **kwargs):
''' Wrapper for Maya's connectAttr command with the extra functionality of allowing to maintain offset
if attribute is a numeric attribute and to connect to multiple objects '''
if type(destPlugs) != list:
destPlugs = [destPlugs]
srcValue = cmds.getAttr(srcPlug)
for destPlug in destPlugs:
if maintainOffset:
# get the attribute type
attrType = cmds.getAttr(srcPlug, type=True)
destValue = cmds.getAttr(destPlug)
if attrType in ["doubleLinear", "doubleAngle", "double", "long", "float"]:
# find the difference 'delta' between the two attributes
diff = srcValue - destValue
# create a node to keep the offset
offsetNode = cmds.createNode("plusMinusAverage")
cmds.setAttr(offsetNode + ".operation", 2) # subtract
# connect and disconnect plug to get the value in
cmds.connectAttr(srcPlug, offsetNode + ".input1D[0]")
cmds.connectAttr(srcPlug, offsetNode + ".input1D[1]")
cmds.disconnectAttr(srcPlug, offsetNode + ".input1D[1]")
cmds.setAttr(offsetNode + ".input1D[1]", diff)
cmds.connectAttr(offsetNode + ".output1D", destPlug, **kwargs)
elif attrType in ["double3", "float3"]:
# find the difference 'delta' between the two attributes
diff = []
for i, d in enumerate(srcValue[0]):
diff.append(d - destValue[0][i])
# create a node to keep the offset
offsetNode = cmds.createNode("plusMinusAverage")
cmds.setAttr(offsetNode + ".operation", 2) # subtract
# connect and disconnect plug to get the value in
cmds.connectAttr(srcPlug, offsetNode + ".input3D[0]")
cmds.connectAttr(srcPlug, offsetNode + ".input3D[1]")
cmds.disconnectAttr(srcPlug, offsetNode + ".input3D[1]")
cmds.setAttr(offsetNode + ".input3D[1]", diff[0], diff[1], diff[2])
cmds.connectAttr(offsetNode + ".output3D", destPlug, **kwargs)
return offsetNode
else:
cmds.connectAttr(srcPlug, destPlug, **kwargs)
</pre>
Hope you guys find this useful.Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-79210000411035046212013-07-24T13:54:00.001-07:002013-07-24T13:54:57.490-07:00Lattice Fun: CorrectivesI wanted to do this quick post to remind some of you the power of lattices when it comes to quickly shaping geometry. The workflow I'll be going over in the example below covers an quick and easy technique for creating some pose space deformations. Do keep in mind however, that while this technique is great for deformations on single axis rotations, in areas that allow for multi axes rotations we would need a more advance approach.<br />
<br />
You can also watch <a href="https://vimeo.com/70970497" target="_blank">here</a> the video example for setting this up.<br />
So let's get started.<br />
<br />
I'll be using a simple polygonal cylinder to demonstrate this. So go ahead and create a cylinder.<br />
Give it enough resolution so we can deform this properly.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMo5IcZnYEQeNeB_C6ek8WNz15BkiSbxogx787esaIPesoaAXa44uS3YY7C3rWbrIza7sJwLGPxdU0fAx5GE79a6aMs9500Vt12Bbecdm8P-x2yLQJ71VTtUNX0_PeUXzaHFvmQQIigO58/s1600/geometry.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgMo5IcZnYEQeNeB_C6ek8WNz15BkiSbxogx787esaIPesoaAXa44uS3YY7C3rWbrIza7sJwLGPxdU0fAx5GE79a6aMs9500Vt12Bbecdm8P-x2yLQJ71VTtUNX0_PeUXzaHFvmQQIigO58/s320/geometry.png" width="318" /></a></div>
<br />
Next lets create some joints for skinning the geometry to.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFRdxC5RQDOuCBpcmabscNwVFVFyLG8JPECNEEsL52k1K8XtX4TO8E8kQySPNHEvu3nM0sLGYtJPQUmzOldYn5dZWpxqRUw9wG7JM7ZpiH6sTVq_sDQtJMxJg1vA5gv_gXnmvr4PrwUSXA/s1600/joints.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFRdxC5RQDOuCBpcmabscNwVFVFyLG8JPECNEEsL52k1K8XtX4TO8E8kQySPNHEvu3nM0sLGYtJPQUmzOldYn5dZWpxqRUw9wG7JM7ZpiH6sTVq_sDQtJMxJg1vA5gv_gXnmvr4PrwUSXA/s320/joints.png" width="268" /></a></div>
<br />
Now, lets go ahead and create a lattice to deform the cylinder. <i>Select </i>the cylinder,<i> </i>and goto<br />
<i><b>Create Deformers -> Lattice </b></i><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4z9cvNK_4aUWuAzHvnY9uyYFLLiUTZaAGHCGGZIhg66nqUBHKteM-65mSuwH_bhk6bWYMxMMOx0OMjMDoxxWbeqCiWOTCdE69XHHINzZuQdIgt2HxyHVGDdf5XBAjGWedw0Lchvn0naY_/s1600/lattice.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh4z9cvNK_4aUWuAzHvnY9uyYFLLiUTZaAGHCGGZIhg66nqUBHKteM-65mSuwH_bhk6bWYMxMMOx0OMjMDoxxWbeqCiWOTCdE69XHHINzZuQdIgt2HxyHVGDdf5XBAjGWedw0Lchvn0naY_/s320/lattice.png" width="242" /></a></div>
<br />
Give the lattice enough resolution to deform the geometry properly. Think about the shape you want to achieve and how many control point spans you might need. I changed the divisions along the height to <b>9</b>, that way I'll have 3 control spans in the midsection of each <i>bone</i>, and that will give me enough control. Play around with these values and see what suits your needs for what you have in mind.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3mbHQtZeCX0b1tO_pqI4WOyJJm1pWjmeB5AXtrQdOagGz6A6xwxEK0bT6ImBqEdG7zq6jjM7cjaUT1PF-MNagyH8-w6UkzFYPMi7JIkw1HiEKtanirtnma-DjLiilzZgnLVsRi7bi_zMh/s1600/lattice+division.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj3mbHQtZeCX0b1tO_pqI4WOyJJm1pWjmeB5AXtrQdOagGz6A6xwxEK0bT6ImBqEdG7zq6jjM7cjaUT1PF-MNagyH8-w6UkzFYPMi7JIkw1HiEKtanirtnma-DjLiilzZgnLVsRi7bi_zMh/s320/lattice+division.png" width="201" /></a></div>
<br />
This is where this gets fun. We now want to bind the lattice to the joints, so <i>Select </i>the first <b>joint</b>, <i>Shift Select</i> the <b>lattice</b> and goto<br />
<i><b>Skin -> Bind Skin -> Rigid Bind</b> (at default settings -> it will use the joint's hierarchy)</i><br />
<i><br /></i>
<b>Note:</b> We could have used the <b>Smooth Bind </b>instead. However, we are looking for easy control of shaping, and when we begin tweaking the lattice to shape our corrective, you'll notice that the <i>smoothing </i>from the <b>Smooth Bind </b>can get in the way. We <b>Rigid Bind</b> the tweaking are more precise.<br />
<i><br /></i>
Great. Now test out the setup by rotating the mid joint.<br />
<br />
Right now, the deformation is a little rough however, so to get a better starting point.<br />
Change the <i>Local Influence</i> in the <b>ffd1</b> node (located under the lattice's <b>OUTPUTS</b>).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWA2qZb66bPxuz8oZ3LTL1RBou9VstL0_y_buhIKO3oGsmaKjvMMhHusMQEkwwQObXQIyX9pyria0XxlP9ZQT3row8rcrS7oGzbtsS9Z_41t7a3NEjTUxJN8Kqx80_CXvk6JtijzcBqBCk/s1600/local+influences.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWA2qZb66bPxuz8oZ3LTL1RBou9VstL0_y_buhIKO3oGsmaKjvMMhHusMQEkwwQObXQIyX9pyria0XxlP9ZQT3row8rcrS7oGzbtsS9Z_41t7a3NEjTUxJN8Kqx80_CXvk6JtijzcBqBCk/s1600/local+influences.png" /></a></div>
<br />
The skinning should be smoother now (see, and we are not even using <b>Smooth Bind</b>!) :)<br />
<br />
The last step is just modifying our lattice to give us the desired shape on the geometry that we want.<br />
Before you do this, just figure out how you plan on using this new shape. You can use it for a <b>BlendShape </b>target for example by duplicating the new geometry and getting rid of the lattice. Or you could just leave the lattice and use <b>Set Driven Keys</b> using the joint's rotation to drive the lattice's control point's position.<br />
<br />
If your going with the second option, make sure you you set a <i>key</i> on the <b>Set Driven Key </b>window before you start tweaking the lattice points. <i>Select </i>all latices points and click <b>Load Driven</b>, select the mid joint and click <b>Load Driver</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqPOLbF6OBQVg44iEI6e1FLWLcP-PGoIDfrK3kR9WwCYxafsvpvtVTUelWfIvxhWihCm-Fng0GRXSIizrjBumO4VO3Usdg-v5rm9Z8ei-KMIOAiwPr9wkcAkpg3vCivD8e5ZCHP6429ROI/s1600/sdk.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhqPOLbF6OBQVg44iEI6e1FLWLcP-PGoIDfrK3kR9WwCYxafsvpvtVTUelWfIvxhWihCm-Fng0GRXSIizrjBumO4VO3Usdg-v5rm9Z8ei-KMIOAiwPr9wkcAkpg3vCivD8e5ZCHP6429ROI/s320/sdk.png" width="230" /></a></div>
<br />
And thats it!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz8rWIgHfWoBtFp9to25uz0AN2_q6ASnYsYcMH6R6XwyihEsk41t755IQjJF7i7BQsUAl8-zYs-NptYvpMzRfKqSyiwUYw5anJN7AlrSNqIZxNOpZMQ_CUdioKuAZK0zxYam98LMG42SfW/s1600/final+2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="192" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz8rWIgHfWoBtFp9to25uz0AN2_q6ASnYsYcMH6R6XwyihEsk41t755IQjJF7i7BQsUAl8-zYs-NptYvpMzRfKqSyiwUYw5anJN7AlrSNqIZxNOpZMQ_CUdioKuAZK0zxYam98LMG42SfW/s200/final+2.png" width="200" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUOehgDKMWH3PU14WJgbq6z6eKJEub-eh9bsM6d6jGKVg9EBapPVbz6C6AaKJoB-moZkOcvHflht6g4xAJRf93WKrIPQPqplSr3PeU0nUFHSVLpZccPCdq9prtZiPJxczR74dO4MEunAgT/s1600/final1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhUOehgDKMWH3PU14WJgbq6z6eKJEub-eh9bsM6d6jGKVg9EBapPVbz6C6AaKJoB-moZkOcvHflht6g4xAJRf93WKrIPQPqplSr3PeU0nUFHSVLpZccPCdq9prtZiPJxczR74dO4MEunAgT/s200/final1.png" width="187" /></a></div>
<br />
Now go have fun using lattices!Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-62213892941651917392013-07-23T10:16:00.000-07:002013-07-23T16:49:59.131-07:00Enhancing your Stretchy IKIn response to some questions about the stretchy IK setup I describe in <a href="http://luizmoreira2012br.blogspot.com/2013/06/creating-stretchy-ik-joint-chain.html" target="_blank"><span id="goog_1110400039"></span>this<span id="goog_1110400040"></span></a> post, I will be going over extra functionality that you could add to that kind of setup to make it more robust. So, since I'll be expanding on the concepts from the other post, I suggest you just quickly go over it if your not yet familiar with this kind of setup.<br />
<br />
<b>Global Scale</b><br />
<b><br /></b>
Lets start with something simple. Allowing your rig to scale is usually a good idea. If we are using the stretchy IK as a smaller component of a rig, we need to make sure it will scale uniformly with the rest of the rig. We can start by looking at our <b>distanceShape </b>node. We are using distance measurements to get this setup to work, however, our translation values from joint to joint does not change when we scale the whole rig up. Therefore we need to factor in the <i>global</i> scale value. We do this by dividing the <i>distance</i> from the <b>distanceShape </b>node by the <i>global </i>scale.<br />
<br />
We can start by creating a <b>multiplyDivide </b>node. Lets set the operation to <i>divide</i>. And next, we want to connect the <i>distance</i> attribute to <b>input1X </b>and our <i>global </i>scale attribute to <b>input2X</b>.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3_pnviZJKLQnbZAqoeoJPMTtG4cxv4YaW9RAXY5yEq_9IzFTSZqib_k3LfYEqEmuM4SCVUkoe_5TosoXzGIBPP8V9XWVqcQYdOZ42Yx1MdfO-S-ulZhBi5I3p54IPE7BH45mh0m7j0gY_/s1600/global+scale+connection.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="247" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg3_pnviZJKLQnbZAqoeoJPMTtG4cxv4YaW9RAXY5yEq_9IzFTSZqib_k3LfYEqEmuM4SCVUkoe_5TosoXzGIBPP8V9XWVqcQYdOZ42Yx1MdfO-S-ulZhBi5I3p54IPE7BH45mh0m7j0gY_/s320/global+scale+connection.png" width="320" /></a></div>
<br />
As a tip, to create and use an alias for the scale attribute on the character node that will be scaling the entire rig, you can use the following mel command,<br />
<i><b><br /></b></i>
<i><b>aliasAttr globalScale characterNode.scaleY</b></i><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitHedmK8I6GYzNMmMCu3x1bErmU9cQX7bTuD8Tpzp6NwNzxCZgIXocDQE_1B9nKYgCgYuoObtyrAuBHS7ht-39H9r3MtC3DV-LVkBaJZJxQ_YRmMdo1zrPdSI0v1Jyak9ELrhR4DGR33KC/s1600/alias+attr+mel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="139" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitHedmK8I6GYzNMmMCu3x1bErmU9cQX7bTuD8Tpzp6NwNzxCZgIXocDQE_1B9nKYgCgYuoObtyrAuBHS7ht-39H9r3MtC3DV-LVkBaJZJxQ_YRmMdo1zrPdSI0v1Jyak9ELrhR4DGR33KC/s320/alias+attr+mel.png" width="320" /></a></div>
<br />
You can then connect the other two scale attributes to the new <i>globalScale </i>attribute. Then you can hide them if you'd like.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6ibnCfUwOZcXzxoFps6di4_sMtX7iPK1lqisUk8q_pIAgfqTNENhIWdzR98aj65LCiIYtquaB1vvGmB1EJl2xBYO_8z7TpFP8gHKeFSe_os5prWKsoscuGiCowsXZP4Npo1kJ0XkWtAJw/s1600/global+scale+y+z+connection.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="272" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj6ibnCfUwOZcXzxoFps6di4_sMtX7iPK1lqisUk8q_pIAgfqTNENhIWdzR98aj65LCiIYtquaB1vvGmB1EJl2xBYO_8z7TpFP8gHKeFSe_os5prWKsoscuGiCowsXZP4Npo1kJ0XkWtAJw/s320/global+scale+y+z+connection.png" width="320" /></a></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcKoqy7TXY7IsdQnBPn7iBVYA7tZ6FOE2WYt1jh7hifsQ4CcGh7WA6z0CggDC_6mEB-L4vOzYL5Ur14XCZ-CHXeZKzXF6ne3V0eTf35DFA2i4B8co29m1ck_DH_hAqn1Jl4LTpH9I2kJWz/s1600/hide+scale+attr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcKoqy7TXY7IsdQnBPn7iBVYA7tZ6FOE2WYt1jh7hifsQ4CcGh7WA6z0CggDC_6mEB-L4vOzYL5Ur14XCZ-CHXeZKzXF6ne3V0eTf35DFA2i4B8co29m1ck_DH_hAqn1Jl4LTpH9I2kJWz/s1600/hide+scale+attr.png" /></a></div>
<br />
Finally you can connect <b>outputX </b>on the <b>multiplyDivide </b>node to what was initially connected to the <i>distance </i>attribute.<br />
<br />
Here is a script you can use to create and connect the <i>global scale </i><b>multiplyDivide </b>node. It takes an <i>inPlug</i> string, an <i>outPlugs</i> list, and a <i>scaleDriver </i>string.<br />
<br />
For example,<br />
<br />
<b><i>inPlug = 'distanceShapeNode.distance'</i></b><br />
<b><i>outPlugs = [ 'conditionNode.firstTerm', 'conditionNode.colorIfTrueR' ]</i></b><br />
<b><i>scaleDriver = 'characterNode.globalScale'</i></b><br />
<pre class="brush: python">def createGlobalScaleNode(inPlug, outPlugs, scaleDriver):
''' Create node to allow setup to scale globally '''
globalScaleNode = cmds.createNode("multiplyDivide")
cmds.setAttr(globalScaleNode + ".operation", 2) # Divide
cmds.connectAttr(inPlug, globalScaleNode + ".input1X", f=True)
cmds.connectAttr(scaleDriver, globalScaleNode + ".input2X", f=True)
for plug in outPlugs:
cmds.connectAttr(globalScaleNode + ".outputX", plug, f=True)
return globalScaleNode</pre>
<br />
<b>Auto Stretch Toggle</b><br />
<br />
With the current stretchy IK setup we are stretching it automatically. Something that's always nice to have if your automating any kind of controlled movement, is to allow the animator to turn that automation off if he chooses to. So lets go ahead and create a toggle for that.<br />
<br />
Once again, start by creating a <b>multiplyDivide </b>node. This time lets leave the operation as <i>multiply</i>. Next we will need to create an actual attribute to toggle the stretch on and off.<br />
<br />
Add a new attribute to the IK control node, <i>autoStrech.</i><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHgnA-Pz8uyWYJH3ZJZ_Zt0EOKjjoymwQMslZKbinZPdjg6VEj_O9p0oivUELn60tqB76jkm9RmHppjw3-tWJ06kmh9jZhlvGrU4umwDpd0s68m3X-DNEzFKTiXK2l1DH02c69W5BhqoLZ/s1600/autoStretch+Add+Attr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiHgnA-Pz8uyWYJH3ZJZ_Zt0EOKjjoymwQMslZKbinZPdjg6VEj_O9p0oivUELn60tqB76jkm9RmHppjw3-tWJ06kmh9jZhlvGrU4umwDpd0s68m3X-DNEzFKTiXK2l1DH02c69W5BhqoLZ/s320/autoStretch+Add+Attr.png" width="240" /></a></div>
<i><br /></i>
As a tip, I like to create any type of switch control as an integer instead of a boolean. The reason for this is that from experience the fact that you cannot middle mouse drag the boolean attribute to switch values can get annoying after awhile. So I prefer using an integer from 0 to 1 so you can easily scrub the value on and off. But that's up to you.<br />
<br />
Now, we can connect the attributes. Lets go ahead and continue from where we left off in the last section.<br />
<br />
We can connect the <b>outputX </b>from the global scale <b>multiplyDivide </b>node to <b>input1X</b> and then connect the <i>autoStretch</i> attribute on the IK control to <b>input2X</b>. The result of this multiplication will be either 0 or the divided distance itself. We can then pipe the <b>outputX</b> into the <b>secondTerm </b>attribute on condition node that we already had in place from the original setup. Because the condition operation is set to <i>greater or equal</i>, when our toggle is off, we will have <i>original length >= 0 </i>return <b>True</b>, and output the original distance, which is correct. The setup now works for when auto stretch is on or off.<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsZJUZGZrHE7jSsbLDEMZ5IJ6Q1lgrk0pat-RG9aJUSxdkOKbfbDyHwla6_hyphenhyphenhR9NHEY7sopVuOgKBpj1lvM7WTf56N2j0pQcpdEsMZ7faGKUtRmZ1W2G8_9FMONdbHs5zWw0uWqVnLhQb/s1600/condition+node+attribute+editor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="285" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgsZJUZGZrHE7jSsbLDEMZ5IJ6Q1lgrk0pat-RG9aJUSxdkOKbfbDyHwla6_hyphenhyphenhR9NHEY7sopVuOgKBpj1lvM7WTf56N2j0pQcpdEsMZ7faGKUtRmZ1W2G8_9FMONdbHs5zWw0uWqVnLhQb/s320/condition+node+attribute+editor.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_oFdDmlda9a4tv1tyra5QgVPZ98zDO7Zy7Ls1dr8YuR8pNoOfvtM29LnhK3o3Jp-gjOgpeooOi3PVVxR6NfBuzbCDJnydp7DwSykgxVmaC0_i-Guse9E-pP10oZvCZinCpdiSwkZ0MOvh/s1600/autoStretch+conn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="193" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi_oFdDmlda9a4tv1tyra5QgVPZ98zDO7Zy7Ls1dr8YuR8pNoOfvtM29LnhK3o3Jp-gjOgpeooOi3PVVxR6NfBuzbCDJnydp7DwSykgxVmaC0_i-Guse9E-pP10oZvCZinCpdiSwkZ0MOvh/s320/autoStretch+conn.png" width="320" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<b>Pole Vector Stretch</b><br />
<b><br /></b>
As we know our IK joints can either use a <i>Single Chain </i>or a <i>Rotate-Plane </i>solver. In the case of the RP solver we can use a polevector constraint. This next setup is a nice feature to give your character rig for example if it has arms and it needs to be able to plant its elbow on something and move his forearm freely. Allowing to stretch the mid joint to the PV object can let you place the polevector ( and therefore the elbow ) wherever you wish and then animate the lower arm without affecting the upper arm.<br />
<br />
How do you go about doing that? Well the setup is a little more intricate. That's why I'll also be going over it in this video <a href="https://vimeo.com/70897088" target="_blank">here</a>. Hopefully it will help you understand the setup a little better.<br />
<br />
We will first start by creating two extra distance nodes. One from the startJoint to the polevector object (we'll call this upper distance), and another from the polevector object to the endJoint (we'll call this lower distance).<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEsfv4zAPDv1ObQOA8G9ahrrZoMawRdy6tEhjV-Y8fQ3WUwI_iFdxr16BoG_ZNC6eMOHkNToKSf0xbFkhYCrhNkhDzZhuKQgkXoHukahWR9CDX4kj1-tGyE5xx00Z-tQOPYiqIA7qGUfVT/s1600/UpperLowerDistance.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="226" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiEsfv4zAPDv1ObQOA8G9ahrrZoMawRdy6tEhjV-Y8fQ3WUwI_iFdxr16BoG_ZNC6eMOHkNToKSf0xbFkhYCrhNkhDzZhuKQgkXoHukahWR9CDX4kj1-tGyE5xx00Z-tQOPYiqIA7qGUfVT/s320/UpperLowerDistance.png" width="320" /></a></div>
<br />
Next, lets go ahead and create on our IK control a <i>pvStretch </i>attribute. This time we can make it a <i>float </i>from 0 to 1.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJTY83KgA0L7uvkq_xKUBJOHbgb0JkJRclcR41c7MkDZIkZ1iBr3__fh9MiBXfReHBNDNY2opUtAJk3sOQxkgtzjrLCW0m3YYQOVX_5swaRlmI3kw0mDEYDxvwRae07Q9-AZIn-10s6fR2/s1600/pvStretch+Add+Attr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgJTY83KgA0L7uvkq_xKUBJOHbgb0JkJRclcR41c7MkDZIkZ1iBr3__fh9MiBXfReHBNDNY2opUtAJk3sOQxkgtzjrLCW0m3YYQOVX_5swaRlmI3kw0mDEYDxvwRae07Q9-AZIn-10s6fR2/s320/pvStretch+Add+Attr.png" width="240" /></a></div>
<br />
Now we need to create a <b>blendTwoAttr </b>node. This node will allow us to blend between two attribute values. Lets start with the mid joint (in an character rig example, this would be the character's elbow joint). For the mid joint we want to blend between the <i>stretch </i>node and the upper distance. This means the joint will either stretch with the IK control or will stretch to meet the distance from the start joint to the pole vector object.<br />
<br />
<b>Note</b>: The same rule applies here if we want to be able to make this setup scalable. We just need to divide these distances by the <i>global </i>scale.<br />
<br />
The <i>stretch </i>node will vary depending on the stretch method you are using. If we're stretching the joints using <b>scale </b>then our <i>stretch </i>node is the <i>stretchFactor</i><b> multiplyDivide </b>node. If <b>translate</b>, then our <i>stretch</i> node will be the <i>stretchMultiply</i><b> multiplyDivide </b>node.<br />
<br />
Next we need to make sure we connect our <i>pvStretch </i>attribute to the <b>attributeBlender</b> attribute on the <b>blendTwoAttr </b>node. Finally, we can connect the <b>output </b>to the joint's <i>stretch axis. </i>Again, this will depend on the stretch method being used and also the joint's orientation. For example, if we are stretching using translation and <i>x</i> is going down the joint (default), then we want to connect to the joint's <b>translateX</b> attribute.<br />
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
From here we simply repeat the process for the end joint, and blend between the <i>stretch</i> node and the lower distance.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIHYMEvcfVDkNw4f92mVCNRy0n7qt7HQnsVz_THejtmoGlCDYaqurBFAqCAcb1J-RogdDIyM2LyOeRC_ncr9GG_TFT8HBpAsMPrGlMV54U5ekvNIpS9KDjJTSBu2IttT_Bn6moKxLPmhzS/s1600/pv+strtch+conn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="221" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhIHYMEvcfVDkNw4f92mVCNRy0n7qt7HQnsVz_THejtmoGlCDYaqurBFAqCAcb1J-RogdDIyM2LyOeRC_ncr9GG_TFT8HBpAsMPrGlMV54U5ekvNIpS9KDjJTSBu2IttT_Bn6moKxLPmhzS/s320/pv+strtch+conn.png" width="320" /></a></div>
<br />
Great! No we have a <i>pvStretch</i> attribute that allows us to stretch our mid joint to the polevector object from any position (stretched or not).<br />
<br />
To make the setup process a little quicker, here is a little script to create and connect the <b>blendTwoAttr</b> node. It takes an <i>inPlugs </i>list, an <i>outPlug </i>string, and a <i>blenderPlug</i> string.<br />
<br />
For example,<br />
<b><i><br /></i></b>
<b><i>inPlugs = [ 'stretchFactorNode.outputX', 'upperDistanceNode.distance' ]</i></b><br />
<b><i>outPlug = 'joint.StretchAxis'</i></b><br />
<b><i>blendPlug = 'ikCtrl.pvStretch'</i></b><br />
<pre class="brush: python">def createBlendAttrNode(inPlugs, outPlug, blenderPlug):
''' Create node to support PV stretch '''
# create blendTwoAttr nodes
blendAttrNode = cmds.createNode("blendTwoAttr", n="%s_blendAttr" % blenderPlug)
# connect nodes
for i,plug in enumerate(inPlugs):
cmds.connectAttr(plug, blendAttrNode + ".input[%d]" % i, f=True)
cmds.connectAttr(blenderPlug, blendAttrNode + ".attributesBlender", f=True )
cmds.connectAttr(blendAttrNode + ".output", outPlug, f=True)
return blendAttrNode
</pre>
<br />
You can also check out the download page <a href="http://luizmoreira2012br.blogspot.com/p/downloads.html" target="_blank">here</a>, for the latest update on the stretchyIkChain tool, which incorporate these new features with a simple gui, but also allows for easy access to the backend for creating the setup from command line.<br />
<br />Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-4292584314086974462013-07-11T20:24:00.003-07:002013-07-11T20:24:36.668-07:00Using Python Iterables<div style="background-color: white; border: 0px; clear: both; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px; margin-bottom: 1em; padding: 0px; vertical-align: baseline; word-wrap: break-word;">
Lets take a look at how we can use iterables in our workflow when rigging for writing procedural methods or classes. Im talking rigging modules, custom nodes, tools, etc..<br />
<br />
Lets first understand what are iterables.</div>
<h2 style="background-color: white; border: 0px; font-family: 'Trebuchet MS', 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 19px; line-height: 1.3; margin: 0px 0px 1em; padding: 0px; vertical-align: baseline; word-wrap: break-word;">
Iterables</h2>
<div>
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">The idea of iteration can be easily seen in a for loop where we read the items in a list one by one and print them.</span><br />
<pre class="brush: python">numbers = [1, 2, 3, 4, 5]
for i in numbers:
print i
</pre>
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">Result:</span><br />
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">1</span><br />
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">2</span><br />
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">3</span><br />
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">4</span><br />
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">5</span><br />
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;"><br /></span>
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">The list <b>numbers</b> is an iterable. When you instance a list through list comprehension you are creating an iterable.</span><br />
<pre class="brush: python">numbers = [x * x for x in range(5)]
for i in numbers:
print i
</pre>
<span style="background-color: white; font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">Result:</span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">0</span></span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">1</span></span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">4</span></span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">9</span></span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">25</span></span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">Other examples of iterables are strings, files, dictionaries, etc... Iterables are useful because they can be read as often as you want.</span></span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">To further understand iterables lets discuss generators.</span></span><br />
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span>
<br />
<h2 style="background-color: white; border: 0px; font-family: 'Trebuchet MS', 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 19px; line-height: 1.3; margin: 0px 0px 1em; padding: 0px; vertical-align: baseline; word-wrap: break-word;">
Generators</h2>
</div>
<div>
<span style="font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">Generators are what we call iterators. These we can only iterate over one time. The reason for this, is because unlike iterables that store the values in memory, generators create the values when they are queried.</span></div>
<pre class="brush: python">numbers = (x*x for x in range(5))
for i in numbers:
print i
</pre>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">Result:</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">0</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">1</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">4</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">9</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">25</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">Notice here we use parenthesis () and not brackets [ ]. Once we iterate over these values, we are done, we can't iterate read it again.</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">Finally we can take a look at yield.</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span></div>
<div>
<h2 style="background-color: white; border: 0px; font-family: 'Trebuchet MS', 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 19px; line-height: 1.3; margin: 0px 0px 1em; padding: 0px; vertical-align: baseline; word-wrap: break-word;">
Yield</h2>
</div>
<div>
<span style="font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">Yield is a function that behaves similar to a <b>return </b>call. However, what it returns is a generator.</span></div>
<div>
<span style="font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;"><br /></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">This is nice because now if we can iterate over all nodes in the current scene that fit a specific requirement for example. </span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">Lets iterate over all 3 joints in the scene:</span></span></div>
<pre class="brush: python">import maya.cmds as cmds
def iter():
for node in cmds.ls(type="joints"):
yield node
joints = iter()
for joint in joints:
print joint
</pre>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">Result:</span></span></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">joint1</span></span></div>
<div>
<span style="font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">joint2</span></div>
<div>
<span style="font-family: Arial, 'Liberation Sans', 'DejaVu Sans', sans-serif; font-size: 14px; line-height: 18px;">joint3</span></div>
<div>
<br /></div>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">If your writing your own class that creates a custom node (usually accompanied by an identifier under a custom attribute) you can write a class method to iterate over all custom nodes in the scene with something like this:</span></span></div>
<pre class="brush: python">@classmethod
def iter(cls):
for node in cmds.ls(type="network"):
if cmds.attributeQuery("type", exists=True, node=node):
if cmds.getAttr(node + ".type") == cls.TYPE:
yield cls(node)
</pre>
<div>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;"><br /></span></span>
<span style="font-family: Arial, Liberation Sans, DejaVu Sans, sans-serif;"><span style="font-size: 14px; line-height: 18px;">In this case the example class will create a custom node of type <b>network</b> and it searches for a custom attribute as an identifier called <b>type</b>. The class has an attribute <b>TYPE</b>. We've found our custom node if the node attribute value matches the value in <b>TYPE.</b></span></span></div>
<div>
<br /></div>
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-10076718998366284722013-07-10T21:42:00.005-07:002013-07-12T10:04:19.573-07:00Tips n Tricks: Managing skin cluster weightsIn this post I'll go over how you can code something to get and set skin cluster weights in Maya. We'll also discuss different things you can do to improve performance, especially if were working with a high resolution model. What will be covered here can be done using the API for more efficiency and better performance. But first we will go over simply using <b>maya.cmds</b>
<br />
<br />
Our first order of business is to look at how we can query the weights in a skin cluster on a vertex base and parse this information to store in an organized manner that we can use later.<br />
<br />
If you search around you'll find than many people suggest using <b>MFnSkinCluster.getWeights</b> method. In its defense it is the fastest to query a high resolution mesh with many influences. However, it return weights for all influences, and as you may already know, not all influences are use for every vertex in the skinCluster, having weight of 0, so you end up with a lot of unnecessary data. Since this stage of querying the weights is to give us data to use later, wether we will be normalizing, modifying, or what have you, when we actually have to iterate through all this 'empty' data there will be a drastic decrease in performance time.<br />
<br />
In this example, we will take a look at the skinCluster node itself. The next thing is very important to understand. The node has an attribute called <b>weightList</b>.<b> </b>This attribute has indices based on the vertex id for the mesh. For each index, there is another attribute called <b>weights</b>.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUT-ee1wuV3fb1G7VVlcolcTzH29PkZKp9E1tYxr8M5Hud-7T09Nlw7YH3MtVX-K8r2FituaMtaFkSTpMdPcEG34VIkyeFze2oWYeeOaJkxovAxaelg4CHMlsJouAzsZ9Vv0aIxii5heM9/s1600/Screen+shot+2013-07-10+at+10.05.13+PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em; text-align: center;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjUT-ee1wuV3fb1G7VVlcolcTzH29PkZKp9E1tYxr8M5Hud-7T09Nlw7YH3MtVX-K8r2FituaMtaFkSTpMdPcEG34VIkyeFze2oWYeeOaJkxovAxaelg4CHMlsJouAzsZ9Vv0aIxii5heM9/s320/Screen+shot+2013-07-10+at+10.05.13+PM.png" width="213" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
This attribute has index based on the influence object id, and each of these store a weight value for the vertex | influence combination.<br />
<br />
Here is some code to query the vertex weights for the skin cluster:<br />
<pre class="brush: python">def getSkinWeights(skinClusterName):
''' Get the weights per vertex on the meshName with the skinClusterName
'''
# to store the weight information
# the key represents the vertex id and the value another dictionary
# with keys representing the influence object id and the the value the weight as a double
# Format: {vertexId: {influenceId: weight, ...}, ...}
weights = {}
# get the Influence Objects
influenceObjects = cmds.skinCluster(skinClusterName, q=True, weightedInfluence=True)
# get the size of the vertex weight list
weightListSize = cmds.getAttr(skinClusterName + ".weightList", size=True)
# build weight dictionary
for vertexId in range(0,weightListSize):
# to store vertex weights
vertexWeights = {}
for influenceId in range(len(influenceObjects)):
weight = cmds.getAttr(skinClusterName + ".weightList[%d].weights[%d]" % (vertexId,influenceId))
# only store non-zero weights
if weight != 0:
vertexWeights[influenceId] = weight
# store the weights for the vertex id
weights[vertexId] = vertexWeights
return weights
</pre>
<br />
Notice I opted for using a dictionary. The way you want to store the data is up to you really. However, dictionaries are really powerful for this sort of thing. We can quickly access data based on the id, and it works great since we can query only the verts we choose to.<br />
<br />
With the weights stored we can now turn to how we can set the weights. Once again, if you look, you'll find that using <b>MFnSkinCluster.setWeights </b>is a popular method and fast. But since we opted to query the weights in our own method, we can now quickly set our weight values using <b>cmds.setAttr</b>. In addition, what is really nice is that we also get <b>undo </b>which we don't with <b>setWeights</b>. Trust me this is a really nice feature.<br />
<br />
So we've covered how to query the skin weights. After which you can modify the weight values however you wish. Then we can set the weights back to the skinCluster.<br />
<br />
Here is some code to set the vertex weights to the skin cluster:<br />
<pre class="brush: python">def setSkinWeights(skinClusterName, weights):
''' Set the modified weights to the skinClusterName.
'''
for vertexId in weights:
# get influence dictionary
influenceDict = weights[vertexId]
# check if vertex is weighted - if there are any influence objects
if influenceDict.items():
for influenceId in influenceDict:
plug = skinClusterName + ".weightList[%d].weights[%d]" % (vertexId,influenceId)
# set the weight
weight = influenceDict[influenceId]
cmds.setAttr(plug, weight)</pre>
<div>
<br /></div>
Its good to point out that this method will set the new weights fine, however it wont check for your if the weights add up to 1, if the weight value is positive, if the influence object doesn't exist, and other possible errors. So its a good idea to have checking method to take care of these things. As far as checking if the weights add up to 1, its an option to normalize them after you've modified them. The <b>cmds.skinPercent </b>has a normalize flag for that. I've noticed that when normalizing the weights while the script is running causes undesired results on vertices that haven't been assigned their new weight values.<br />
<div>
<br /></div>
<div>
This method of using <b>setAttr</b> is already faster than <b>skinPercent</b>, and can be faster depending on what you plan on doing with it, can be faster than <b>MFnSkinCluster </b>as well. Lets just expand our method to be a little more robust.</div>
<div>
<br />
You could also use prune small weights, also in <b>skinPercent</b>, before setting the new weights to remove all zero weights.<br />
<br /></div>
<div>
Hero is the modified code to allow us to do just that:</div>
<pre class="brush: python">def setSkinWeights(skinClusterName, weights, meshName, nonZero=True):
''' Set the modified weights to the skinClusterName. The nonZero flags assures that
only nonZero weights are set.
'''
for vertexId in weights:
# get influence dictionary
influenceDict = weights[vertexId]
# check if vertex is weighted - if there are any influence objects
if influenceDict.items():
for influenceId in influenceDict:
if nonZero:
influenceObj = cmds.listConnections("skinCluster1.matrix[%d]" % influenceId)
if influenceObj:
influenceObj = influenceObj[0]
cmds.setAttr(influenceObj + ".liw", 0)
# prune small weight values
# need to turn off normalize to use prune
plug = skinClusterName + ".normalizeWeights"
normalize = cmds.getAttr(plug)
if normalize:
cmds.setAttr(plug, 0)
cmds.skinPercent(skinClusterName, meshName, nrm=False, prw=.01)
# return normalizing to what it was
if normalize:
cmds.setAttr(plug, normalize)
plug = skinClusterName + ".weightList[%d].weights[%d]" % (vertexId,influenceId)
# set the weight
weight = influenceDict[influenceId]
cmds.setAttr(plug, weight)
</pre>
<br />Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-90382431484444246972013-07-03T20:22:00.001-07:002013-07-03T20:23:50.003-07:00Space Switching Part 5 uploadedJust finished uploading Part 5 for the video series. I admit this took longer than I intended for it to take me to upload it. But its finally here. Part 5 will wrap up the tool implementation. I'll have the next video parts uploaded sooner. They will go over the animation tool to interact with the space switching controls in your rig.<br />
<div>
<br /></div>
<div>
Watch part 5 <a href="https://vimeo.com/69667865" target="_blank">here</a></div>
<div>
<br /></div>
<div>
Read about the series here in the announcement post.<br />
<a href="http://luizmoreira2012br.blogspot.com/2013/06/space-switching-video-series.html" target="_blank">http://luizmoreira2012br.blogspot.com/2013/06/space-switching-video-series.html</a></div>
<div>
<br /></div>
<div>
Enjoy! And leave some feedback if you can.</div>
<div>
Thanks!</div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-70385582864763307882013-06-22T19:21:00.001-07:002013-06-22T19:21:43.168-07:00Spidey Rig 1.0 project - Joint LayoutThis is a cartoon rig Im designing that will soon be available for download. It will be a robust, flexible and animator friendly rig for easy and intuitive interaction. Ill be posting notes and studies for different features that will be included in the rig. Here is the joint layout for the basic hierarchy and rotation order plan.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijB4e10Xvpd4TUCAxWox3LpAh6lIS2oxpMS0WfOaY4-cHAmihvfVVDWJPmsw_OZEIEO0l3T-pbcofkbZEJEHph4Fcu1NApCK3wON2dnXwU_it5fQDD1vEKiaZuE_-h2uFid25vos5SHkUf/s1600/jointLayout.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="449" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEijB4e10Xvpd4TUCAxWox3LpAh6lIS2oxpMS0WfOaY4-cHAmihvfVVDWJPmsw_OZEIEO0l3T-pbcofkbZEJEHph4Fcu1NApCK3wON2dnXwU_it5fQDD1vEKiaZuE_-h2uFid25vos5SHkUf/s640/jointLayout.jpg" width="640" /></a></div>
<br />
Thanks!Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-41762398572984591302013-06-19T21:15:00.001-07:002013-06-22T19:16:26.214-07:00Creating Stretchy IK Joint ChainI thought it would be nice to go over how to create a basic yet so important setup in character rigging. The concept itself is quite simple, yet I've seen many times people struggle with the idea. So in this post I'll go over how to setup a stretchy joint chain being driven by an Ik solver using code. The code itself will be generating utility nodes and hooking things up for us to make the setup work.<br />
<br />
If you want you can download a tool that uses this technique <a href="https://www.dropbox.com/s/khjom3gp438xhuy/lpStretchyIKChain.py" target="_blank">here</a><br />
<br />
Watch instructional video <a href="https://vimeo.com/68753513" target="_blank">here</a><br />
<br />
From this hopefully you'll be able to use the information to create your own version of this setup. So lets get to it. We will be using the python maya.cmds package<br />
<pre class="brush: python">import maya.cmds as cmds</pre>
For us to setup a stretchy joint chain, first we will require some joints.
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnnhTV8WUbrmn6CbwxB9mPVXBvAkmC21zhPBNTEx1WcQprUydyJQrZBRjleTh-cL61hjP52zWlkNfvvCqywiCiBUpjbRl7PzRcmEVRHtTxb79O1_Nlw-n9zSD9SrjewqfcgUzK0wuOdME1/s1600/joints.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgnnhTV8WUbrmn6CbwxB9mPVXBvAkmC21zhPBNTEx1WcQprUydyJQrZBRjleTh-cL61hjP52zWlkNfvvCqywiCiBUpjbRl7PzRcmEVRHtTxb79O1_Nlw-n9zSD9SrjewqfcgUzK0wuOdME1/s320/joints.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Joint Chain</td></tr>
</tbody></table>
Once the joints have been created all we really need is to specify what is the start and end of our stretchy joint chain. With a start and end joint we can go ahead an create a new Ik handle. For this demonstration we will use a single chain solver (SC). So lets define a method that takes in a start and an end joint. Lets also go ahead and also rename and store the new nodes created with the Ik handle so we can keep track of them.<br />
<br />
<pre class="brush: python">def stretchyIKChain(startJoint, endJoint):
ikNodes = cmds.ikHandle(sj=startJoint, ee=endJoint, solver="ikSCsolver", n=startJoint + "_ikHandle")
ikNodes[1] = cmds.rename(ikNodes[1], startJoint + "_effector")
ikHandle = ikNodes[0]
</pre>
<br />
If we were to describe the process of making this joint chain stretch uniformly, we would say that we want each individual 'bone' to stretch by the same factor. That stretch factor would be the current length divided by the original length. So right from the start we already know we need a way to measure the length from our start joint to the end joint. Thankfully we have something called the 'distance tool'. In maya, it is located under Create -> Measure Tools -> Distance Tool. We want to create a distance node measuring form the start joint to the end joint. The tool will create a locator at each end. So to do this in our code we will create locators and hook it up ourselves.<br />
<br />
<pre class="brush: python"> # create locators for distance tool node
startLocator = cmds.spaceLocator(n=startJoint + "_startLocator")[0]
cmds.pointConstraint(startJoint, startLocator, mo=False, n=startLocator + "_pointConstraint")
endLocator = cmds.spaceLocator(n=endJoint + "_endLocator")[0]
cmds.xform(endLocator, ws=True, absolute=True, translation=cmds.xform(ikHandle, q=True, ws=True, translation=True))
cmds.pointConstraint(endLocator, ikHandle, mo=False, n=ikHandle + "_pointConstraint")
distanceNode = cmds.createNode("distanceDimShape", n="%s_%s_distance" % (startLocator,endLocator))
cmds.connectAttr(startLocator + "Shape.worldPosition[0]", distanceNode + ".startPoint")
cmds.connectAttr(endLocator + "Shape.worldPosition[0]", distanceNode + ".endPoint")
</pre>
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigAwnY3AMO8X_ZGquJ1qO3YaA-HdvKGBU080-I3-Zx4jR0cK8fF0x_lR89PIRsIP5op2iTsKqQo-5FqRY27h1HUvdEGs2T-m9h1gyfeW30Mil8sGOZcibmSGSkcZPoti1oprhDuEVUxFQX/s1600/Screen+shot+2013-06-19+at+9.27.32+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEigAwnY3AMO8X_ZGquJ1qO3YaA-HdvKGBU080-I3-Zx4jR0cK8fF0x_lR89PIRsIP5op2iTsKqQo-5FqRY27h1HUvdEGs2T-m9h1gyfeW30Mil8sGOZcibmSGSkcZPoti1oprhDuEVUxFQX/s320/Screen+shot+2013-06-19+at+9.27.32+PM.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Ik Joint Chain distanc</td></tr>
</tbody></table>
The point constraint will serve 2 purposes. With 'maintainoffset' set to False it will snap the locator to the position we want, and now the start locator will follow the start joint. For the end locator we first move it to the end joints positions, where the ikHandle is, and point constraint the ikHandle to the locator. This way, you can organize the nodes and put the ikHandle in other group and just work with the locator. The locator will be your control for the stretchy ikHandle, you can parent it or constraint it to another control curve if would like to. Next, we hook up the locator to the distance node through their worldPosition attribute.
<br />
<br />
I mentioned we will be dividing the current length by the original length. The distance node provides with current distance between the locators, and because we have hooked up the locators to our ik joints, it will update correctly. However, we need to get the original length of the joint chain. We could just get the distance value right now, but what if the joint chain was laid out straight like we did it here. What if it was something like this<br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEighrfHvYpVGyfPr4sCetyava_RfS3jtpjZo2rYG5TouEFLNGw6kz8I82-OK3hxqumLJgPOqtOZmytDPjaRmmz4BhRLH7qwH906MGBqUH742ouqoLYSBbFhkI-aJg8CkA1UOfW76zeuEB4q/s1600/Screen+shot+2013-06-19+at+7.45.17+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" height="195" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEighrfHvYpVGyfPr4sCetyava_RfS3jtpjZo2rYG5TouEFLNGw6kz8I82-OK3hxqumLJgPOqtOZmytDPjaRmmz4BhRLH7qwH906MGBqUH742ouqoLYSBbFhkI-aJg8CkA1UOfW76zeuEB4q/s320/Screen+shot+2013-06-19+at+7.45.17+PM.png" width="320" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">Non-straight Joint chain</td></tr>
</tbody></table>
Then the default distance doesn't actually represent the length of the joint chain. If we move the ikHandle so that the joints are lined up straight, we will then have the actual original length. Its from this point on that we want the joints to start stretching. But we won't do it this way.<br />
<br />
One solution could be to require whoever is setting this up, to have the joints straighten out beforehand. But that's really inconvenient. The other, and much better alternative, is to find the total length of the joint chain by adding the translateX values of all child joints starting with the start joint's first child all the way to the end joint. Now I said translateX because by default the x-axis is the joint's rotation axis, the one going down the joint. But what if that's not the case. Then need first a way to find the rotation axis. To do that we just need to check any of the child joint (we will use the end joint) for its translations and whichever axis has a value, that axis is the rotation axis. This is how that would look like<br />
<br />
<pre class="brush: python">def getRotationAxis(joint):
''' Get the rotation axis from the translations in the child joint '''
# get the translation values of the child joint
translate = cmds.getAttr(joint + ".t")[0]
# to store the axis string
axis = ""
for i, t in enumerate(translate):
# check which translate axis has non-zero values
# check for + and - values. Joints could be going opposite direction
value = abs(t)
if value > .0001:
if i == 0:
axis = "x"
elif i == 1:
axis = "y"
elif i == 2:
axis = "z"
if not axis:
cmds.error("Child joint is too close to its parent.")
return axis
</pre>
<br />
Now, that we have a way to determine the ration axis lets use it to find the original length of our joint chain.<br />
<br />
<pre class="brush: python"> rotationAxis = getRotationAxis(endJoint)
# to store original joint chain length
originalLength = 0.0
# to store child joints
childJoints = []
currentJoint = startJoint
done = False
while not done:
# get the child joints for the current joint
children = cmds.listRelatives(currentJoint, c=True)
children = cmds.ls(children, type="joint")
# we reached the end of the joint chain
if not children:
done = True
else:
child = children[0]
childJoints.append(child)
# start summing the original length. Add the absolute for + and - translate values
originalLength += fabs(cmds.getAttr(child + ".t" + rotationAxis))
currentJoint = child
# we reached the end of the stretchy joint chain
if child == endJoint:
done = True
</pre>
<br />
Now that we have the original length we can find the stretchFactor by dividing the current distance by the original length. We can use a 'multiplyDivide' node for this<br />
<br />
<pre class="brush: python"> # divide distance by original length
stretchFactorNode = cmds.createNode("multiplyDivide", n=ikHandle + "_stretchFactor")
cmds.setAttr(stretchFactorNode + ".operation", 2) # Divide
cmds.connectAttr(distanceNode + ".distance", stretchFactorNode + ".input1X")
cmds.setAttr(stretchFactorNode + ".input2X", originalLength)
</pre>
<br />
Now that we have a normalized distance we can use it to stretch the joints. We can stretch the joints in two different ways. We can stretch them by driving the scale or by driving the translate of the child joints. To stretch using scale we can connect the output of the multiplyDivide node (stretchFactor) to the scaleX (or whatever our rotation axis is) of all joints except the end joint.<br />
<br />
<pre class="brush: python"> # connect joints to stretchy math nodes using scale
childJoints.insert(0, startJoint)
childJoints.remove(endJoint)
for joint in childJoints:
multiplyNode = cmds.createNode("multiplyDivide", n=joint + "_translate_stretchMultiply")
cmds.setAttr(multiplyNode + ".input1X", cmds.getAttr(joint + ".t" + rotationAxis))
cmds.connectAttr(stretchFactorNode + ".outputX", multiplyNode + ".input2X")
cmds.connectAttr(multiplyNode + ".outputX", joint + ".t" + rotationAxis)
</pre>
<br />
Nice! We are finally done setting up our stretchy joints using scale. Now lets look at how to do it for translate. The idea is the same, but with translate we want to multiply the joint's original translate by our stretchFactor instead of just plugging it in. We will do this for all joints except the first the start joint. So lets do that.<br />
<br />
<pre class="brush: python"> # connect joints to stretchy math nodes using translate
for joint in childJoints:
multiplyNode = cmds.createNode("multiplyDivide", n=joint + "_translate_stretchMultiply")
cmds.setAttr(multiplyNode + ".input1X", cmds.getAttr(joint + ".t" + rotationAxis))
cmds.connectAttr(stretchFactorNode + ".outputX", multiplyNode + ".input2X")
cmds.connectAttr(multiplyNode + ".outputX", joint + ".t" + rotationAxis)
</pre>
<br />
Awesome! We now have a stretchy joint chain! So now you could even add the option to the stretchyIK function to allow the user to pick between scale and translate setup. I'll set the default method to translate.<br />
<br />
<pre class="brush: python">def stretchyIKChain(startJoint, endJoint, stretchMethod="translate")
</pre>
<br />
So, are we done? Well, our joints certainly stretch now when we pull on the ikHandle. But what if we move the ikHandle towards the start joint. Oh-oh! That's not good. The preferred angle on the joints are neglected and we are now overriding the joints, and they are now actually squashing instead of maintaining their original length. If you want that effect, well you don't need an ikHandle in the first place. And now you know how to set it up to have stretchy joints that squash and stretch. With what you've learned here. Its really easy to create an expression to do that for us.<br />
<br />
In fact, I might go over in another post, how to create stretchy joints using expressions. But, going back to where we were, we want to have the joints only stretch when the distance is greater than or equal to the original length. So how do we do that using utility nodes? Using a 'condition' node! Like this<br />
<br />
<pre class="brush: python"> # create a condition node to allow stretching only when the distance is >= the original length
conditionNode = cmds.createNode("condition", n=startJoint + "_condition")
cmds.setAttr(conditionNode + ".operation", 3) # >=
cmds.setAttr(conditionNode + ".firstTerm", originalLength)
cmds.setAttr(conditionNode + ".colorIfTrueR", originalLength)
cmds.connectAttr(distanceNode + ".distance", conditionNode + ".secondTerm", f=True)
cmds.connectAttr(distanceNode + ".distance", conditionNode + ".colorIfFalseR", f=True)
</pre>
<br />
Now we just need to make sure this code comes before the one where we create and hook up the 'stretchFactorNode' (multiplyDivide). And we will need to change that code for this<br />
<br />
<br />
<pre class="brush: python"># divide distance by original length
stretchFactorNode = cmds.createNode("multiplyDivide", n=ikHandle + "_stretchFactor")
cmds.setAttr(stretchFactorNode + ".operation", 2) # Divide
cmds.connectAttr(conditionNode + ".outColorR", stretchFactorNode + ".input1X")
cmds.setAttr(stretchFactorNode + ".input2X", originalLength)
</pre>
<br />
Great! Now we can say we are officially done! Go and create your own stretchy IK joints!Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-88860821206116701272013-06-18T19:11:00.000-07:002013-06-19T21:43:17.998-07:00lpMultiShaderTool.pyThe idea to put together a tool like this came from a peer of mine needing to be able to identify material assigned to components.<br />
<br />
In this post you can see the approach taken to doing this:<br />
<a href="http://luizmoreira2012br.blogspot.com/2013/06/finding-shader-for-face-component.html">http://luizmoreira2012br.blogspot.com/2013/06/finding-shader-for-face-component.html</a><br />
<br />
In summary, this tool allows for the creation and interaction of shaders in both object and component level. Users can copy and transfer shaders across objects. They can also empty shaders and reassign new objects to them, as well as deleting shaders and their shading groups.<br />
<br />
Download script <a href="https://www.dropbox.com/s/4gi23o2dobhs3z3/lpMultiShaderTool.py" target="_blank">here</a><br />
Watch video demo <a href="https://vimeo.com/68669548" target="_blank">here</a><br />
<br />
Here are instructions on how to use the tool:<br />
<br />
First make sure to place the script in maya/scripts directory.<br />
<br />
In Windows it's something like this:<br />
<i>C:/Program Files/<span class="s1">Autodesk</span>/Maya2013/<span class="s1">Python</span>/<span class="s1">lib</span>/site-packages/ </i><br />
<br />
In Mac:<br />
<i>Users/yourUserName/Library/Preferences/<span class="s1">Autodesk</span>/<span class="s1">maya</span>/2013-x64/scripts/</i><br />
<br />
<table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto; text-align: center;"><tbody>
<tr><td style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuWmvtr2GSnF_t1LvM1J1KzBMpe2_YmMqU5Wr2vTysfvOvx_GjcR-KJzMJV1ggYeO1y0uveN87pH64cXdpkU2KyJMG-3KfFxdVgJACxCVRjG9pnPG6mwsyR9uU8rtY8ZWdWMc6twPtlkB2/s1600/Screen+shot+2013-06-18+at+10.03.43+PM.png" imageanchor="1" style="margin-left: auto; margin-right: auto;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjuWmvtr2GSnF_t1LvM1J1KzBMpe2_YmMqU5Wr2vTysfvOvx_GjcR-KJzMJV1ggYeO1y0uveN87pH64cXdpkU2KyJMG-3KfFxdVgJACxCVRjG9pnPG6mwsyR9uU8rtY8ZWdWMc6twPtlkB2/s1600/Screen+shot+2013-06-18+at+10.03.43+PM.png" /></a></td></tr>
<tr><td class="tr-caption" style="text-align: center;">lpMultiShaderTool v1.0- Live update of current shader</td></tr>
</tbody></table>
<br />
<div>
To run the tool:<br />
<br />
<pre class="brush: python">import lpMultiShaderTool
reload(lpMultiShaderTool)
lpMultiShaderTool.lpMultiShaderToolUI()</pre>
<b><br /></b>
<b>1. Assign New Shader to Selected:</b><br />
Creates a new shader and shading group and connects to selected objects<br />
<br />
<b>2. Get Shader from Selected:</b><br />
Copies the shader assigned to the selected object and stores it. Requires that only one object is<br />
selected<br />
<br />
<b>3. Add Shader to Selected:</b><br />
Adds the current shader to the selected objects<br />
<br />
<b>4. Select All with Shader:</b><br />
Selects all objects in the scene with the current shader<br />
<br />
<b>5. Remove All from Shader:</b><br />
Empties shader, so that all objects previously shaded with it are now shaded with the default shader -<br />
lambert1. Doe<br />
<br />
<b><span style="color: blue;">6. Select All in Selected Shader: <i>NEW</i></span></b><br />
<b> </b>Selects all objects that have the same shader as the selected object without affecting the current <br />
shader.<br />
<br />
<b>7. Switch All in Shaders:</b><br />
Gets the shader from the selected object and assigns it to all the objects with the current shader.<br />
Applies the current shader to all objects with the same shader as the selected object. In other words,<br />
switches the objects from on shading group to the other.<br />
<br />
<b>8. Delete Current Shader:</b><br />
Deletes shader and shading group for the current shader, and returns all objects previously with that<br />
shader to the default shader: lambert1<br />
<br />
<b>9. Reset To Default Shader:</b><br />
Assigns the default shader to all selected objects.</div>
Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0tag:blogger.com,1999:blog-4665036873052018975.post-45466801227743928652013-06-18T12:33:00.001-07:002013-06-18T19:19:36.556-07:00Finding the shader for a face componentYou can find the shader group attached to a face component using python like this:<br />
<br />
<pre class="brush: python">import maya.cmds as cmds
def getFaceShader(face):
''' Return the shading group for the given face or None '''
# get all shading groups
allSG = cmds.ls(type='shadingEngine')
for shadingGrp in allSG:
# check if the face is in the set for the shading group
if cmds.sets(face, isMember=shadingGrp):
return shadingGrp
# no shading group was found for the face
return None
</pre>
To use this you need to give it a face component. For example, select a face on a polygon object and do this:<br />
<pre class="brush: python">selectedFace = cmds.ls(selection=True)[0]
shadingGroup = getFaceShader(selectedFace)
</pre>
After you retrieve the shading group, if you want the actual shader (ex: blinn), you just need to do:<br />
<pre class="brush: python">shader = cmds.listConnections("%s.surfaceShader" % shadingGroup, source=True)
</pre>
Once you've extracted the shader from a component, its really easy to transfer it over to another selected polygon or faces.
You can actually do this by adding selected objects to the shading group set. If the items are in another set which is in the
same partition as the given set, the items will be removed from the other set in order to keep the sets in the partition mutually
exclusive with respect to membership. This is the code to do it:
<pre class="brush: python">selected = cmds.ls(sl=True)
if selected:
cmds.sets(e=True, forceElement=shadingGroup)
</pre>Anonymoushttp://www.blogger.com/profile/00284681356699608659noreply@blogger.com0