Dorian Fevrier's blog - Mot-clé - enJe m’appelle FEVRIER Dorian, je suis infographiste 3D passionné par mon métier, l’informatique en général, l’internet, la programmation et l’évolution de tout ce petit monde. Vous trouverez sur ce blog des tutoriaux, mes coups de cœurs, avis, etc.2024-01-02T23:48:05+01:00FEVRIER Dorianurn:md5:695d9c73474c33ce3dab043823509c4bDotclearfxguide and deep dataurn:md5:3433f9212ad011cdeab73d46fa9aa7a82014-10-15T22:09:00+02:002014-10-22T02:44:00+02:00NarannInfographie 3D - Boulotanimated featuredeep dataenfxguidesupervisor<p><img src="https://www.fevrierdorian.com/blog/public/billets/2014_10_18_fx_guide/fxguide_tn.png" alt="fxguide_tn.png" style="float:left; margin: 0 1em 1em 0;" title="fxguide_tn.png, oct. 2014" height="150" width="150" />As you might know, I work mainly on animated features. I am often involved with productions and RnD departments to talk about incoming challenges, in particular on the lighting side.</p>
<p>There are many situations where peoples argue how deep comp is canonical now and how it will change the face of the CGI... I'm often arguing it's not a "tiny" feature and, while it can be useful "sometime", it creates more troubles than it solve in practice, specially if its use is generalized (and on a animated feature you often need to "over-generalize" uses). Doing so, I'm facing to a lot of "What? But fxguide state a lot of animation studios use it! Dreamworks, Disney, etc...". :tuComprendRien:</p>
<p>I will not argue on the fact the projects I have to deal with are not running in such studios. I will not even argue about the actual budget compared to big production ones. I will simply quote something fxguide report and pray the various supervisors I have the opportunity to worked with will take this in consideration.</p> <blockquote><p>This article is not a shout against fxguide (I read it often and enjoy it a lot, their work is massive!) but mainly against peoples taking fxguide's articles as gospel while having no idea of technical implications using such high end techs fxguide talks about in real world production, specially on (sometime "low cost") animated features.</p></blockquote>
<p>fxguide is one of the most popular CGI news website. It's one of the few (the only one?) providing a lot of articles/interviews of well known personality and high end techs used in CG.</p>
<p>Few days ago, I read <a href="http://www.fxguide.com/featured/disneys-new-production-renderer-hyperion-yes-disney/" hreflang="en">this</a>.</p>
<p>I tends to take lightly what fxguide states because it often obfuscate a lot of details which are very important in a productions situations (all the "what if" questions actually). It's the "everything goes smooth and rosy in the best world ever 'cause the flexibility of our internal tools allow us to deliver high end shots in deadlines with lower cost ever" syndrome (not surprising in the CGI) while the reality of the day to day job is more like:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2014_10_18_fx_guide/PicassoGuernica_800.jpg" title="PicassoGuernica_800.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2014_10_18_fx_guide/.PicassoGuernica_800_m.jpg" alt="PicassoGuernica_800.jpg" style="display:block; margin:0 auto;" title="PicassoGuernica_800.jpg, oct. 2014" height="250" width="560" /></a></p>
<center><i><a href="https://en.wikipedia.org/wiki/Guernica_%28painting%29" hreflang="en">Guernica</a> from <a href="https://en.wikipedia.org/wiki/Pablo_Picasso" hreflang="en">Pablo Picasso</a></i></center>
<p>(Joke :jdicajdirien: ).</p>
<p>So for the first time fxguide seems to report wise words about deep data in animated feature and it came from Hank Driskill, Technical Supervisor on Big Hero 6 (next Disney animated feature). If you are an animated feature supervisor, please consider this:</p>
<blockquote><p>The renderer and pipeline can render with deep data. It was not used on all shots, however. “We use it sparingly just because of the data management concerns,” says Driskill, referring to the huge data sets complex deep data rendering can produce.</p></blockquote>
<p>No kidding... Only Weta would have created a such heavy, massive and crazy stuff that deep data file format is. But the reality is you are often not at Weta (are they really rendering beauties with it?) and you are not working with the same budgets.</p>
<p>Notice the fxguide's "It was not used on all shots" following by contrasted Driskill's quote: "We use it sparingly". They both mean the same thing, just taking the problem by two different angle.</p>
<p>There is a huge gap between what fxguide reports and the artists work, RnD/production relation and the hard time you can have to deliver on time. There is a reason behind this: CGI is very shy when time to talk came. You can't blame that, that's the way things work in any big company. But you have to be aware of that when you read such informations: fxguide, and implicitly, you, don't have the whole story.</p>
<p>This brings fxguide to often have a big abstraction of how things actually run in a studio and how nuanced the stated reality can be (but which supervisor would tell: "This was a mess!" and rant over their in house tools?). This have to be kept in mind when you choose a technical solution based on what fxguide states, specially if you have limited experience on animated features that involve a huge data consistency management.</p>
<p>This might be obvious for a lot of peoples but as I've seen many supervisors taking fxguide as a reference to choose a technology based on <em>according to the industry</em>, I'm taking this opportunity to call for vigilance: You are artists, take the tool you known and move on!</p>
<p>For fun:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2014_10_18_fx_guide/fxguide_batman_robin.jpg" alt="fxguide_batman_robin.jpg" style="display:block; margin:0 auto;" title="fxguide_batman_robin.jpg, oct. 2014" height="310" width="320" /></p>
<p>You will get more chances to deliver a show using a shitty tech you known than a high end tech you have no idea how to use and what it would imply. This has to be considered when you work with limited resources. :redface:</p>
<p>Hope this help. You're welcome to comment, specially if you don't agree with me! :)</p>
<p>Dorian</p>
<center>:marioCours:</center>
<p>If any fxguide staff member read this post: Don't take any offense, I'm just trying to show that some people can be very strongly influenced and make mistakes because your articles won't always covers the downside. :)</p>Multithreading for Visual Effects book reviewurn:md5:bfffa7c4e38ece8de4f0ef6fccb0caee2014-08-24T19:14:00+02:002014-10-09T14:08:02+02:00NarannMes coups de coeurbookenhoudinilibeemultithreadingopenclopensubdivopenvdbprestoreviewtbbvfx<p><img src="https://www.fevrierdorian.com/blog/public/billets/2014_04_24_multithreading_for_vfx_review/multithreading_for_vfx_review_tn.png" alt="multithreading_for_vfx_review_tn.png" style="float:left; margin: 0 1em 1em 0;" title="multithreading_for_vfx_review_tn.png, août 2014" height="150" width="150" />In programming, nothing is better than a good book. You will often ear that from many experienced programmers.</p>
<p>Today I want to share my humble review of <a href="http://www.crcpress.com/product/isbn/9781482243567" hreflang="en">Multithreading for Visual Effects</a>.</p>
<p>This book has been created after the <a href="http://s2013.siggraph.org/attendees/courses/events/multithreading-and-vfx" hreflang="en">Multithreading and VFX courses session</a> at Siggraph 2013 where some companies were presenting how they used multithreading in their tools. The results where so interesting they decide to gather them and make a book.</p>
<p>As this kind of <em>niche</em> book is rare in the CGI, let's talk about it! :enerve:</p> <p><a href="https://www.fevrierdorian.com/blog/public/billets/2014_04_24_multithreading_for_vfx_review/multithreading_for_vfx_review_001.jpg" title="multithreading_for_vfx_review_001.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2014_04_24_multithreading_for_vfx_review/.multithreading_for_vfx_review_001_m.jpg" alt="multithreading_for_vfx_review_001.jpg" style="display:block; margin:0 auto;" title="multithreading_for_vfx_review_001.jpg, août 2014" height="560" width="420" /></a></p>
<h3>Chapter 1: Introduction and Overview (by James Reinders)</h3>
<p>The prologue is an ode to multithreading (What a surprise :siffle: ). James Reinders is an engineer from Intel and we can say he know his job!</p>
<p>What I'd really liked is that multiple faces of multithreading are presented (multi task vs multi thread, vectorization, etc...), explaining technically how they work and giving pros and cons for each of them.</p>
<p>I must talk about how <a href="https://en.wikipedia.org/wiki/Threading_Building_Blocks" hreflang="en">TBB</a> is presented. In general TBB is considered as a serious choice for multithreading programs (Almost every program in the book use it). And from what I've read from the documentation, it deserves it. To be honest, TBB make we want to master C++ templates and I totally understand you would choose TBB if you are familiar with them.</p>
<p>But I regret James didn't spend more time and love presenting alternatives. I know it's a Intel guy but clearly, TBB receive a lot of emphasis and it totally hides the others. I think it's quiet a shame because a lot of chapters talk about TBB too. Alternatives are "presented" but not as in depth than TBB.</p>
<p>Anyway, you get the message: TBB is good, eat some! :banaeyouhou:</p>
<h3>Chapter 2: Houdini: Multithreading Existing Software (by Jeff Lait)</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/logos/.houdini_black_s.png" alt="houdini_black.png" style="display:block; margin:0 auto;" title="houdini_black.png, sept. 2014" height="56" width="240" /></p>
<p>A lot of multithreading informations you can find here and there, even on books (including this one), are very academic. They explain how you <em>should</em> write code.</p>
<p>This chapter is interesting because it take the invert approach: How convert old code to modern multithreading. Jeff present different concrete problems and how they choosed to solve them, what they lost doing so and how, if it was possible, they can still save what can be.</p>
<p>What I really love in this chapter is how unacademic it is. Some example's code solution make you realize: "Yeah! I could do that... It's weird but it works pretty well!". The best examples are how Houdini handles the thread creation time (because create a thread is long actually) where the datas to compute doesn't worth the threads creation time. Easy trade of: If data_count < 5000 : compute in the main thread! :IFuckTheWorld:</p>
<p>You will never see this kind of solution on an academic book but you will never have more efficient that this! :D</p>
<p>Each case is interesting. While not relevant for a general experience, read them all train your brain in <em>why</em> you should avoid to do things like this and how to react when you see them.</p>
<p>I must confess I'd read <a href="http://www.multithreadingandvfx.org/course_notes/MultithreadingHoudini.pdf" hreflang="en">the original Side Effect paper</a> after the Siggraph 2013. This make me realize some work has been done to reorganize and better explain the examples. I remember I were pretty lost with some examples in the original paper but it was not a problem with the book so I guess this examples where better explained (or my general programming skills has increase from that time).</p>
<p>This chapter was frustrating for me because I wanted to know more! More examples, more tricky problems to solve, more "here boys is what you've got if you write code this way".</p>
<p>If you want to know more about how Houdini works under the hood you can find this <a href="http://forums.odforce.net/topic/17105-short-and-sweet-op-centric-lessons/#entry104263" hreflang="en">very interesting post</a></p>
<h3>Chapter 3: The Presto Execution System: Designing for Multithreading (by George ElKoura)</h3>
<p>Interactivity is the key of this chapter.</p>
<p>The first part is not very interesting. George explain the technical goals and constrains they had. This is good, but he spend too much time on things you often know without explain what where the specific implication in Presto ("vectorization is important because blah blah", "you should organized you data because blah blah"). Its kind of redundant with the first chapter. I would have put every general multithreading stuff explained in the first chapter and let the study case focus on implementation.</p>
<p>However, the second part is very interesting. Here are the smart choices they had to do. The way they handle threads in Presto (especially how graph computation is separate) is very different than an "academic" graph network. Indeed, Presto had to deal with "time" because time is the animator toy. He also talk about interactivity and the relation betweens "batch" threads (responsive of math computation) and UI threads and how they tried to avoid any kind of lag in interactivity (interrupt or not interrupt, that is the question). It's very well explained while not enough detailed for my taste. They finish with a small (2 pages) concrete code example of how a "naive" geometric deformer code can be multithreaded. It's an interesting code snippet because it present a general problem (but he give a "general" solution).</p>
<p>If you want to have an <em>overview</em> of how Presto work under the hood, you will like this chapter. The word <em>overview</em> is a good word here because George only focus on multithreading (it's what this book is supposed to do so...) but he often talk about things implicitly that (I guess) you could understand if you were familiar with Presto (what a rig look like in Presto?!). This "abstraction" of some technical details can be frustrating. :injures:</p>
<p>I also felt a "everything were fine and rosy" like if there was no complications, no multithreading specific "problems", no "what a silly idea we had there". I mean, in development, there is always moments where the vision and abstracted concepts face reality and involve serious trade of, moments where the academic approach can't be applied <em>as it</em>. This is even more glaring when you compare to the following chapter.</p>
<h3>Chapter 4: LibEE: Parallel Evaluation of Character Rigs (by Martin Watt)</h3>
<p>This one surprise me a lot! It shine in many places!</p>
<p>From the first pages of the chapter I was not very excited. LibEE is presented as a multithreaded Dependency Graph... You could almost read "Maya DG is not relevant anymore so there was no alternative: We have to write our own". I'm not a big fan of the classic "Dependency" Graph pattern. I'm not sure it's a good approach for multithreading and more I was reading the chapter more it confirm my humble opinion (but you know, I'm not working at Dreamwork so there is maybe a lot of valid reasons...). While the Dreamwork team seems to have done a big job to bypass some of the limitations a Dependency Graph could have, many sections make me realize "why" DG is not straightforward for massive multithreading. I tend to prefer a compiled approach like <a href="http://www.sidefx.com/docs/houdini13.0/vex/" hreflang="en">VEX</a> or what Presto team choosed but reading the rest of the chapter, I'm not sure the Dreamwork RnD team has all the needed discretion (and resources?) to choose a such approach.</p>
<p>And this is one point where this chapter is amazing: You "feel" the production. I mean, the deadlines, riggers ranting over the new tools <em>because-it-was-better-to-use-the-old-shitty-Maya-DG-before</em>. I'm a big fan of various few line examples of discussions betweens various peoples involved (copy of rigger mails make me ROFL). It also explained how they have to <em>train</em> riggers to <em>think</em> multithreaded. The various bench tools they provided to riggers to find where the <em>critical paths</em> were and when "cut" them (yet another point to stay away from DG IMHO). LibEE follow the typical VFX situation where tools are developed and used at the same time (unlike Presto team that seems to have worked far away the production before release even if this is not clearly stated). Handle such situation is an art from a development perspective as goals often change and this chapter explain (a little) how handle this.</p>
<p>The second impressive point is the experience they share with the tests they did in production. I was sad the Presto chapter where only mentioning few technical points to be aware of in general (NUMA, bandwith, Hyperthreading, etc...) without provide any graph or so, while the Dreamworks team not only explain, but show <em>where</em> this points are problematics, how they bench them and what they observed. And they became very far at this point. Once again, this is not academic, this is true use cases decorticates.</p>
<p>So a very cool and clear chapter! One of the most valuable of this book. I would have love the same level of in depth discussion with Presto.</p>
<h3>Chapter 5: Fluids: Simulation on the CPU (by Ronald D. Henderson)</h3>
<p>lol this one is massive. If you love code, maths and computer science, you would love it! :aupoil:</p>
<p>There is some interesting OpenMP/TBB comparison codes (with TBB C++11 lambdas, first time I read such! :gne2: ). If you are not comfortable with C++, that's gonna be hard (and kind of disrupting).</p>
<p>About maths, I must admit I've smiled a lot each time I realize "I don't get anything! :baffed: " after read some lines.</p>
<p>In others chapters of the book, a lot of explanation is often presented with sentences like: "We measure the core efficiency by dividing the bench time by the number of core" (you know, something humanly readable). In this chapter, such sentence are actually presented by a tiny mathematical formulation following by: "Where <em>p</em> is the relational infrastructured derived of the inversed logarithm's result of the number of core and <em>n</em> is the time of the benchmark expressed in second (ISU-CIPM 1967)". Yeah, I exaggerate of course but as I'm not used to mathematical formulations this is exactly the feel I had reading this kind of sentences for the first time. And I laugh even more when, after some digging minutes, I realize all of this could be said with a simple sentence. Haha! :smileFou:</p>
<p>But I had the opportunity to work with mathematicians (or peoples loving maths in general) and they tend to use math equations to model anything (even such trivial things than the average time peoples spend at lunch or to be late at a meeting) so it reminded me all this funny moments. :jdicajdirien:</p>
<p>I'd also really loved the quick <a href="http://www.openvdb.org/" hreflang="en">OpenVDB</a> presentation. I had often heard about OpenVDB and never know how it was working under the hood. This tiny presentation was perfect. There is a lot of widely open source libraries used in the CGI but they lack of easy-to-get presentations and there is a lot of CG artists, while not true developers, are very technical and would benefit of advanced presentations of such libraries in their day to day work. So VFX artists, this presentation is for you!</p>
<p>For the rest, it was too <em>mathematics</em> for me. If you find it hard to read mathematics equation you would be lost in this chapter. I did so I can't really comment how valuable it is. Actually, I think you could be right with it if you are familiar with all "fluid/liquid" simulation maths.</p>
<h3>Chapter 6: Bullet Physics: Simulation with OpenCL (by Erwin Coumans).</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/logos/.Bullet_Physics_Logo.svg_s.png" alt="Bullet_Physics_Logo.svg.png" style="display:block; margin:0 auto;" title="Bullet_Physics_Logo.svg.png, sept. 2014" height="90" width="240" /></p>
<p>What a cool chapter I was not expecting. :bravo:</p>
<p>I'm not so much interested by physics in general but I must admit Erwin did a great work to decorticate his nice library. It's also the only chapter talking about <a href="http://en.wikipedia.org/wiki/OpenCL" hreflang="en">OpenCL</a> and GPU low level computation. While it's a very fast overview, it's well described and every steps of the various collision detection/repulsion computation of the Bullet libs that have been parallelized are well highlighted. This give a very interesting overview of how a physic engine can work which is something pleasant to know in VFX.</p>
<p>There is also a lot of tiny and very clear schema. I tend to prefer a lot of tiny, one problem focused, schema than bigger ones, you can see in the Presto eating places where some other schemas could have been interesting.</p>
<p>It finished on more advanced principles.</p>
<p>One of the clearest and pleasant chapter to read. :marioCours:</p>
<h3>Chapter 7: OpenSubdiv: Interoperating GPU Compute and Drawing (by Manuel Kraemer)</h3>
<p>I was waiting for this one impatiently and I've not been disappointed, very cool chapter. It's composed of two parts.</p>
<p>The first part talk about the iterative Catmull Clark subdivision, its history, why a "universal-matching" subdivision is needed, how it was working and how they multithread it. If you are not familiar with geometry <a href="http://www.flipcode.com/archives/The_Half-Edge_Data_Structure.shtml" hreflang="en">hard edge data structure</a> you will certainly be lost. They also give some shy benchmarks and conclude iterative subdivision design, while a need for off-line rendering, does not fit well to realtime constraints. I've been surprised they didn't talk about how they organize the memory to improve cache and benefit vectorization. I'm not an expert but after have read the previous chapters, I wonder if there was not other things to do to improve performances. I guess they are just not presented because the benefit where not such interesting who know... But to have distantly followed OpenSubdiv development I have the impression they quickly jump to the GPU side as it was maybe a more important step for the studio (Presto seems to rely on it), leaving the CPU code as "good enough".</p>
<p>The second part talk about how OpenSubdiv use GPU (and OpenGL) to offer realtime subdivision using realtime tessellation shaders available with OpenGL. Once again, I enjoyed the GPU presentation and how you are supposed to organize your batchs to benefit from it (and deal with strong GPU limitation). Edge Crease (a pivotal point of OpenSubdiv) seems to have been a problem (so does the triangle/quad/polygon) but I really like how they handle it: They separate each set of the geometry to apply different algorithms. The separation process is done once as the topology doesn't change, and the GPU simply recompute the tessellation shader according to the face set. Interesting and pragmatic approach to apply the most efficient algorithm to various part of a single geometry (while I guess it should complicate the code).</p>
<p>Off topic, I wonder if creases were a good idea for an open subdiv library. It fit very well with the <em>surface limit</em> concept dear to Renderman but:</p>
<ul>
<li>This concept doesn't seems to fit well with raytracers that need true geometry to raycast.</li>
<li>It seems that you need high subdivision levels to benefits of strong (but still smooth) crease edges were a simple bevel or double edge could do the job (it certainly make the geometry more complex indeed).</li>
<li>It seems to complicate and fragment the subdivision code.</li>
<li>Fragmented code? Fragmented data structure? What impact it could have on memory (efficient access and size)?</li>
</ul>
<p>If you can't subdivide for hell, a last option could be to represent creases modifying the normal but you would have to deal with shading stuff and this is not what OpenSubdiv is supposed to do (beside all the troubles you could have to "merge" the other shading normals).</p>
<p>So I wonder if creases where not something Pixar (and mainly Pixar) absolutely needs and this would explain while a such "exotic" concept fall in OpenSubdiv.</p>
<p>Notice the <a href="http://www.fxguide.com/quicktakes/opensubdiv-3-0-coming/" hreflang="en">next OpenSubdiv is coming</a> and seems to have some other studios contributions. It's a good thing as it seems to became a strong part of Maya. :)</p>
<h3>Conclusion</h3>
<p>Well... Good book! :D</p>
<p>There is inconsistencies between chapters. Some are very deep (LibEE, Fluids, Bullets), and others just touch the surface and finish too soon (Houdini, Presto).</p>
<p>So this book is exactly what it states: Multithreading for Visual Effects. And it focus on how multithreading can be/is used in VFX. But as the chosen softwares are very interesting (Seriously, Side Effect, Pixar, Dreamworks!) it can be sometime frustrating!</p>
<p>The book is very nice (hard cover!) while a bit pricey (hard cover?). But it's about a specific domain so it's ok (is there any book that talk about such softwares?). Most of the time it will be your studio that will buy it so... :P</p>
<p>Does it worth it? If you have to code on VFX softwares, don't hesitate. You will not find massive "ready to use" resources but it's like if you had talks with this guys sharing their experience, they explain how their babies works, what is important and what is a mess. Not sure you can find such infos everywhere.</p>
<p>If you are an artists enjoying technical stuff, I wouldn't recommend this book. It's very focus on development, you will not find multithreading tricks that will enhance your work. Anyway, I encourage to prick it to your RnD and see by yourself! :P</p>
<p>After reading this book, I'm sure I could pay for books (even with soft cover!) that focus on in depth architecture of a specific VFX software from a dev point of view (Houdini, Presto, Maya, Nuke). How datas are managed, what data structures look like, how a specific kind of operations are done etc... And not just an overview!</p>
<p>From the time I've been on the artistic side, I've tried to understood how softwares where technically working (because most of the time they were working bad/not as expected/I DIDN'T SAVE MY WORK!!!! etc...). I'm now able to understand the "logic" part of them so I want to read such high end code more!</p>
<p>Unfortunately, I missed a lot of valuable informations because of my lack of mathematic skills. I'm more familliar with computer science in general. I mean, I know what 4x4 matrices are, what they are used to, I know what a dot/cross product is and how it can be used to achieve various stuff but thats pretty much all. I was angry against myself to not be able to read and understand some advanced part of the code (I skip a lot of the Fluids chapter. :( ).</p>
<p>What is also interesting is to gather interesting Siggraph courses to make a book. I really like the idea while I would suggest to favor courses that go more in depth or with a lot of implementation studies (Once again, Presto is very frustrating). I also tend to prefer books than pdf.</p>
<p>I don't know about the success of this book but I've heard many peoples interested in it so we could hope for... Let say... "Alembic/OpenVDB/OpenEXR code review and use cases" with a lot of in depth explanation of the foundation and infrastructure of each of this libs. :D</p>
<p>Hope you enjoyed this humble review! I would be very interesting to ear your opinion if you also read this book so feel free to comment. :)</p>
<p>Dorian</p>
<center>:marioCours:</center>
Mental ray for Maya: Decrease Final Gather flickingurn:md5:0a9ec9489fde5e36a8bae4182f717d0d2013-07-20T17:30:00+02:002013-07-27T22:50:24+02:00NarannInfographie 3D - Boulotenfinal gatherflickingmayamental ray<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_tn.png" alt="fg_diminuer_flicking_tn.png" style="float:left; margin: 0 1em 1em 0;" title="fg_diminuer_flicking_tn.png, juil. 2013" height="150" width="150" />In this ticket, I present a <del>simple way</del> hack (keep in mind we are talking about mental ray for Maya :baffed: ) to drastically decrease Final Gather flicking.</p>
<p>You will see this method is a little tricky but it simply aims to reproduce the <a href="http://www.spot3d.com/vray/help/maya/150R1/render_params_advancedimap.htm#basic" hreflang="en">Interp. samples</a> Vray's option behavior.</p> <h3>Foreword</h3>
<p>Before start, know that it's truly Vray which prompted me to put my nose in mental ray, especially Final Gather map merging. While everything is very simple in Vray, in mental ray, it's a pain (as usual :septic: ).</p>
<p>Basically you will have to inject scripts in the <em>Pre render frame MEL</em> and <em>Post render frame MEL</em> to recreate the behavior of Vray.</p>
<p>Mental ray actually provide a way to merge Final Gather maps, but Maya integration doesn't do it temporally (no Final Gather map with frame number name).</p>
<h3>The scene</h3>
<p>I took the typical kind of scene that brings Final Gather to its knees (and most GI caching methods in general):</p>
<ul>
<li>No lights (<em>Default Light</em> disabled).</li>
<li>A white corridor.</li>
<li>From the invisible-to-the-camera side: A plane with a white emissive material.</li>
<li>At the crossroads: Animated colorful spheres and cubes.</li>
<li>And from the other side, our camera.</li>
</ul>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_001.png" title="fg_diminuer_flicking_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/.fg_diminuer_flicking_001_m.jpg" alt="fg_diminuer_flicking_001.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_001.png, juil. 2013" height="470" width="560" /></a></p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_003.png" title="fg_diminuer_flicking_003.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/.fg_diminuer_flicking_003_m.jpg" alt="fg_diminuer_flicking_003.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_003.png, juil. 2013" height="420" width="560" /></a></p>
<center>Keep in mind: This is an extreme case. :papi:</center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_002.png" alt="fg_diminuer_flicking_002.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_002.png, juil. 2013" height="408" width="405" /></p>
<p>The Final Gather diffuse bounces number is high to increase visual artectacts. Everything is here to have something awful. :aupoil:</p>
<h3>Final Gather without interpolation</h3>
<p>There is a render of the scene with Final Gather generated for each frame, without interpolation (Mode <em>Automatic</em>):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_anim_001.gif" alt="fg_anim_001.gif" style="display:block; margin:0 auto;" title="fg_anim_001.gif, juil. 2013" height="240" width="320" /></p>
<p>As you can see, there is a lot of flicking. :ideenoire:</p>
<p>How to solve this? <a href="http://docs.autodesk.com/MENTALRAY/2014/ENU/mental-ray-help/files/manual/fg_copy.html" hreflang="en">fg_copy</a> is your friend! :dentcasse: (Specially the <em>-f</em> flag).</p>
<p>Well, acutally, there is already a way to give Final Gather maps to Maya to make them merged before render. This way will be the foundation to our "temporal interpolation".</p>
<h3>Interpolate frames?</h3>
<p>Basically, the idea is simply to "mix" several Final Gather maps together to mitigate the effect of flicking.</p>
<p>For a frame <em>n</em>, we merge Final Gather maps <em>n</em>-2, <em>n</em>-1, <em>n</em>, <em>n</em>+1, and <em>n</em>+2 (two before, two after).</p>
<p>In practice, mental ray will merge Final Gather points with similar normal using <em>Min Radius</em> of the scene. Two Final Gather points having a similar normal (I suppose the <em>Normal Tolerance</em> of the <em>Final Gather Quality</em> section is used to control that) and where the distance doesn't execeed the <em>Min Radius</em> of the scene (if set to zero is 10% of the whole scene bouding box) have their color and position merged.</p>
<p>You can see the scene size mental ray is rendering using the log:</p>
<pre>
RC 0.2 41 MB info : scene extent: (-12.34,-0.45,-17.07) : (15.83,10.96,12.07)
</pre>
<p>This approach is far from perfect and you will see it doesn't solve all problems. The <em>ghosting</em> <a name="ghosting"></a> you can have, better than a by frame flicking though, is still unsightly, particularly on renders with few texture/color variations (diffuse surfaces). That said, this effect may go unnoticed on some sequences (especially camera movements), it really depends on what you've got.</p>
<h3>Bake Final Gather</h3>
<p>To be able to merge Final Gather maps before render (5 maps in our case) you must, as a first step, generate all of them. And this is what we will do. :enerve:</p>
<p>As we have a range of 101-110 and we will generate two frame before and two frame after, the final Final Gather maps sequence will be 99-112.</p>
<p>Personnally, I use <em>RenderLayers</em> overriding <em>Pre render frame MEL</em> and <em>Post render frame MEL</em> scripts to generate fg map names using the current frame (eg: <em>fgmap.0012.fgmap</em>) just before render start.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_004.png" alt="fg_diminuer_flicking_004.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_004.png, juil. 2013" height="200" width="374" /></p>
<p>For example, I override the <em>RenderLayer</em> that will be used to generate fg maps with this:</p>
<pre class="mel mel">python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.updateFgFiles()"</span><span style="color: #009900;">)</span>
python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.cleanFgParams()"</span><span style="color: #009900;">)</span></pre>
<p>Ok, that's not really sexy but as you know, <em>Pre render frame MEL</em> and <em>Post render frame MEL</em> only execute... MEL... So we ask MEL to execute Python! :baffed:</p>
<p>In a "readeable" mode, the two calls are followings:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> preframe
<span style="color: #008000;">reload</span><span style="color: black;">(</span>preframe<span style="color: black;">)</span>
preframe.<span style="color: black;">updateFgFiles</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>And:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> preframe
<span style="color: #008000;">reload</span><span style="color: black;">(</span>preframe<span style="color: black;">)</span>
preframe.<span style="color: black;">cleanFgParams</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Of course, there is a little <em>preframe.py</em> in your: <em>maya\scripts</em>.</p>
<h4>The script</h4>
<p>Here is the contents of <em>preframe.py</em>, contents that I will explain, function by function.</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</span>
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">def</span> cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> updateFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
fgmapFile = <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># "fgmap.0012.fgmap"</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"Set fgmap file for frame %s -> %s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>frame, fgmapFile<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">1</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Rebuild On</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, fgmapFile, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> prepareFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">2</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Freeze</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>You can copy this in a file in <em>maya/scripts/preframe.py</em>.</p>
<h5>cleanFgParams()</h5>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[4]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[5]"</span>, <span style="color: #483d8b;">""</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>This function clears the Final Gather <em>file</em> attributes (put an empty string). It's called just before setting the name of the Final Gather maps to save, and just after render, in <em>Post render frame</em>, to be sure to clear text fields.</p>
<h5>updateFgFiles()</h5>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> updateFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
fgmapFile = <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># "fgmap.0012.fgmap"</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"Set fgmap file for frame %s -> %s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>frame, fgmapFile<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">1</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Rebuild On</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, fgmapFile, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>This function is set in the <em>Pre render frame</em> attribute of the <em>RenderLayer</em> and is used to give a correct name to the Final Gather map to save. It gets the current frame and generates a fg map name with the frame number.</p>
<p>The little "hack":</p>
<pre class="python python"><span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span></pre>
<p>Used to generate the frame number with padding. Converting it to string first then apply a <em>zero fill</em> of 4, to make "10" to "0010" for example.</p>
<h5>prepareFgFiles() <a name="prepareFgFiles"></a></h5>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> prepareFgFiles<span style="color: black;">(</span><span style="color: black;">)</span>:
cleanFgParams<span style="color: black;">(</span><span style="color: black;">)</span>
frame = <span style="color: #008000;">int</span><span style="color: black;">(</span><span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span> query=<span style="color: #008000;">True</span> <span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherRebuild"</span>, <span style="color: #ff4500;">2</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Freeze</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherFilename"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[0]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame-<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[1]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame<span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[2]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span><span style="color: #483d8b;">"miDefaultOptions.finalGatherMergeFiles[3]"</span>, <span style="color: #483d8b;">"fgmap.%s.fgmap"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>frame+<span style="color: #ff4500;">2</span><span style="color: black;">)</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span>, <span style="color: #008000;">type</span>=<span style="color: #483d8b;">"string"</span><span style="color: black;">)</span></pre>
<p>And the last function, in <em>Pre render frame</em> of the <em>RenderLayer</em>, used to fill every text fields with Final Gather maps to merge during final render. This is the same method we used to generate Final Gather map name but we generate two before and two after.</p>
<p>This is where you put Final Gather mode to <em>Freeze</em> to ensure you will merge your files before using them as such.</p>
<h3>Render layers</h3>
<p>There is a little description of my <em>RenderLayers</em>. :)</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_005.png" alt="fg_diminuer_flicking_005.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_005.png, juil. 2013" height="183" width="419" /></p>
<p>Don't worry about FG_RAW, it's only used to render in <em>Automatic</em> mode.</p>
<h4>masterLayer</h4>
<p>The main <em>RenderLayer</em> is prepared in order to get <em>Secondary FinalGather Maps</em>.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_009.png" alt="fg_diminuer_flicking_009.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_009.png, juil. 2013" height="289" width="456" /></p>
<p>Note that, as all Maya's multi-attributes, if this values are empty, blocks are removed while reopening the scene. That's why I put the <em>temp</em> words in it. In any case, I remove this values just before frame render start (using the <em>cleanFgParams()</em> function). That's only to prevent Maya to remove entries (Workaround time!).</p>
<blockquote><p>Important: Each entry has a <em>temp</em> with a different number just so I can render the FG_RAW <em>RenderLayer</em> avoiding mental ray to try to merge a fg map it is actually generating.</p></blockquote>
<p>But there shouldn't ever be any Final Gather map named "temp" in your fg map folder. If there is, something has gone wrong during our process. :zinzin:</p>
<h4>FGMAPONLY</h4>
<p>This <em>RenderLayer</em> will be the first started. It's the one that will generate Final Gather maps (with two frames before and two frames after).</p>
<p><em>Pre render frame MEL</em> and <em>Post render frame MEL</em> to override (using <a href="http://download.autodesk.com/global/docs/maya2014/en_us/index.html?url=files/Vari_Work_with_attribute_overrides.htm,topicNumber=d30e650035" hreflang="en">Create Layer Override</a>) are followings:</p>
<pre class="mel mel">python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.updateFgFiles()"</span><span style="color: #009900;">)</span>
python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.cleanFgParams()"</span><span style="color: #009900;">)</span></pre>
<p>Don't forget to put this <em>RenderLayer</em> in <em>Multiframe</em> (<em>Optimize for Animation</em> in Maya). But the attribute can't be override from the <em>Render Settings</em>. :trollface:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_006.png" alt="fg_diminuer_flicking_006.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_006.png, juil. 2013" height="201" width="450" /></p>
<p>You must go through the <em>miDefaultOptions</em> node you can select typing this MEL command:</p>
<pre class="mel mel">select miDefaultOptions</pre>
<p>And looking for <em>Final Gather Mode</em> in <em>Extra Attributes</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_007.png" alt="fg_diminuer_flicking_007.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_007.png, juil. 2013" height="116" width="306" /></p>
<p>Easy as 1-2-3... :mayaProf:</p>
<p>As there is no interest to keep the image rendered during this process, you can override <em>Render Mode</em> to make mental ray compute only Final Gather:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_005a.png" alt="fg_diminuer_flicking_005a.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_005a.png, juil. 2013" height="83" width="420" /></p>
<h4>RENDERONLY</h4>
<p>It's in this <em>RenderLayer</em> the "real" render will be done. The idea is to set the differents fg maps (<em>n</em>-2, <em>n</em>-1, <em>n</em>, <em>n</em>+1 et <em>n</em>+2) to make mental ray merging them in <em>Freeze</em> (no rebuild), and compute the final render.</p>
<pre class="mel mel">python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.prepareFgFiles()"</span><span style="color: #009900;">)</span>
python<span style="color: #009900;">(</span><span style="color: #ff0000;">"import preframe<span style="color: #000099; font-weight: bold;">\n</span>reload(preframe)<span style="color: #000099; font-weight: bold;">\n</span>preframe.cleanFgParams()"</span><span style="color: #009900;">)</span></pre>
<p>I encourage you to read the <a href="https://www.fevrierdorian.com/blog/post/2013/07/20/Mental-ray-for-Maya-Decrease-Final-Gather-flicking#prepareFgFiles">prepareFgFiles()</a> function to fully understand what we do.</p>
<h3>Command line</h3>
<p>And it's time to start rendering! :popcorn:</p>
<p>I give you my two batch command line to help you:</p>
<pre>
"C:\Program Files\Autodesk\Maya2013\bin\render.exe" -mr:v 4 -mr:rt 4 -cam camera1 -rl FGMAPONLY -s 99 -e 126 -preFrame "python(\"import preframe\nreload(preframe)\npreframe.updateFgFiles()\")" -postFrame "python(\"import preframe\nreload(preframe)\npreframe.cleanFgParams()\")" -proj "D:\3D\VracProject" "D:\3D\VracProject\scenes\tuto_flick_mr_maponly.ma"
"C:\Program Files\Autodesk\Maya2013\bin\render.exe" -mr:v 4 -mr:rt 4 -cam camera1 -rl RENDERONLY -preFrame "python(\"import preframe\nreload(preframe)\npreframe.prepareFgFilesNoAnim()\")" -postFrame "python(\"import preframe\nreload(preframe)\npreframe.cleanFgParams()\")" -proj "D:\3D\VracProject" "D:\3D\VracProject\scenes\tuto_flick_mr_maponly.ma"
</pre>
<blockquote><p>Notice: I've personally encounter few problems with <em>Pre render frame MEL</em> and <em>Post render frame MEL</em> overrides. That's the reason why I "hard" write them in the commande line.</p></blockquote>
<h4>Generating Final Gather maps (first line)</h4>
<p>In the end of the first line execution, you should have the folder <em>renderData/mentalray/finalgMap</em> with something like:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_diminuer_flicking_008.png" alt="fg_diminuer_flicking_008.png" style="display:block; margin:0 auto;" title="fg_diminuer_flicking_008.png, juil. 2013" height="418" width="351" /></p>
<h4>Second line, render!</h4>
<p>Aaaaaaaand:</p>
<p>Before:
<img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_anim_001.gif" alt="fg_anim_001.gif" style="display:block; margin:0 auto;" title="fg_anim_001.gif, juil. 2013" height="240" width="320" /></p>
<p>After:
<img src="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/fg_anim_002.gif" alt="fg_anim_002.gif" style="display:block; margin:0 auto;" title="fg_anim_002.gif, juil. 2013" height="240" width="320" /></p>
<center>FG Merge. Because I'm worth it. :smileFou:</center>
<p>Ok, I admit gif is not the best thing to assess the image quality but you will notice it's a lot more stable. You will notice too, on the right wall, the ghosting effect <a href="https://www.fevrierdorian.com/blog/post/2013/07/20/Mental-ray-for-Maya-Decrease-Final-Gather-flicking#ghosting">I was talking about</a>.</p>
<p>Once again, it's an extreme case:</p>
<ul>
<li>No direct lighting.</li>
<li>An emissive surface.</li>
<li>A corridor.</li>
<li>High number of bouces.</li>
<li>Low Final Gather quality parameters.</li>
</ul>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_07_20_Mental_ray_final_gather_diminuer_flicking/tuto_flick_mr_maponly.7z">There is the scene</a> so you can see parameters and try all of this yourself.</p>
<p>Important: Keep an eye on you logs because (and I don't think it's "good" :pasClasse: ) <em>Freeze</em> mode is not as stupid it seems to be (<a href="http://docs.autodesk.com/MENTALRAY/2014/ENU/mental-ray-help/files/manual/options.html#finalgathering" hreflang="en">see the doc</a>). If a Final Gather map don't exists, it create it anyway. You will so have the impression, when the two batch lines will ends, that everything passed well (Final Gather maps are there and images so) but everything will flick...</p>
<p>Few informations you should keep an eye on (example for the frame 123):</p>
<pre>
RC 0.2 26 MB info : option: rebuild freeze
RC 0.2 26 MB info : option: files D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0121.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0122.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0123.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0124.fgmap
RC 0.2 26 MB info : option: D:/3D/VracProject/renderData/mentalray/finalgMap/fgmap.0125.fgmap
</pre>
<pre>
RCFG 0.2 27 MB info : finalgather map is frozen, using loaded from file(s)
</pre>
<p>I encourage you, as a first step, to start these two lines separately and check everything passed well between them.</p>
<p>I insist on the fact that you will not necessary succeed the first time. There is actually a lot of things to do. So persevere and keep in mind "this is possible". :hehe:</p>
<h3>Variant</h3>
<p>As you can see, flicking of animated objects (spheres and cube) don't change too much. We can consider many variant to solve this problem.</p>
<p>The first approach is to generate two distinct Final Gather maps sequence:</p>
<ul>
<li>One only with animated objects (putting static objects in <em>primary rays</em> off) with higher Final Gather quality. Named <em>fg_anim.####.fgmap</em>.</li>
<li>The other only with static objects (putting animated objects in <em>primary rays</em> off in order to keep their diffuse bounces on the fg map). Named <em>fg_static.####.fgmap</em>.</li>
<li>And we merge everything just before render.</li>
</ul>
<p>We can also go a step up in complexity (while we're there... :pasClasse: ) and use the <a href="http://docs.autodesk.com/MENTALRAY/2014/ENU/mental-ray-help/files/manual/fg_copy.html" hreflang="en">fg_copy</a> command to merge animated object Final Gather maps with a higher radius to to get farther Final Gather points. That said, I'm not sure this approach produces better results but it has to be tested.</p>
<p>And last but not least, you can <a href="https://www.fevrierdorian.com/blog/post/2013/02/10/Combine-importons-with-Final-Gather-English-Translation">combine this with importons</a> to have better Final Gather points. :)</p>
<h3>Conclusion</h3>
<p>I was thinking it was impossible but we can actually "immitate" the <a href="http://www.spot3d.com/vray/help/maya/150R1/render_params_advancedimap.htm#basic" hreflang="en">Interp. samples</a> Vray option in mental ray. :sourit:</p>
<p>Of course, doing it this way is not necessarily easy, and it's again unfortunate having to fight to benefit a feature available using a simple slider in Vray... :redface:</p>
<p>However, I hope this post have interest you and that you've learned things. I think if you can put this kind of system in place, you solve the main concerns about Final Gather (or similar techniques) in animations.</p>
<p>Don't hesitate to feedbacks in comments if I missed some points/explaination you would like to have. :dentcasse:</p>
<p>See you guys!</p>
<p>Dorian</p>
<center>:marioCours:</center>
11 Second Club Mars 2013urn:md5:8f8c5c69c764482debe77824365b62102013-04-23T18:24:00+02:002020-07-23T08:57:31+02:00NarannInfographie 3D - Boulot11secondclubenfrlightingmayavray<iframe src="https://player.vimeo.com/video/64650552" width="640" height="360" frameborder="0" allow="autoplay; fullscreen" allowfullscreen></iframe>
<h3>Français</h3>
<p>Camille Campion a gagné le <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">11 Second Club du mois de Mars</a>! :bravo:</p>
<p>C'est moi qui me suis chargé de son lighting. C'était surtout un bon gros prétexte pour bosser le soir sur VRay et rendre de l'anim full CG. :hehe:</p>
<p>Les délais étaient tendus (un mois pour tout faire), heureusement que Camille anime rapidement (il ne le dit nulle part parce qu'il est modeste mais il a animé ça en 15 jours!). Les ressources étaient assez limitées (C'est ça... Quand on a l'habitude de bosser avec des render farms, ça fait tout drôle de revenir sur son PC maison. :baffed: ).</p>
<p>Le rigg utilisé fut <a href="http://www.animschool.com/DownloadOffer.aspx" hreflang="en">Malcom</a> (j'en profite pour dire que ce rigg voit ses assignations faites "par face" ce qui est assez gênant au moment du lighting).</p>
<p>Au niveau lighting:</p>
<ul>
<li>Une grosse area light blanche sur la vitre.</li>
<li>Une grosse area light orange dans le bar.</li>
<li>Quelques IES au plafond.</li>
</ul>
<p>Si vous avez regardé <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">la version du 11 Second Club</a>, vous remarquerez que la version de ce billet est plus "orange". En fait, Camille m'a aidé à calculer les images sur sa machine et le profil IES (peu d'utilité à utiliser une IES la dessus, j'en conviens) a été oublié. Donc les quelques lumières IES de la scène ne rendaient rien... :dtcFuckSmile:</p>
<p>Les IES étant subtiles et le temps manquant, on a continué la dessus pour la version 11 Second Club. :smileFou:</p>
<p>Merci à <a href="http://deex.info/wordpress2/" hreflang="en">Damien Bataille</a> pour ses conseils avisés.</p>
<p>Le compo à été fait sous Nuke.</p>
<h3>English</h3>
<p>Camille Campion won the <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">11 Second Club of March</a>! :bravo:</p>
<p>I've been in charge of its lighting. It was mainly a by opportunity to work with VRay and render full CG animation. :hehe:</p>
<p>Deadline was short (a month for everything). Fortunately, Camille is a very quick animator (he don't say this anywhere because it's an humble guy but he animate this in 15 days!). Hardware was also very limited (When you're used to deal with renderfarms, it's weird to go back on your home PC. :baffed: ).</p>
<p>The rigg used was <a href="http://www.animschool.com/DownloadOffer.aspx" hreflang="en">Malcom</a> (I want to say that rigg has its material assignments done "by face" which is quite annoying during lighting).</p>
<p>Lighting</p>
<ul>
<li>A big white area light on the window.</li>
<li>A big orange area light in the bar.</li>
<li>Some IES on the ceil.</li>
</ul>
<p>If you've watch <a href="http://www.11secondclub.com/competitions/march13/winner" hreflang="en">the 11 Second Club version</a>, you will notice the version of this post is more "orange". Acutally, Camille help me to render images on its computer and the IES profile (not so usefull on a such shot, I agree) has been lost. So, the few IES light was actually blacks... :dtcFuckSmile:</p>
<p>As IES effect was subtle and we don't have much time, we continue that way for the 11 Second Club version. :smileFou:</p>
<p>Thanks to <a href="http://deex.info/wordpress2/" hreflang="en">Damien Bataille</a> for its good advices.</p>
<p>Compositing has been made in Nuke.</p>Pick a color outside Maya 2011 user interface (Qt) (English Translation)urn:md5:e68c6dff650ea6ca0e998cc3bddeec532013-03-28T20:30:00+01:002013-07-26T17:58:45+02:00NarannInfographie 3D - Boulotenmayapickerqt <p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_28_color_picker/maya_color_picker_tn.png" alt="maya_color_picker_tn.png" style="float:left; margin: 0 1em 1em 0;" title="maya_color_picker_tn.png, mar. 2013" height="150" width="150" />An "ultra express" post! :marioCours:</p>
<p>The color picker has been modified since Maya 2011 and its transition to Qt and it's now impossible to pick a color outside the Maya interface.</p>
<p>Actually yes, you can! And it's quite easy to do! :hehe:</p>
<p>You just need to select the picker, click anywhere inside Maya interface, keep the left mouse button, and drag outside the interface on the color you want.</p>
<p>I must confess, <a href="http://mayafeedback.autodesk.com/forums/160518-small-annoying-things-to-fix-in-maya-forum/suggestions/2993858-allow-the-color-picker-to-operate-on-anything-visi" hreflang="en">I have not found it myself</a>. :baffed:</p>Geometry voxelization using Maya Python API (English Translation)urn:md5:92070cf095ad4a8188940b424f02707b2013-03-16T13:47:00+01:002013-07-26T17:59:10+02:00NarannScript et codeapiengeomtriemayapythonvoxel<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray_tn.png" alt="voxel_vray_tn.png" style="float:left; margin: 0 1em 1em 0;" title="voxel_vray_tn.png, mar. 2013" height="150" width="150" />Yet another little script only aimed to use the Maya API. :sauteJoie:</p>
<p>The idea is to reproduce the voxel geometric effect you may have <a href="http://www.bilderzucht.de/blog/3d-pixel-voxel/" hreflang="en">already seen</a>.</p>
<p>Nothing too serious and it probably already exists and maybe faster. To be honest, I did not even look. The goal is again to use the API. :baffed:</p> <h3>Principe</h3>
<p>Before we begin, know that this problem is a textbook case. There are a thousands of ways to solve it and each method has these pro and con. :reflechi:</p>
<p>The method chosen here is simple: For each vertex of the geometry, we find the position "in cube unit" and generates a cube.</p>
<p>The only dificulty with this implementation is, once the vertex position recovered, to know where must be the center of the cube. :seSentCon:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_vray.png" title="voxel_vray.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/.voxel_vray_m.jpg" alt="voxel_vray.png" style="display:block; margin:0 auto;" title="voxel_vray.png, mar. 2013" height="420" width="560" /></a></p>
<h3>The code</h3>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
voxelStep = <span style="color: #ff4500;">1.0</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span>
grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>pointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<h3>Explainations</h3>
<pre class="python python">sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Basically, a <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_selection_list.html,topicNumber=cpp_ref_class_m_selection_list_html" hreflang="en">MSelectionList</a> is a list of MObject.</p>
<p>I never really understood what the term "selection" meant in this context as it actually does not "select" anything. :bete:</p>
<p>The particularity of a <em>MSelectionList</em> is to retrieve the <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject</a> of a Maya node from its name, what we will do later.</p>
<pre class="python python">dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>It is always difficult to explain to begginers what is a DAG and a DAG path. :gne:</p>
<p>As a large summary:</p>
<ul>
<li>DAG: This is the Maya "hierarchy" (parent/child).</li>
<li>DAG path: This is the "path" of a node through the hierarchy (and thus, having all these transformations).</li>
</ul>
<p>Many Maya API functions require DAG path to work.</p>
<p>For example, you can't retrieve world space coordinates of shape node's vertices if you don't know the path by which you get there. Two instances have only one shape node but it is indeed two different DAG path and world space coordinates of one vertex can have two possible values depending "from where we go".</p>
<pre class="python python">sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span></pre>
<p>We add the Maya object "pSphere1" in the <em>MSelectionList</em> and we retrieve its DAG path (zero is the index in the list).</p>
<p>So we've "converted" "pSphere1" (that doesn't mean anything in Maya API) in true <em>MObject</em>.</p>
<pre class="python python">inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span></pre>
<p>By default, all what you get from the API are <em>MObjects</em>. In general, we check the <em>MObject</em> type using <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MObject.apiType()</a> or <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_dag_node.html,topicNumber=cpp_ref_class_m_fn_dag_node_html" hreflang="en">MObject.hasFn()</a>.</p>
<p>But here we assume the user provide a mesh. :siffle:</p>
<p>The <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">MFnMesh</a> class allow you to deal with a "true" programming object from which we will be able to get informations. It's a <em>function set</em>.</p>
<p>I invite you to read <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_object.html,topicNumber=cpp_ref_class_m_object_html" hreflang="en">the documentation</a> to understand the how and why of <em>function sets</em>.</p>
<p>So we have a _inMesh_ that we will use to retrieve mesh's vertices.</p>
<pre class="python python">pointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
inMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>pointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span></pre>
<p>The first line creates a <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html" hreflang="en">MPointArray</a> to store our mesh vertices positions.</p>
<p>And the second line fills the array with vertices coordinates world space (position relative to the center of the scene, not the center of the object itself).</p>
<pre class="python python">grpName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>empty=<span style="color: #008000;">True</span><span style="color: black;">)</span></pre>
<p>Here we only create an empty group that will store cubes we will create later. It is just more convenient to delete a group with everything in it than select all the cubes manually. :dentcasse:</p>
<pre class="python python">voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>We create a list which will be used to store identifiers (or keys) of areas where cubes have already been generated. I return below.</p>
<pre class="python python">cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>pointArray<span style="color: black;">[</span>i<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep</pre>
<p>And here are the small lines that does everything. You'll see, it is very simple.</p>
<p>So, what are we trying to do with this?</p>
<p>Let's suppose the desired size cubes is 2.0.</p>
<p>It comes across a vertex set with a X value of 10.2. Of course, we will not put our cube in the center of the vertex (10.2). We would not get the desired effect at all. :nannan:</p>
<p>We need the exact position of the cube. We must "count the number of cube."</p>
<p>How do I know what is the 10.2 distance in cube size 2.0? By simply: 10.2/2.0 = 5.1.</p>
<p>As we cann't have 5.1 cubes, we round using the round() function. In the case of round(5.1), we have 5 (5.7 would give 6).</p>
<p>So now we know that if we create a cube of size 2.0, you should move it 5 times its size to make it emcompass the vertex. We then multiply the rounded value (5) by the size of a cube (2) to obtain a new position: The position of the cube, not wedged on the vertex but keyed to the voxel grid.</p>
<p>And voila! Now you know! :laClasse:</p>
<p>We did this for the three axes.</p>
<pre class="python python">cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>Here, we create a "hash" (a string) of the cube position and we store it in a list. That way, if we fall on a vertex which, once rounded, is in the same places than an already existing cube, it does not create it (no duplicates! :hehe:).</p>
<p>Although method seems <del>a little</del> very odd (convert vertex positions to string), I felt it was the easiest way to manage a unknown sized grid without too many lines of code.</p>
<p>But if you have another one short and quick to implement, don't hesitate to share. : D</p>
<pre class="python python">myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpName<span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span></pre>
<p>After all of this, everything is simple:</p>
<ul>
<li>We create our cube from it desired size (2.0).</li>
<li>We applied a bevel on it because it looks good. :smileFou:</li>
<li>We parent it to the group.</li>
<li>We place it to the calculated position.</li>
</ul>
<p>And start again for another vertex!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_001.png" alt="voxel_maya_api_001.png" style="display:block; margin:0 auto;" title="voxel_maya_api_001.png, mar. 2013" height="387" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_03_16_voxel_maya_api/voxel_maya_api_002.png" alt="voxel_maya_api_002.png" style="display:block; margin:0 auto;" title="voxel_maya_api_002.png, mar. 2013" height="598" width="616" /></p>
<p>Again: This script is not optimized at all, it is a rough prototype for training purpose, not a production tool. Just give it a heavy mesh to realize. :mechantCrash:</p>
<h3>Conclusion</h3>
<p>This express ticket is over.</p>
<p>As you see, principle is quite simple. Well, once again we could have done this differently and probably more effective (try with <a href="http://download.autodesk.com/global/docs/mayasdk2012/en_us/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=a5b3f852a00f3dc096c136cbe04057733" hreflang="en">MFnMesh.allIntersections</a>).</p>
<p>Personally, playing with the Maya API always amuses me so much. :)</p>
<p>See you!</p>
<center>:marioCours:</center>
<h3>EDIT 2013/03/17</h3>
<p>I couldn't resist to try <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.allIntersections()</a>. There is a well more optimized version (with acceleration structure <a href="http://docs.autodesk.com/MAYAUL/2013/ENU/Maya-API-Documentation/index.html?url=cpp_ref/class_m_fn_mesh.html,topicNumber=cpp_ref_class_m_fn_mesh_html,hash=aa6c4717b168258664d399fb92e2cdcee" hreflang="en">MFnMesh.autoUniformGridParams()</a>).</p>
<p>The main difference between the previous one and this one is we don't go through each vertex anymore but we project rays through a grid instead (X, Y, Z).</p>
<p>The second difference is the code work on a animated mesh (1 to 24 here). You should test, the effect is cool. :)</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">cmds</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">cmds</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
startFrame = <span style="color: #ff4500;">1</span>
endFrame = <span style="color: #ff4500;">24</span>
voxelSize = <span style="color: #ff4500;">20.0</span>
voxelStep = <span style="color: #ff4500;">0.5</span>
sel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
sel.<span style="color: black;">add</span><span style="color: black;">(</span><span style="color: #483d8b;">"pSphere1"</span><span style="color: black;">)</span>
sel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span><span style="color: #ff4500;">0</span>, dagPath<span style="color: black;">)</span>
inMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
grpReelNames = <span style="color: #008000;">dict</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
grpName = <span style="color: #483d8b;">"frameGrp_%s"</span>.<span style="color: black;">zfill</span><span style="color: black;">(</span><span style="color: #ff4500;">4</span><span style="color: black;">)</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">int</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
grpReelName = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">group</span><span style="color: black;">(</span>name=grpName, empty=<span style="color: #008000;">True</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime-<span style="color: #ff4500;">0.1</span><span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">1.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setKeyframe</span><span style="color: black;">(</span>grpReelName+<span style="color: #483d8b;">".visibility"</span>, value=<span style="color: #ff4500;">0.0</span>, <span style="color: #dc143c;">time</span>=<span style="color: black;">[</span>curTime+<span style="color: #ff4500;">1</span><span style="color: black;">]</span><span style="color: black;">)</span>
grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span> = grpReelName
<span style="color: #ff7700;font-weight:bold;">for</span> grpReelName <span style="color: #ff7700;font-weight:bold;">in</span> grpReelNames :
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">cmds</span>.<span style="color: black;">objExists</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">delete</span><span style="color: black;">(</span>grpReelName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> curTime <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>startFrame, endFrame+<span style="color: #ff4500;">1</span><span style="color: black;">)</span> :
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">currentTime</span><span style="color: black;">(</span>curTime<span style="color: black;">)</span>
voxelIdList = <span style="color: #008000;">list</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#I use while just because xrange with floats is impossible</span>
i = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> i <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
j = -voxelSize/<span style="color: #ff4500;">2.0</span>
<span style="color: #ff7700;font-weight:bold;">while</span> j <span style="color: #66cc66;"><</span>= voxelSize/<span style="color: #ff4500;">2.0</span> :
<span style="color: #ff7700;font-weight:bold;">for</span> axis <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: black;">[</span><span style="color: #483d8b;">"zSide"</span>, <span style="color: #483d8b;">"ySide"</span>, <span style="color: #483d8b;">"xSide"</span><span style="color: black;">]</span> :
z = <span style="color: #ff4500;">0</span>
y = <span style="color: #ff4500;">0</span>
x = <span style="color: #ff4500;">0</span>
zOffset = <span style="color: #ff4500;">0</span>
zDir = <span style="color: #ff4500;">0</span>
yOffset = <span style="color: #ff4500;">0</span>
yDir = <span style="color: #ff4500;">0</span>
xOffset = <span style="color: #ff4500;">0</span>
xDir = <span style="color: #ff4500;">0</span>
<span style="color: #ff7700;font-weight:bold;">if</span> axis == <span style="color: #483d8b;">"zSide"</span> :
x = i
y = j
zOffset = <span style="color: #ff4500;">10000</span>
zDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"ySide"</span> :
x = i
z = j
yOffset = <span style="color: #ff4500;">10000</span>
yDir = -<span style="color: #ff4500;">1</span>
<span style="color: #ff7700;font-weight:bold;">elif</span> axis == <span style="color: #483d8b;">"xSide"</span> :
y = i
z = j
xOffset = <span style="color: #ff4500;">10000</span>
xDir = -<span style="color: #ff4500;">1</span>
raySource = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> x+xOffset, y+yOffset, z+zOffset <span style="color: black;">)</span>
rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatVector</span><span style="color: black;">(</span>xDir, yDir, zDir<span style="color: black;">)</span>
faceIds=<span style="color: #008000;">None</span>
triIds=<span style="color: #008000;">None</span>
idsSorted=<span style="color: #008000;">False</span>
space=<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span>
maxParam=<span style="color: #ff4500;">99999999</span>
testBothDirections=<span style="color: #008000;">False</span>
accelParams=inMesh.<span style="color: black;">autoUniformGridParams</span><span style="color: black;">(</span><span style="color: black;">)</span>
sortHits=<span style="color: #008000;">False</span>
hitPoints = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
hitRayParam=<span style="color: #008000;">None</span>
hitFacePtr = <span style="color: #008000;">None</span><span style="color: #808080; font-style: italic;">#OpenMaya.MScriptUtil().asIntPtr()</span>
hitTriangle=<span style="color: #008000;">None</span>
hitBary1=<span style="color: #008000;">None</span>
hitBary2=<span style="color: #008000;">None</span>
hit = inMesh.<span style="color: black;">allIntersections</span><span style="color: black;">(</span>raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
space,
maxParam,
testBothDirections,
accelParams,
sortHits,
hitPoints,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> hit :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #808080; font-style: italic;"># for each interestected points</span>
<span style="color: #ff7700;font-weight:bold;">for</span> k <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">xrange</span><span style="color: black;">(</span>hitPoints.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span> :
cubePosX = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">x</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosY = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">y</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubePosZ = <span style="color: #008000;">round</span><span style="color: black;">(</span>hitPoints<span style="color: black;">[</span>k<span style="color: black;">]</span>.<span style="color: black;">z</span>/voxelStep<span style="color: black;">)</span><span style="color: #66cc66;">*</span>voxelStep
cubeId = <span style="color: #483d8b;">"%s%s%s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span>cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdList :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span> :
voxelIdList.<span style="color: black;">append</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span>
myCube = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyCube</span><span style="color: black;">(</span>width=voxelStep, height=voxelStep, depth=voxelStep<span style="color: black;">)</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">polyBevel</span><span style="color: black;">(</span>myCube, offset=<span style="color: #ff4500;">0.02</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">parent</span><span style="color: black;">(</span>myCube, grpReelNames<span style="color: black;">[</span>curTime<span style="color: black;">]</span><span style="color: black;">)</span>
<span style="color: #dc143c;">cmds</span>.<span style="color: black;">setAttr</span><span style="color: black;">(</span>myCube+<span style="color: #483d8b;">".translate"</span>, cubePosX, cubePosY, cubePosZ<span style="color: black;">)</span>
j += voxelStep
i += voxelStep</pre>
<center><i>Sorry for the horizontal code. :dentcasse:</i></center>
<p>I don't explain this version because I think if you understood the previous one, you shouldn't have too much problems with this one. :gniarkgniark:</p>
<h3>EDIT 2013/03/19</h3>
<p>Justin Israel <a href="https://groups.google.com/d/msg/python_inside_maya/1pYjnkTB5l0/JUFgz8LSD-gJ" hreflang="en">caught my attention</a> about my <em>voxelIdList</em>. I've learn something so I share his message with you:</p>
<p>If you are using it as a lookup for the hash of previously seen items, using a list is going to be progressively slower and slower over time as the list grows, because doing "x in list" is O(n) complexity. You might want to use a set():</p>
<pre class="python python">voxelIdSet = <span style="color: #008000;">set</span><span style="color: black;">(</span><span style="color: black;">)</span>
...
<span style="color: #ff7700;font-weight:bold;">if</span> cubeId <span style="color: #ff7700;font-weight:bold;">in</span> voxelIdSet :
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
voxelIdSet.<span style="color: black;">add</span><span style="color: black;">(</span>cubeId<span style="color: black;">)</span></pre>
<p>A set is O(1) complexity, so doing "x in set" will instantly find the item by its hash, as opposed to have to scan the entire list looking for an equality match.</p>VRay User Attributes (English Translation)urn:md5:f058c64e76d2cee538cad90e272c3b632013-02-17T22:33:00+01:002014-02-25T16:24:34+01:00NarannInfographie 3D - Boulotenmayashaderuser attributesvrayvrmesh<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_tn.png" alt="user_attr_vray_tn.png" style="float:left; margin: 0 1em 1em 0;" title="user_attr_vray_tn.png, fév. 2013" height="150" width="150" />Here is a tutorial I wanted to do for quite some time. :dentcasse:</p>
<p>For a long time now, VRay can use per object attributes to control some values. It can go far but often, this values are used in shaders. The general idea is to apply a single and unique shader to a big number of object, but that each object had some "special" attributes with value in them that will be applied to the shader. If you remember, <a href="https://www.fevrierdorian.com/blog/post/2011/04/16/Mental-ray-3.9-Les-User-Data-Shaders-ou-Shader-Package">I presented (french)</a> a similar feature for mental ray (User Data).</p>
<p>But before doing that, this tutorial will explain how to export/import vrmesh then redo shader assignments directly within the vrmesh! As you can see, we have a lot to do! :bravo:</p> <h3>vrmeshs (aka proxy)</h3>
<h4>What?</h4>
<p>Briefly: You know the principle of texture tiling? (No? <a href="https://www.fevrierdorian.com/blog/post/2009/01/14/Le-mappage-en-m%C3%A9moire-des-textures-dans-Mental-Ray-expliqu%C3%A9-%28Memory-mapped-textures%29.">Hop! Hop! Hop! (french)</a> :Nannan: ) Well, principle is the same, but for geometry: Vrmesh are "ready to raycast" meshes. Geometry is stored in a grid (voxel).</p>
<p>So you only load necessary "box" to render. Your vrmesh can be huge, only the elements necessary for the calculation will be loaded/unloaded on the fly. As well as tiled maps, this increase your disk IO but gives you great flexibility.</p>
<p>Don't be afraid because of the size, vrmesh are done for! :D</p>
<h4>Practice</h4>
<p>Let's do simple, here is a scene with to 24 frame animation cycles:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_001.png" title="user_attr_vray_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/.user_attr_vray_001_m.jpg" alt="user_attr_vray_001.png" style="display:block; margin:0 auto;" title="user_attr_vray_001.png, fév. 2013" height="346" width="560" /></a></p>
<center><i>You can't see it but they are walking! :onSeFendLaPoire:</i></center>
<p>At this step, principle is to assign, to each "shader group", an ordinary shader (lambert is fine). Name is the importante part because it will be stored in the vrmesh with it assigned faces:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_002.png" alt="user_attr_vray_002.png" style="display:block; margin:0 auto;" title="user_attr_vray_002.png, fév. 2013" height="159" width="549" /></p>
<center><i>"lambert5" should have been "arms" but I've completely missed it. :baffed:</i></center>
<p>Even though this is not required, try to keep coherence in names between all your objects. So, if you have characters crowd to export. Assign them all shaders "arm", "corp", "head", "Hair", "tenuHaut", "tenuBas", etc ... With a bit of script, you will save time later.</p>
<p>Select mesh group you want to export:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_003.png" alt="user_attr_vray_003.png" style="display:block; margin:0 auto;" title="user_attr_vray_003.png, fév. 2013" height="92" width="253" /></p>
<p>Then go in <em>Create/V-Ray/Create proxy</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_004.png" alt="user_attr_vray_004.png" style="display:block; margin:0 auto;" title="user_attr_vray_004.png, fév. 2013" height="489" width="420" /></p>
<p>You will have a <del>horrible</del> quite sober interface:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_005.png" alt="user_attr_vray_005.png" style="display:block; margin:0 auto;" title="user_attr_vray_005.png, fév. 2013" height="582" width="576" /></p>
<p>Select <em>Export animation</em>. If your mesh is animated and you plan to use motion blur on your final render, select <em>Export velocity</em>. The two values will create the interval to calculate direction vectors to be stored in the vertices. VRay will calculate the motion blur geometry from this informations. Be careful on this interval. You can expect some (bad) surprises. If you don't go with motion blur, deselect this option. This will store less information in the geometry.</p>
<p>In my case I also select <em>Use playback range</em> but advise accordingly to your case. :reflechi:</p>
<p>With <em>Face in preview</em>, Chaos Group provide us what look like a small and trivial feature but very interesting in practice: The idea is to save <em>n</em> face indices that will be displayed in the viewport later.</p>
<p>When doing proxy, mainly to lighten a scene, we often have to manage large bouding box. Depending on the pipeline, you may even have a proxy made by another department. Here, Chaos Group guys had a very interesting reflection: "Why not make a cool proxy corresponding to the real geometry?". The bet is entirely successful, you will see that later. :)</p>
<p>If you want to import directly the newly created proxy, you can select <em>Automatically create proxies</em>.</p>
<p>For the rest, I strongly advise you to read the <a href="http://www.spot3d.com/vray/help/maya/150R1/vrayproxy_params.htm#export_gui" hreflang="en">documentation</a>.</p>
<p>Once that's done, click on <em>Create Proxy</em>!</p>
<p>Proceed this steps for every characters you want to export.</p>
<center>:longBar:</center>
<p>To import all this:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_006.png" alt="user_attr_vray_006.png" style="display:block; margin:0 auto;" title="user_attr_vray_006.png, fév. 2013" height="109" width="431" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_007.png" alt="user_attr_vray_007.png" style="display:block; margin:0 auto;" title="user_attr_vray_007.png, fév. 2013" height="201" width="333" /></p>
<p>Tadaaaa:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_008.png" alt="user_attr_vray_008.png" style="display:block; margin:0 auto;" title="user_attr_vray_008.png, fév. 2013" height="398" width="349" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_009.png" alt="user_attr_vray_009.png" style="display:block; margin:0 auto;" title="user_attr_vray_009.png, fév. 2013" height="446" width="538" /></p>
<p>And now our 100 faces per object. Slap it right? :aupoil:</p>
<p>Seriously, you can import hundreds of them, your viewport does not flinch. In addition, these proxies correspond to the final model. This approximation should less bother you than a low model or worse, a bounding box.</p>
<p>Now let's look at this options:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_010.png" alt="user_attr_vray_010.png" style="display:block; margin:0 auto;" title="user_attr_vray_010.png, fév. 2013" height="711" width="522" /></p>
<p>If we select <em>Bounding box</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_011.png" alt="user_attr_vray_011.png" style="display:block; margin:0 auto;" title="user_attr_vray_011.png, fév. 2013" height="389" width="418" /></p>
<center><i>For nostalgic. :trollface:</i></center>
<p>If you are not satisfied with the approximation, Chaos Group has got you covered: <em>Show whole mesh</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_012.png" alt="user_attr_vray_012.png" style="display:block; margin:0 auto;" title="user_attr_vray_012.png, fév. 2013" height="309" width="257" /></p>
<p>Other options are more specific. I will not go into detail. If you're interested: <a href="http://www.spot3d.com/vray/help/maya/150R1/vrayproxy_params.htm#parameters" hreflang="en">:RTFM:</a>.</p>
<p>Just notice animation speed is customisable (anim is possible too! :gniarkgniark: ) and that in our case (and most cases) it's <em> Loop</em>.</p>
<p>Go tabs on the right. VRay has already connected a shader and...</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_013.png" alt="user_attr_vray_013.png" style="display:block; margin:0 auto;" title="user_attr_vray_013.png, fév. 2013" height="419" width="520" /></p>
<p>Oh miracle! Our slots are there! :laClasse:</p>
<p>You will just have to drag and drop shaders to use.</p>
<p>Note that trying too hard to be nice, VRay create a shader for each imported vrmesh. But if you import twice the same vrmesh, you will have two similar shaders. Don't hesitate to remove one and apply a single shader for each vrmesh "source".</p>
<p>Now let's get serious! :enerve:</p>
<h3>User Attributes</h3>
<p>Select the <em>transform</em> node of one vrmesh you have and add <em>User attributes</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_014.png" alt="user_attr_vray_014.png" style="display:block; margin:0 auto;" title="user_attr_vray_014.png, fév. 2013" height="171" width="439" /></p>
<p>In these <em>User attributes</em> (lost at the bottom of your <em>Attribute Editor</em>) add the following:</p>
<pre class="bash bash"><span style="color: #007800;">casquetteColor</span>=<span style="color: #000000;">1</span>,<span style="color: #000000;">1</span>,<span style="color: #000000;">0</span>;</pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_015.png" alt="user_attr_vray_015.png" style="display:block; margin:0 auto;" title="user_attr_vray_015.png, fév. 2013" height="82" width="522" /></p>
<p>There, you should begin to understand. :siffle:</p>
<p>On the second, create a similar value:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_016.png" alt="user_attr_vray_016.png" style="display:block; margin:0 auto;" title="user_attr_vray_016.png, fév. 2013" height="77" width="517" /></p>
<p>Now create a shader, a <em>blinn</em> in my example (Yes, I know it's bad). Give it a clear name (it will quickly become a mess in your connections so it's up to you :redface: ):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_017.png" alt="user_attr_vray_017.png" style="display:block; margin:0 auto;" title="user_attr_vray_017.png, fév. 2013" height="119" width="224" /></p>
<p>Add a <em>VRayUserColor</em> node then enter your attribute value:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_018.png" alt="user_attr_vray_018.png" style="display:block; margin:0 auto;" title="user_attr_vray_018.png, fév. 2013" height="134" width="394" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_019.png" alt="user_attr_vray_019.png" style="display:block; margin:0 auto;" title="user_attr_vray_019.png, fév. 2013" height="289" width="517" /></p>
<p>Set the default color you want. It will be the color used if <em>User Attribute</em> is not present/valid.</p>
<p>You have some syntax examples just below the node (another good idea).</p>
<p>Connect this node to the <em>color</em> attribute of your shader (it's a shame the <em>default color</em> is not used in the hypershade, it would be a nicer than a black hole).</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_020.png" alt="user_attr_vray_020.png" style="display:block; margin:0 auto;" title="user_attr_vray_020.png, fév. 2013" height="139" width="395" /></p>
<p>Connect your shader to both <em>VRayMeshMaterial</em>, in the "casquette" slot:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_022.png" alt="user_attr_vray_022.png" style="display:block; margin:0 auto;" title="user_attr_vray_022.png, fév. 2013" height="333" width="515" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_021.png" alt="user_attr_vray_021.png" style="display:block; margin:0 auto;" title="user_attr_vray_021.png, fév. 2013" height="223" width="367" /></p>
<p>Then render:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_023.png" alt="user_attr_vray_023.png" style="display:block; margin:0 auto;" title="user_attr_vray_023.png, fév. 2013" height="433" width="597" /></p>
<center><i><a href="http://www.youtube.com/watch?v=8RLPG88u3lM">Victory song</a> (to listen with :smileFou: )</i></center>
<p>Do the same for all shaders (except "corp" that we keep for after :jdicajdirien: ):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_024.png" alt="user_attr_vray_024.png" style="display:block; margin:0 auto;" title="user_attr_vray_024.png, fév. 2013" height="144" width="502" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_025.png" alt="user_attr_vray_025.png" style="display:block; margin:0 auto;" title="user_attr_vray_025.png, fév. 2013" height="426" width="564" /></p>
<p>Well, those are for colors. You can do the same with <em>VRayUserScalar</em> and floatting point value.</p>
<p>Now we start the body ("corp"):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_026.png" alt="user_attr_vray_026.png" style="display:block; margin:0 auto;" title="user_attr_vray_026.png, fév. 2013" height="189" width="357" /></p>
<center><i>Just a file node.</i></center>
<p>Supposing you have four textures:</p>
<ul>
<li>checker_default.png</li>
<li>checker_four.png</li>
<li>checker_etc.png</li>
</ul>
<p>The only variable in your texture is located at the end (and, why not, in the path).</p>
<p>Put this in your file path:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_027.png" alt="user_attr_vray_027.png" style="display:block; margin:0 auto;" title="user_attr_vray_027.png, fév. 2013" height="64" width="354" /></p>
<p>Then apply this variable to your different <em>User Attributes</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_028.png" alt="user_attr_vray_028.png" style="display:block; margin:0 auto;" title="user_attr_vray_028.png, fév. 2013" height="61" width="506" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_029.png" alt="user_attr_vray_029.png" style="display:block; margin:0 auto;" title="user_attr_vray_029.png, fév. 2013" height="69" width="518" /></p>
<p>Render!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_030.png" alt="user_attr_vray_030.png" style="display:block; margin:0 auto;" title="user_attr_vray_030.png, fév. 2013" height="428" width="567" /></p>
<center><i><a href="http://www.youtube.com/watch?v=8RLPG88u3lM">Once again?</a></i></center>
<p>And if we have no limit:
<img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_031.png" alt="user_attr_vray_031.png" style="display:block; margin:0 auto;" title="user_attr_vray_031.png, fév. 2013" height="520" width="681" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_17_Les_User_Attributes_de_VRay/user_attr_vray_032.png" alt="user_attr_vray_032.png" style="display:block; margin:0 auto;" title="user_attr_vray_032.png, fév. 2013" height="417" width="579" /></p>
<p>If you often do crowds, you should seriously consider this solution.</p>
<h3>Notice</h3>
<p>I think, as mental ray <em>User Data</em>, it's a bit cumbersome to deal with. But if you or your colleague is scripter, it is not too difficult to put it up on a production.</p>
<p>I breath the poppy it may (this is unconfirmed so take with a grain of salt) Deex (whom I thank for making me discover VRay) integrates a simple way to deal with them in <a href="http://arsenal.deex.info/" hreflang="en">Arsenal</a> (but dont be too hasty :sourit: ).</p>
<h3>Conclusion</h3>
<p>I hope this tutorial will help you in your productions.</p>
<p>See you!</p>
<p>Dorian</p>
<center>:marioCours:</center>
Combine importons with Final Gather (English Translation)urn:md5:fbc178cca3b83e95a05a222f2eb7d7352013-02-10T20:40:00+01:002013-07-26T17:59:59+02:00NarannInfographie 3D - Boulotautodeskenfinal gatherimportonsmental ray<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_render_002_29sec_tn.png" alt="fg_render_002_29sec_tn.png" style="float:left; margin: 0 1em 1em 0;" title="fg_render_002_29sec_tn.png, fév. 2013" height="150" width="150" />This is an english translation of a <a href="https://www.fevrierdorian.com/blog/post/2013/02/10/Combiner-les-importons-avec-le-Final-Gather">ticket I wrote</a> few days ago.</p>
<p>Have you ever had a lighting where mister <em>Final Gather</em> give you a scene with a whole dirty flicking? The only solution is often to increase render settings and so, render times (when it is still possible ^^') to hide the misery.</p>
<p>And yet, sometimes, increase all settings like a pig is not enough, simply because <em>Final Gather</em> can not handle your case.</p>
<p>A solution to drastically solve (but not always) this problem exists: Importons.</p> <p>Importons are more than a simple <em>Final Gather</em> variation. They fit in a significant change in the way mental ray will deal with global illumination and sampling in general.</p>
<h3>First step, get a correct <em>Final Gather</em>.</h3>
<p>The question on how to configure the <em>Final Gather</em> is not straightforward as the results can deeply change depending on the scene. I'm going to assume we don't want a perfect <em>Final Gather</em> but an "effect" of <em>Final Gather</em> (indirect illumination in one bounce). I invite you to look at <a href="http://docs.autodesk.com/MENTALRAY/2013/ENU/mental-ray-help/files/manual/node76.html#opt:finalgather_trace_depth" hreflang="en">the documentation</a>.</p>
<h4>Accuracy</h4>
<p>It's the number of samples send through the scene for each point of <em>Final Gather</em> to determine the value of the illumination at this point. If there is not enough, the actual point will not be stable. We can set it to 200-500 for most cases, but sometime you need to go on values close to 1000 in really extreme cases.</p>
<p>If you have a high <em>point density</em> value, <em>accuracy</em> car drastically increase render times.</p>
<h4>Point density</h4>
<p>This is a factor of the number of <em>Final Gather</em> points shoot from the camera. A value of 2.0 doubles the number of <em>Final Gather</em> points to compute, and so, double the <em>Final Gather</em> generation time.</p>
<p>This is the first parameter that I invite you to increase before boost the <em>accuracy</em>. Keep in mind that <em>accuracy</em> has an exponent effect. Do not change one without taking care the value of the other. A value of 10 on a production render with flat surfaces of uniform color is possible. With textures, variations can be less visible and you can almost stay on the little values like 2.0-5.0.</p>
<h4>Point interpolation</h4>
<p>This is the number of <em>Final Gather</em> points that will be interpolated to return a value of indirect illumination to the projected sample. This value drastically changes the visual appearance of <em>Final Gather</em>. The more interpolation is, the more <em>Final Gather</em> is smooth but more it flick in animation if it's not bake. If your scene has high contrasted lighting, I suggest you go down this value to make the flicking generally less visible (more granular). We can put 1 if needed.</p>
<p>Note that you can increase this value at the same time as <em>point density</em>, because the more you have points, the more you can interpolate and keep flicking invisible.</p>
<h4>For the rest...</h4>
<p>I do the following things:</p>
<ul>
<li><em>Secondary Diffuse Bounce</em>: 0</li>
<li><em>Reflection</em>, <em>Refraction</em> and <em>Max Trace Depth</em> (of <em>Final Gather</em> of course): 0</li>
</ul>
<p>Why all of this to zero? Because beyond to increase rendering time and potential artifacts, one diffuse bounce is enough to create a convincing "effect" of indirect illumination.</p>
<ul>
<li>If you are doing animation, set the <em>multiframe</em> mode (<em>Optimize for Animation</em> in Maya).</li>
<li>If you want a brute force <em>Final Gather</em>, switch to <em>force</em> (<em>No FG Caching</em>). For each sample, a new <em>Final Gather</em> point will be created. Of course, this is (very) long.</li>
</ul>
<p>I will not spend much time on this, it was mainly a reminder. Let's talk about Importons! :hehe:</p>
<h3>What importons are?</h3>
<p>I could spend a lot of time trying to explain what the importons are but I think <a href="http://docs.autodesk.com/MENTALRAY/2013/ENU/mental-ray-help/files/manual/importons.html" hreflang="en">the official documentation</a> is very clear. Just read it! :RTFM:</p>
<p>Basically, you guessed it, mental ray "shoot" particles from camera that bounces in the scene and determines which areas of the scene have "visual" importance in the final image. Therefore, mental ray is able to "know" which areas of the scene will be important to sample and forget the others. Until now, <em>Final Gather</em> was very silly, but with the importons, it is much less. :jdicajdirien:</p>
<h3>Autodesk, again and again and...</h3>
<p>Well, I starts to get bored to slug them all the time but you will see that, once again, we fight against Maya to run the thing...
For an unknown reason, Autodesk has decided that if the user tick <em>Final Gather</em>, he had no interest to tick <em>Importons</em> (neither <em>Irradiance Particles</em>). This option becomes grayed out.
Yes, that's stupid... I suppose choice has been made one day, without paid to much attention to the documentation and has never been change since (are they only aware of?).
So, we will not speak about this too much, I invite you to use my tool <a href="Mhttp://forums.cgsociety.org/showthread.php?f=87&t=971719" hreflang="en">enjoyMentalRayStringOptions</a> to go through Maya's limitations.</p>
<p>Insofar tick <em>Final Gather</em> disables importons, you need to tick <em>Final Gather</em> in <em>Render Settings</em> THEN <em>Importons</em> from my interface... Reverse would disables <em>Importons</em>...</p>
<p>Notice that each time you restart <em>Render Settings</em> window, <em>Final Gather</em> will disable <em>Importons</em>... So keep it open! (Yes yes, I know... :pasClasse: )</p>
<p>There is also a tool that I have not tested but who aim to fully integrate with Maya and should solve this problem. It is shown <a href="https://elementalray.wordpress.com/2012/08/10/new-maya-rendering-ui-testing/" hreflang="en">here</a> and the page of the script is <a href="https://code.google.com/p/maya-render-settings-mental-ray/" hreflang="en">here</a>.</p>
<h3>And the tiny tutorial!</h3>
<p>Once you've understand the above part, you will see it's quite easy! :D</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_001.png" alt="fg_001.png" style="display:block; margin:0 auto;" title="fg_001.png, fév. 2013" height="209" width="411" /></p>
<center><i>DIM Tick FG!</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_002.png" alt="fg_002.png" style="display:block; margin:0 auto;" title="fg_002.png, fév. 2013" height="204" width="483" /></p>
<center><i>DAM Tick importons!</i></center>
<p>DOOM Launch the render! :grenadelauncher:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_render_001_27sec.png" title="fg_render_001_27sec.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/.fg_render_001_27sec_m.jpg" alt="fg_render_001_27sec.png" style="display:block; margin:0 auto;" title="fg_render_001_27sec.png, fév. 2013" height="315" width="560" /></a></p>
<center><i>Before... (27sec)</i></center>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_render_002_29sec.png" title="fg_render_002_29sec.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/.fg_render_002_29sec_m.jpg" alt="fg_render_002_29sec.png" style="display:block; margin:0 auto;" title="fg_render_002_29sec.png, fév. 2013" height="315" width="560" /></a></p>
<center><i>After... (29sec)</i></center>
<p>There is a clear improvement in artifacts on the walls. Indeed, mental ray shoot it <em>Final Gather</em> points taking into account the importance (calculated at the beginning of the render) of these points in the final image.</p>
<p>I also invite you to look at the log in <em>Progress</em> mode to ensure <em>Importons</em> are well shooted through the camera</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_003.png" alt="fg_003.png" style="display:block; margin:0 auto;" title="fg_003.png, fév. 2013" height="310" width="209" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_02_10_FG_Importons/fg_004.png" alt="fg_004.png" style="display:block; margin:0 auto;" title="fg_004.png, fév. 2013" height="163" width="468" /></p>
<p>Importons are very fast to shoot through camera so don't hesitate to use them! :banaeyouhou:</p>
<h3>Conclusion</h3>
<p>I hope this little explanations will help you to improve your renders with <em>Final Gather</em>. :sourit:</p>
<p>See you!</p>
<center><i>:marioCours:</i></center>
Deex VRay Arsenal, a small tool that saves a lot of time (English Traduction)urn:md5:288cc3302d771adb7dbd6338560954522013-02-07T10:03:00+01:002013-07-26T18:00:39+02:00NarannInfographie 3D - Boulotarsenaldeexenmayapasserender layerrenduvray<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_tn.png" alt="VRay_Arsenal_tn.png" style="float:left; margin: 0 1em 1em 0;" title="VRay_Arsenal_tn.png, janv. 2013" height="150" width="150" />It's been a while since I heard Damien (aka <a href="http://deex.info/" hreflang="en">Deex</a>) talk about his tool. I even had the opportunity to be on the same production when he thought the main line. When he told me that his tool was finally done, I could not help but ask him a license to test it out. :sourit:</p>
<p>The idea of this post is to present the main features of Arsenal. You will see that despite an approach that may seem a little too user friendly, this tool is a true production tool that make the job quite easier.</p>
<p>Let's go! :sauteJoie:</p> <blockquote><p>Notice: Before we start, I invite you to check <em>Use V-Ray VFB</em> in the <em>VRay Common</em> tab of the <em>Render Settings</em> panel:</p></blockquote>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_012_useVRay_frameBuffer.png" alt="VRay_Arsenal_012_useVRay_frameBuffer.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_012_useVRay_frameBuffer.png, janv. 2013" height="369" width="428" /></p>
<p>This will give you access to a kind of special VRay <em>Render View</em> that allows you (in addition to a lot of things) to directly visualize passes (top left).</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_011_frameBuffer.png" alt="VRay_Arsenal_011_frameBuffer.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_011_frameBuffer.png, janv. 2013" height="137" width="331" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_sampleImage.png" alt="VRay_Arsenal_sampleImage.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_sampleImage.png, janv. 2013" height="319" width="301" /></p>
<center><i>My test imaget: A HDR from <a href="http://www.hdrlabs.com/sibl/archive.html">sIBL</a>, and a lot of talent! :smileFou:</i></center>
<h3>Installation</h3>
<p>I thought <a href="http://arsenal.deex.info/wordpress/documentation/installation/" hreflang="en">installation</a> was "special" in some part. This is not what we are used to see in common scripts.</p>
<p>First, you must install PyQt which most of the time is not an easy task (especially on Windows). Fortunately, an "easy" installer is available on the website. It's pretty cool!</p>
<p>The tool installation has been, designed to simplify the production integration (using environment variables), but can be quite daunting for people who are not necessarily used to play with their <em>Maya.env</em> file. Especially since I'm sure he would have been possible to have a "old school" install (scripts in the <em>script</em> folder, the shelves in the <em>shelves</em> folder, etc ... ), but this wouldn't have been so clean and it would have been a bit more complicated to deal with every update then with the current way, you just need to replace one folder. :laClasse:</p>
<h3>The main window</h3>
<p>When you launch it, you've got this:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_001.png" alt="VRay_Arsenal_001.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_001.png, janv. 2013" height="933" width="536" /></p>
<p>We will talk about tabs bellow.</p>
<p>There is a <em>spin</em> button. Once ticked, Arsenal will "stay at the top". Else, it behave like a Maya window.</p>
<p>There also is a <em>close</em> button at the bottom I would not insult you explain the purpose. :reflexionIntense:</p>
<p>There is three tabs we will see in the order:</p>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2013/02/07/Deex-VRay-Arsenal-a-small-tool-that-saves-a-lot-of-time#pass">Pass</a>, which regroup Maya <em>Render Layers</em> and the VRay pass system. Their both combined use is common.</li>
<li><a href="https://www.fevrierdorian.com/blog/post/2013/02/07/Deex-VRay-Arsenal-a-small-tool-that-saves-a-lot-of-time#quality">Quality</a>, which is a very usefull feature: A slider, from 0 to 99 to increase or decrease render quality using a gradual modification of your render settings.</li>
<li><a href="https://www.fevrierdorian.com/blog/post/2013/02/07/Deex-VRay-Arsenal-a-small-tool-that-saves-a-lot-of-time#toolbox">ToolBox</a>, which regroup a lot of tiny tools that save a lot of time.</li>
</ul>
<h3>L'onglet <em>Pass</em> <a name="pass"></a></h3>
<p>On the top part, it's just Maya <em>Render Layer</em> (unjustly named "Pass" in the three buttons on the left). Click on one of these <em>Render Layer</em> select it the same way Maya does. It's just a time saver. For renderers that don't need complex passes you should not need them.</p>
<p>Notice you can do a <em>Set global to on</em> directly on your <em>RenderLayers</em> from this window (see the <a href="http://download.autodesk.com/global/docs/maya2013/en_us/index.html?url=files/Rendering_nodes_Render_Layer_render_attributes.htm,topicNumber=d30e656262" hreflang="en">Maya doc</a>).</p>
<p>Let's go now through the most interesting part. :popcorn:</p>
<h4>Properties control</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_onglet_propertiesControl.png" alt="VRay_Arsenal_onglet_propertiesControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_onglet_propertiesControl.png, janv. 2013" height="607" width="473" /></p>
<p>This system is extremly fast to prepare your passes.</p>
<p>It's quite (quite) simple:</p>
<ul>
<li>You select an object/group, you do a <strong>Add</strong> and presto!</li>
<li>You want to remove an object/group? <strong>Remove</strong>!</li>
<li>You want to see objets having a particular property? <strong>Select</strong>!</li>
</ul>
<p>It's at once clear and also much faster than the "conventional" method aimed to manage these properties in grainy way on the groups/objects. Here, the system is reversed: You handle objects based on their property.</p>
<p>I find it much clearer. :redface:</p>
<p>Here is an quick explaination of the differentes <em>propertties</em>. For more details, refer to the <a href="http://arsenal.deex.info/wordpress/documentation/" hreflang="en">official documentation</a>.</p>
<h5>Black hole</h5>
<p>Objects having this <em>property</em> will not be shown in the render (their will be black) and their alpha will be empty.</p>
<p><strong>Receive shadow</strong>: <strong>Black hole</strong> objects having this <em>property</em> will recieve shadows and store it in their alpha (example: a ground).</p>
<h5>GI</h5>
<ul>
<li><strong>Generate off</strong>: Object with this <em>property</em> will not be used during the global illumination step (exmple: separate <strong>GI</strong> between characters and the set).</li>
<li><strong>Receive off</strong>: Object with this <em>property</em> will not recieve any indirect illumination.</li>
</ul>
<h5>Visibility</h5>
<ul>
<li><strong>Primary off</strong>: Object with this <em>property</em> will not be visible in primary rays (their will be visible in reflections/refractions/GI).</li>
<li><strong>Reflection off</strong>: Object with this <em>property</em> will not be visible in reflection rays (can be used to provide complex object to be reflected if it doesn't bring something to the final render).</li>
<li><strong>Refraction off</strong>: Object with this <em>property</em> will not be visible in refraction rays.</li>
</ul>
<h5>Shadows</h5>
<p><strong>Cast off</strong>: Les objets ayant cette <em>property</em> ne projetteront pas d'ombres.
<strong>Cast off</strong>: Object with this <em>property</em> will not project shadows.</p>
<h4>On fly control</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_002_onFlyControl.png" alt="VRay_Arsenal_002_onFlyControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_002_onFlyControl.png, janv. 2013" height="447" width="475" /></p>
<p>I'm not a big fan of terminologies used in this tab (even if it is VRay one). I'll try to be clear. :gne:</p>
<h5>Light select</h5>
<p>Here we will try to render passes by light or for every lights. This <em>passes</em> will be named as <em>nameOfYourLightShape_normal</em>, <em>nameOfYourLightShape_diffuse</em>, etc...</p>
<ul>
<li><strong>Manual</strong>: If you want to render "contributions" for given lights only, it is here that you will.
<ul>
<li><strong>Normal</strong>: And this is where it lends quite confusing: Here, <em>Normal</em> is not the normal from a vector direction sense but normal within the meaning "classic". It is therefore a pass containing all the direct illumination of one light. That's all. Note that this is the terminology used in VRay...</li>
<li><strong>Diffuse</strong>: A direct lighting <em>Pass</em> of a light, with only diffuse informations.</li>
<li><strong>Raw</strong>: A <em>pass</em> that contain "raw" direct lighting informations of the light.</li>
<li><strong>Specular</strong>: A specular <em>pass</em> of the given light.</li>
</ul></li>
<li><strong>Automatic</strong>: Say that if you intend to render all the passes for all lights in the scene, go directly there. You can also combine <strong>Automatic</strong> mode for a specific pass with <strong>Manual</strong> mode for another specific pass.
<ul>
<li><strong>Specular</strong>: Render all specular <em>passes</em> for every light in the scene.</li>
<li><strong>Raw</strong>: Same as <strong>Specular</strong> but for the <strong>Raw</strong>.</li>
<li><strong>Diffuse</strong>: Same but for the <strong>Diffuse</strong> pass.</li>
<li><strong>Normal</strong>: Same but for the <strong>Normal</strong> pass.</li>
</ul></li>
</ul>
<h5>Arsenal settings</h5>
<p>Here are my favorite options. Even if you can't really guess what they are at first. :siffle:</p>
<ul>
<li><strong>VrayMtl Lambert</strong>: Tick this case will transform to <em>VrayMtl Lambert</em>, on the fly and during the render, every materials of your scene, which is really usefull to check your global lighting. The point where this way differ from the <em>material override</em> with <em>RenderLayer</em>, it's materials will still have their displacement/bump/normal if their have one.</li>
<li><strong>Material ID on fly</strong>: Tick this case will generate <em>Material ID</em> on the fly, when the render start, using material names (without namespaces) as "seed". So, you will always have the same <em>Material ID</em> color as your material has the same name. Of course, you must have a <em>Material ID</em> in your <em>Render Element</em>. Note this feature is compatible with <em>VRayblendMaterial</em>: If you blend many materials (with mask) in a <em>VRayblendMaterial</em>, each material will have it own ID in the pass.</li>
<li><strong>Proxy Objects ID on fly</strong>: Same as <em>Material ID</em> but this time with VRay proxy.</li>
</ul>
<h4>Matte control</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_003_matteControl.png" alt="VRay_Arsenal_003_matteControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_003_matteControl.png, janv. 2013" height="252" width="473" /></p>
<p>This one I also love it as it's simple! :dentcasse:</p>
<p>Principe is to generate "mask" passes. You know? RGB mask:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_013_passMask.png" alt="VRay_Arsenal_013_passMask.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_013_passMask.png, janv. 2013" height="369" width="324" /></p>
<p>Roughly:</p>
<ul>
<li>You click <strong>Create new</strong>.</li>
<li>You give a name to you mask pass (<strong>Mask name</strong>). If you don't give a name to your mask pass, it will be removed. :mitraille:</li>
<li>You select a/some objects/groups.</li>
<li>You click <strong>add in red</strong> (or green/blue).</li>
<li>You render and you have you mask pass!</li>
<li>You can do this again for another pass.</li>
</ul>
<p>It's as simple I don't even know what to add! :redface:</p>
<p>Oh yes! This system bypasses a VRay limitation. Example:</p>
<ul>
<li>You have a object_A with ID 1.</li>
<li>A object_D with ID 2.</li>
<li>This two objects are in a group with ID 3.</li>
<li>Create a <em>Multi Matte</em> <em>Render Element</em> and set <em>Red object ID</em> to 3.</li>
<li>Render and look at your multimatte pass.</li>
</ul>
<p>It is black! Why? Because as VRay notice objects in group have an object ID, it doesn't show them in the pass.</p>
<p>With the Arsenal system, if you want to have a red group, it will be red!</p>
<h4>FastControl</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_004_fastControl.png" alt="VRay_Arsenal_004_fastControl.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_004_fastControl.png, janv. 2013" height="183" width="474" /></p>
<p>This tab just gather main VRay options to quickly activate/desactivate them.</p>
<h3><em>Quality</em> tab <a name="quality"></a></h3>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_005_qualityTab.png" alt="VRay_Arsenal_005_qualityTab.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_005_qualityTab.png, janv. 2013" height="773" width="492" /></p>
<center><i>A big slider which change of color! :D</i></center>
<p>Here we are in the area that peoples who are not really render guru should enjoy. And yet, you will see it can definitely pleased peoples who are used to change their settings manually. :dentcasse:</p>
<p>The first thing to do to activate all of this is to click on <strong>Save actual settings</strong> which will save your current settings, and on <strong>Optimize</strong> to setup different expressions. From this point, you can start to change the main slider to vary render quality.</p>
<h4>Preset Type</h4>
<p><em>Preset Type</em> are more about slider configuration than "preset". There is two <em>Preset Type</em>:</p>
<ul>
<li><em>deeX_interior</em></li>
<li><em>deeX_exterior</em></li>
</ul>
<p>This two choices represent a majority of lighting case.</p>
<p>Presets are simple texte file that can be found in:</p>
<pre class="bash bash">C:\Users\Narann\Documents\maya\deeXVRayArsenal\<span style="color: #000000;">2013</span>\win64\scripts\presets</pre>
<p>This files, with very simple syntax, are used to drive ranges (min and max) of VRay attributes which will be then modified by your slider. If I don't talk nonsense, the relationship between the slider and settings it drive is <a href="https://en.wikipedia.org/wiki/Logarithm" hreflang="en">logarithmic</a> to avoid settings burn render times when you start to increase too much the slider. :grenadelauncher:</p>
<p>If the opportunity to create it own presets will probably doesn't interest occasional lighters, being able to set these <em>min</em> and <em>max</em> parameters manually can be very interesting for VRay gurus having their little habits. :papi:</p>
<p>Moreover, it would have been great to see this tool independent of VRay to drive, for example, Mental Ray. :baffed:</p>
<blockquote><p>If you want to prevent Arsenal modifications on a specific attribute, just lock it.</p></blockquote>
<h4>Offset quality</h4>
<p>Here we have a very good idea. Principe is to apply an offset to the slider for some parameters.</p>
<p>For example, my slider is suppose to be <em>50</em>. I think sampling is a bit sensitive and increases the rendering time too quickly. However, I am satisfied with the rest... So, I just check the <strong>Image sampler</strong> then type <em>-10</em> for the settings of the sampler applies as if my overall slider was <em>40</em>! Brillant! :youplaBoum:</p>
<h4>Optimize material(s)</h4>
<p>Another interesting idea: You determine two extrem subdivision values (sampling), click <strong>Optimize material(s)</strong> button and Arsenal will determine the value to use based on <strong>Glossiness</strong> value of the material. More it's gloss, more you need samples.</p>
<p>Notice the checkbox right to <strong>LayerMode</strong>. If you check it before clicking <strong>Optimize material(s)</strong> button and you are in a <em>RenderLayer</em> (other than <em>masterLayer</em>) attributes will changed only on the current <em>RenderLayer</em>.</p>
<h4>Optimize light(s)</h4>
<p>I'm not sure this button is very useful. For now it only set every VRayLights Subdiv parameter to 5...</p>
<h3><em>ToolBox</em> tab <a name="toolbox"></a></h3>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_006_toolBoxTab.png" alt="VRay_Arsenal_006_toolBoxTab.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_006_toolBoxTab.png, janv. 2013" height="707" width="494" /></p>
<p>Here you will find small very specific tools.</p>
<h4>Proxy</h4>
<p><a href="http://www.spot3d.com/vray/help/maya/150R1/vrayproxy_params.htm" hreflang="en">VRayProxy</a> are a marvel. We used them on <a href="https://www.fevrierdorian.com/blog/post/2013/02/07/url=http://www.imdb.com/title/tt1701210/" title="url=http://www.imdb.com/title/tt1701210/">url=http://www.imdb.com/title/tt170...</a>Day Of The Falcon<a href="https://www.fevrierdorian.com/url" title="/url">/url</a> to make horse animation cycles shifting speed.</p>
<h5>Multi proxy importer</h5>
<p>This button allows you to import multiple VRayProxy because, as surprising as it may seem, it is not possible to do this in standard... You need to import VRayProxy one by one...</p>
<h5>Shader auto connect</h5>
<p>When you export a VRayProxy, the "assignation slots" are created. Principle is to store, inside the proxy, shader assignations for reassign your shaders later.</p>
<p>Once VRayProxy reimported into your scene, you can apply a <a href="http://www.spot3d.com/vray/help/maya/150R1/vraymeshmtl_params.htm" hreflang="en">VRayMeshMaterial</a> (FYI, it's already created when you import). This material will show different slots assignments for your proxy to connect your own shaders.</p>
<p><strong>Shader auto connect</strong> button can automatically reconnect shaders based on their names, which avoids having to do it yourself. On proxy with tens or hundreds of assignments, it is rather convenient...</p>
<h5>Ignore namespace</h5>
<p>This box allows you to ignore shader namespaces of your scene when reassigning to the proxies. In principle, this allows you to have these shaders in external references (with a namespace) without have problems for reconnect using their names.</p>
<h4>ID</h4>
<p>Once again, here we have tools to quickly assign IDs to materials and objects.</p>
<p>Goal is then to release this IDs as passes to give to your compositing friends.</p>
<h4>Material ID</h4>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_008_materialID.png" alt="VRay_Arsenal_008_materialID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_008_materialID.png, janv. 2013" height="431" width="383" /></p>
<ul>
<li><strong>Add on</strong>: Used to apply <em>Material ID</em> attributes to selected materials/all materials/all shading engine.</li>
<li><strong>Set on</strong>: Apply a unique ID to <em>Material ID</em> attributes for the selected materials/all materials/all shading engine. This number is generated randomly using the material name. This method has double side: If you change the name of the material them you click again on <em>Set on</em>, it ID color will change likewise. Notice <strong>Material ID on fly</strong> checkbox from <em>Pass</em>/<em>On fly control</em> do the same thing directly on the fly! :)</li>
<li><strong>Remove on</strong>: Supprime les attributs de <em>Material ID</em> des materials sélectionnés/tous les materials/tous les shading engine.</li>
<li><strong>Remove on</strong>: Remove <em>Material ID</em> attributes from selected materials/all materials/all shading engine.</li>
</ul>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_014_materialID.png" alt="VRay_Arsenal_014_materialID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_014_materialID.png, janv. 2013" height="115" width="528" /></p>
<p>Last point, to make this work, don't forget to create a <em>Material ID</em> pass (<em>Render Element</em>):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_007_materialID.png" alt="VRay_Arsenal_007_materialID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_007_materialID.png, janv. 2013" height="568" width="437" /></p>
<h4>Object ID</h4>
<p>Same as <em>Material ID</em> but at the objects level.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_015_objectID.png" alt="VRay_Arsenal_015_objectID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_015_objectID.png, janv. 2013" height="84" width="451" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_010_objectID.png" alt="VRay_Arsenal_010_objectID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_010_objectID.png, janv. 2013" height="621" width="570" /></p>
<center><i>Notice difference with the ''Material ID'' pass.</i></center>
<p>Each object will have a differente id.</p>
<p>Once again, don't forget to create <em>Object ID</em> <em>Render Element</em>:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2013_01_15_VRay_Arsenal/VRay_Arsenal_009_objectID.png" alt="VRay_Arsenal_009_objectID.png" style="display:block; margin:0 auto;" title="VRay_Arsenal_009_objectID.png, janv. 2013" height="549" width="429" /></p>
<h4>Material control</h4>
<p>This part allows you to apply some key values on selected/all materials in one click.</p>
<ul>
<li>Reflection/Refraction Subdivs</li>
<li>Reflection/Refraction Interpolation</li>
<li>Reflection/Refraction Max depth</li>
</ul>
<p>A <strong>Trace</strong> option (Reflection/Refraction) to completly activate/desactivate raytracing would be interesting here. :dentcasse:</p>
<h4>Lights control</h4>
<p>It can't be easier: Apply a <strong>Shadow subdivs</strong> value to selected/all lights of the scene in one click.</p>
<h3>Techniquement, comment ça marche?
How it work technically?</h3>
<p>Hahaha! The particularity of this tool is to not change your scene when you generate passes (Except Maya <em>RenderLayers</em> of course), when you do some "On the fly" stuff or even Matte.</p>
<p>But then? How is it doing? :gne2:</p>
<p>Well, it uses a feature of VRay I'll dream of seeing widespread throughout render engines as it increase pipeline possibilities: The Python API <a href="http://www.spot3d.com/vray/help/maya/150R1/render_scene_access.htm" hreflang="en">VRay Scene Access</a>.</p>
<p>I quote the documentation as it's very clear:</p>
<blockquote><p>The V-Ray scene access python API allows you to modify the V-Ray scene after it is translated by the V-Ray for Maya translator, and before it is rendered and/or exported to a .vrscene file.
The scene access API allows you to expand the V-Ray for Maya translator by providing custom translation for constructs that are not recognized by V-Ray, or for modifying the scene before rendering without changing the original Maya scene.</p></blockquote>
<p>Boum! Chaos Group guys are genius! :sauteJoie:</p>
<p>Roughly, it allow you to drive the renderer before (during?) the render. So we can imagine crazy stuff like generate scenes directly in VRay, on the fly, without using heavy vrscene file. For those who have worked with Renderman, it means no more generation of heavy RIBs you use only once. Here, you can control VRay using Python!</p>
<p>I asked for a similar feature on <a href="http://forum.nvidia-arc.com/showthread.php?8097-Python-to-controle-mental-ray" hreflang="en">Nvidia ARC forum</a> (formerly Mental Image) as I thought it was a great idea but I'm not sure it changes much. :triste:</p>
<p>So, Damien's tool uses it, and that's what allows him to keep a clean Maya scene and be able to act directly at VRay level, before rendering.</p>
<h3>Conclusion</h3>
<p>For having test the thing thoroughly I can assure you it worth every penny (69€! :bravo:).</p>
<ul>
<li>Time saved preparing passes is terrible and you don't have to go through all the VRay menus.</li>
<li>The ability to control objects according their <em>properties</em> is quite natural.</li>
<li>RGB Mattes management is also easy.</li>
<li>On the fly Material/Object ID working in few clicks... You have no more excuse for not using them.</li>
<li>On the fly VRayMtlLambert deserves a section all by itself as it can be practical.</li>
<li>Quality slider preset and offsets system can go far.</li>
<li>Deex will continue to add stuff.</li>
</ul>
<p>What is missing:</p>
<ul>
<li>Ability to export you "pass set" in a text file for later import (planned!).</li>
<li>More <em>On the fly</em> options (I'm a big fan of this!)</li>
<li>A Python API as easy to use than this buttons. Like <em>arsenal.pass.propertiesControl.blackHole.add("myObject")</em>. But except me, I'm not sure any one will find this useful... :trollface:</li>
</ul>
<p>OK, I sell <del>a little</del> completely the thing but yes: It's worth it! :aupoil:</p>
<p>Beyond the fact that I invite you to jump into this tool, I hope that, if you already have it, this presentation/tutorial will allow you to go further! :)</p>
<p>See you!</p>
<center><i>:marioCours:</i></center>
Resolve network slowdowns due to Nuke, After Effects and others software like them (English Translation)urn:md5:83166070f4695a715ca359133a4fb7a02012-10-19T10:31:00+02:002014-11-26T14:51:19+01:00NarannScript et codeafter effectennetworknukepythonslowdown<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_10_14_nuke_reseau_lenteurs/nuke_reseau_tn.png" alt="nuke_reseau_tn.png" style="float:left; margin: 0 1em 1em 0;" title="nuke_reseau_tn.png, oct. 2012" height="150" width="150" />This is an english translation of a <a href="https://www.fevrierdorian.com/blog/post/2012/10/14/Resoudre-les-ralentissements-reseaux-dus-a-Nuke-After-Effect-et-autres-logiciels-du-genre">ticket I wrote</a> (in french :franceHappy: ) a few days ago.</p>
<p>Nuke, After Effects and probably other softwares, can be very "greedy" in terms of disk accesses. So much so they can break network performances drastically if several stations are rendering.</p>
<p>In this ticket I will show you a quick explanation of the why and how a small Python class that can be used as a prototype to solve the problem.</p> <h3>What's the problem?</h3>
<p>Let's start with a concrete exemple:</p>
<p>At the end of <a href="http://www.imdb.com/title/tt1482459/" hreflang="en">Lorax</a>, many compositors were rendering their images all at the same time and time estimations given by Nuke were sometime surprising (3h-4h left for a simple nuke script). Monitoring a file being written, I realized its weight was increasing very slowly. It was 100ko, then 300ko, then after 10 minutes it became 400ko etc... I concluded the network was overloaded... :reflexionIntense:</p>
<p>I remembered that we had the same worries on <a href="http://www.allocine.fr/film/fichefilm_gen_cfilm=183772.html" hreflang="fr">Tales Of The Night</a>. Our infrastructure was certainly much smaller but 3 After Effect renders were "pumping" the entire network. The found solution at that time was to render the file in local and copy it once finished. The effects were immediate: No more slowdown with the network. :sauteJoie:</p>
<p>So I tried to render the nuke script of a CG artist locally to see if it reduces the problem. The largest files to read were source files (because they are many), and the final image weight was actually very light so I was not thinking it could change anything. But, once again, the conclusion was clear: The rendering was finished in 10-15 minutes (I'm not kidding) instead of 3-4hrs... :laClasse:</p>
<p>I thought it was maybe the writing process which was a problem. However I tried to the copy the new images sequence to the server, and it happened to be very fast. So I've done this with every artist, one by one, then the network has no bottleneck anymore and every renders completed.</p>
<p>But this was a very poor fix. We had to understand why Nuke couldn't write its images faster through the network. After <a href="http://forums.thefoundry.co.uk/phpBB2/viewtopic.php?p=29117" hreflang="en">having asked</a> on the nuke-users mailing list (where I could see I was not alone but The Foundry didn't seem to be able to fix this), I've started to "profile" Nuke to know how it does the job (<a href="http://en.wikipedia.org/wiki/Strace" hreflang="en">strace</a> and <a href="http://linux.die.net/man/1/inotifywatch" hreflang="en">inotifywatch</a> are you friends :dentcasse: ).</p>
<p>Sometimes conclusion seems to be obvious but it's always good to check you assumptions:</p>
<p>With Nuke, if you write a <em>zip 1 line</em> compressed EXR, in 1920x1080, Nuke will do a little less than 900 (approximately) write accesses on the file. If you are in <em>zip 16 lines</em>, it will do about 70 accesses (1080/16). And in uncompressed, that's really 1080 accesses. :trollface:</p>
<p>In facts, compress in <em>zip 16 line</em> is not efficient if images should be read by Nuke. And depending of your network infrastructure, write line by line can put it completely down. It's difficult to explain how finally few Nuke rendering can fill a network, even if this one is strong. I feel this is related to multithreading: Nuke reads images (often, many at the same time) on the network while it is writing through it.</p>
<p>The most obvious solution is therefore to write the rendered image(s) on the local disk and to copy it in one time (one access) on the network disk. If you don't have technical resources (or just time), it's the simplest approach, but on larger projects it can quickly become daunting and (lest we forget) source of errors.</p>
<p>There are several solutions and I was leaning on a prototype that I found interesting because it's easy to implement.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2012_10_14_nuke_reseau_lenteurs/problems.jpg" alt="problems.jpg" style="display:block; margin:0 auto;" title="problems.jpg, oct. 2012" height="440" width="720" /></p>
<h3>The principle</h3>
<ul>
<li>You launch a Python thread that will watch a folder.</li>
<li>Every three seconds, the thread will list present files in a folder and check if their name will match to a given regular expression (the "pattern" of your file name).</li>
<li>If the file seems to be a file you want to move once finished, it searches the same file with ".finished" (example: "toto.001.exr.finished").</li>
<li>If this file exists, it moves the origin file and removes the ".finished" one and start the main loop again.</li>
<li>Once the render is done (so every images are finished), you ask the thread to stop itself.</li>
</ul>
<p>As you can see, this method requires that you create a ".finished" file each time an image is finished. This is because it's impossible for the thread to know when an image is actually completed. The creation of this ".finished" file can be handled in a thousand different ways (For Maya, a simple "Post render frame" should do the job) so I won't go into the details. :siffle:</p>
<h3>The code</h3>
<p>Here it is:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">os</span>, <span style="color: #dc143c;">threading</span>, <span style="color: #dc143c;">re</span>, <span style="color: #dc143c;">time</span>
<span style="color: #ff7700;font-weight:bold;">class</span> MoverThread<span style="color: black;">(</span> <span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span> <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>, dirTocheck, dirToMoveIn, patternToCheck, force=<span style="color: #008000;">False</span> <span style="color: black;">)</span> :
<span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span>
<span style="color: #008000;">self</span>._terminate = <span style="color: #008000;">False</span>
<span style="color: #008000;">self</span>.<span style="color: black;">dirTocheck</span> = dirTocheck
<span style="color: #008000;">self</span>.<span style="color: black;">dirToMoveIn</span> = dirToMoveIn
<span style="color: #008000;">self</span>.<span style="color: black;">force</span> = force
<span style="color: #808080; font-style: italic;"># regex pattern</span>
<span style="color: #008000;">self</span>.<span style="color: black;">patternToCheck</span> = patternToCheck
<span style="color: #008000;">self</span>.<span style="color: black;">rePattern</span> = <span style="color: #dc143c;">re</span>.<span style="color: #008000;">compile</span><span style="color: black;">(</span> patternToCheck <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># sanity check</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">(</span><span style="color: #008000;">self</span>.<span style="color: black;">dirTocheck</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">Exception</span><span style="color: black;">(</span> <span style="color: #483d8b;">"The given directory (dirTocheck) is not a valid directory -> %s"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">dirTocheck</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isdir</span><span style="color: black;">(</span><span style="color: #008000;">self</span>.<span style="color: black;">dirToMoveIn</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">raise</span> <span style="color: #008000;">Exception</span><span style="color: black;">(</span> <span style="color: #483d8b;">"The given directory (dirToMoveIn) is not a valid directory -> %s"</span> <span style="color: #66cc66;">%</span> <span style="color: #008000;">self</span>.<span style="color: black;">dirToMoveIn</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> run<span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
filesNotMoved = <span style="color: black;">[</span><span style="color: black;">]</span>
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>._terminate :
<span style="color: #808080; font-style: italic;"># we wait 3 seconds before do anything</span>
<span style="color: #dc143c;">time</span>.<span style="color: black;">sleep</span><span style="color: black;">(</span> <span style="color: #ff4500;">3</span> <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># for every "entry" (file or folder) in the folder we check it have the good pattern. If it has, we check for a ".finished" file</span>
<span style="color: #ff7700;font-weight:bold;">for</span> entry <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">listdir</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">dirTocheck</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># check the current entry is "compliant" with the given regex</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>.<span style="color: black;">rePattern</span>.<span style="color: black;">match</span><span style="color: black;">(</span> entry <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">continue</span>
srcFilePath = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">dirTocheck</span>, entry <span style="color: black;">)</span>
dstFilePath = <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">join</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">dirToMoveIn</span>, entry <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">(</span> srcFilePath+<span style="color: #483d8b;">".finished"</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># destination file aready exist?</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">isfile</span><span style="color: black;">(</span> dstFilePath <span style="color: black;">)</span> <span style="color: #ff7700;font-weight:bold;">and</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #008000;">self</span>.<span style="color: black;">force</span>:
<span style="color: #808080; font-style: italic;"># don't add the entry if it is already in the list</span>
<span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> entry <span style="color: #ff7700;font-weight:bold;">in</span> filesNotMoved :
filesNotMoved.<span style="color: black;">append</span><span style="color: black;">(</span> entry <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">continue</span>
<span style="color: #808080; font-style: italic;"># move the file to it new location</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">rename</span><span style="color: black;">(</span> srcFilePath, dstFilePath <span style="color: black;">)</span>
<span style="color: #dc143c;">os</span>.<span style="color: black;">remove</span><span style="color: black;">(</span> srcFilePath+<span style="color: #483d8b;">".finished"</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"File %s moved to %s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span> entry, <span style="color: #008000;">self</span>.<span style="color: black;">dirToMoveIn</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">break</span> <span style="color: #808080; font-style: italic;"># restart the while loop to avoid to continue the list of file we maybe have removed: ".finished"</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"Terminated!"</span>
<span style="color: #ff7700;font-weight:bold;">for</span> fileNotMoved <span style="color: #ff7700;font-weight:bold;">in</span> filesNotMoved :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"Already exists: Can't move %s to %s"</span> <span style="color: #66cc66;">%</span> <span style="color: black;">(</span> fileNotMoved, <span style="color: #008000;">self</span>.<span style="color: black;">dirToMoveIn</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> join<span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
<span style="color: #008000;">self</span>._terminate = <span style="color: #008000;">True</span>
<span style="color: #dc143c;">threading</span>.<span style="color: black;">Thread</span>.<span style="color: black;">join</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span></pre>
<p>As you can see (or not), everything happens in a thread.</p>
<p>It's used like this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> waitFinishAndCopy
myMoverThread = waitFinishAndCopy.<span style="color: black;">MoverThread</span><span style="color: black;">(</span><span style="color: #483d8b;">"/a/local/path/"</span>, <span style="color: #483d8b;">"/a/network/path/"</span>, <span style="color: #483d8b;">"^toto<span style="color: #000099; font-weight: bold;">\.</span>[0-9]{4}<span style="color: #000099; font-weight: bold;">\.</span>exr$"</span><span style="color: black;">)</span>
myMoverThread.<span style="color: black;">start</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># start rendering, do rendering, end rendering.</span>
myMoverThread.<span style="color: black;">join</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>And voila!</p>
<h3>Conclusion</h3>
<p>I hope this modest prototype will inspire you if you are experiencing delays on your network. :mechantCrash:</p>
<p>I also suggest to do some profiling on your core network applications, especially if they are used by many people. Their behavior is always interesting (and sometimes surprising).</p>
<p>Have a nice day!</p>
<p>Dorian</p>
<center>:marioCours:</center>
Quickly retrieve vertex positions of a Maya mesh (English Translation)urn:md5:5d032309b99261355833f3413e46f8432011-09-27T22:16:00+02:002013-07-26T18:05:41+02:00NarannScript et codeapienmayameshpythonvertexxform<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_03_30_Recuperer_Position_Vertex_Rapidement/recuperer_Position_Vertex_Rapidement_tn.png" alt="recuperer_Position_Vertex_Rapidement_tn.png" style="float:left; margin: 0 1em 1em 0;" title="recuperer_Position_Vertex_Rapidement_tn.png, mar. 2011" height="150" width="150" />If you ever had to recover vertex positions of a mesh object, you probably crash yourself against the <em>xform</em> command and his legendary slowness. :baffed:</p>
<p>In this post, I purpose a piece of Python script using the Python Maya API that allows you to faster recover the list of all vertex positions of an object.</p>
<p>Primarily didactic, these codes may interest peoples who want to look a little more deeply how to use the API while having a concrete application. :laClasse:</p> <h3>Using xform</h3>
<p>Here is the <a href="http://download.autodesk.com/us/maya/2011help/Commands/xform.html">xform</a> version of the script:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> getVtxPos<span style="color: black;">(</span> shapeNode <span style="color: black;">)</span> :
vtxWorldPosition = <span style="color: black;">[</span><span style="color: black;">]</span> <span style="color: #808080; font-style: italic;"># will contain positions un space of all object vertex</span>
vtxIndexList = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">getAttr</span><span style="color: black;">(</span> shapeNode+<span style="color: #483d8b;">".vrts"</span>, multiIndices=<span style="color: #008000;">True</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> vtxIndexList :
curPointPosition = <span style="color: #dc143c;">cmds</span>.<span style="color: black;">xform</span><span style="color: black;">(</span> <span style="color: #008000;">str</span><span style="color: black;">(</span>shapeNode<span style="color: black;">)</span>+<span style="color: #483d8b;">".pnts["</span>+<span style="color: #008000;">str</span><span style="color: black;">(</span>i<span style="color: black;">)</span>+<span style="color: #483d8b;">"]"</span>, query=<span style="color: #008000;">True</span>, translation=<span style="color: #008000;">True</span>, worldSpace=<span style="color: #008000;">True</span> <span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># [1.1269192869360154, 4.5408735275268555, 1.3387055339628269]</span>
vtxWorldPosition.<span style="color: black;">append</span><span style="color: black;">(</span> curPointPosition <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">return</span> vtxWorldPosition</pre>
<h3>With Maya API</h3>
<p>Alternative to xform.</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">def</span> particleFillSelection<span style="color: black;">(</span> <span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;"># get the active selection</span>
selection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSelectionList</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MGlobal</span>.<span style="color: black;">getActiveSelectionList</span><span style="color: black;">(</span> selection <span style="color: black;">)</span>
iterSel = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MItSelectionList</span><span style="color: black;">(</span>selection, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFn</span>.<span style="color: black;">kMesh</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># go througt selection</span>
<span style="color: #ff7700;font-weight:bold;">while</span> <span style="color: #ff7700;font-weight:bold;">not</span> iterSel.<span style="color: black;">isDone</span><span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #808080; font-style: italic;"># get dagPath</span>
dagPath = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MDagPath</span><span style="color: black;">(</span><span style="color: black;">)</span>
iterSel.<span style="color: black;">getDagPath</span><span style="color: black;">(</span> dagPath <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># create empty point array</span>
inMeshMPointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># create function set and get points in world space</span>
currentInMeshMFnMesh = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span>dagPath<span style="color: black;">)</span>
currentInMeshMFnMesh.<span style="color: black;">getPoints</span><span style="color: black;">(</span>inMeshMPointArray, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># put each point to a list</span>
pointList = <span style="color: black;">[</span><span style="color: black;">]</span>
<span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">(</span> inMeshMPointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span> :
pointList.<span style="color: black;">append</span><span style="color: black;">(</span> <span style="color: black;">[</span>inMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span><span style="color: black;">[</span><span style="color: #ff4500;">0</span><span style="color: black;">]</span>, inMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span><span style="color: black;">[</span><span style="color: #ff4500;">1</span><span style="color: black;">]</span>, inMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span><span style="color: black;">[</span><span style="color: #ff4500;">2</span><span style="color: black;">]</span><span style="color: black;">]</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">return</span> pointList</pre>
<blockquote><p>Notice: I thought that specifying the size of the list from the start would gain time but it changes nothing. It seems that <a href="http://stackoverflow.com/questions/311775/python-create-a-list-with-initial-capacity">Python handles it well</a>. So, I learned a new saying: <em>Premature optimization is the root of all evil</em>. :gniarkgniark:</p></blockquote>
<h3>Benchmarks</h3>
<p>Well, it's fine to say it's faster. But how much? :reflexionIntense:</p>
<p>Here are times I have with a 50x50 pSphere smoothed 4 (633602 vertices):</p>
<ul>
<li>With xform: 39 sec</li>
<li>With Python Maya API: 4 sec</li>
</ul>
<p>10x faster! This would give me almost want to code in C++, just to see results (which is suppose to be 10x faster than Python!)!!! :grenadelauncher:</p>
<h3>Conclusion</h3>
<p>Well now it's up to you to know when use it.</p>
<p>You can try to optimize <a href="https://www.fevrierdorian.com/blog/post/2011/03/14/Remplir-un-mesh-de-spheres-dans-Maya-La-methode-d-un-senior">Djelloul's algorythm</a> and let me know your results! :sourit:</p>
<p>Now, I couldn't do without the API if I have to retrieve vertex positions of a mesh. It's much faster. Even if it's a little more complicate, I admit, but when you have this code, you keep it and use it at the right time! :aupoil:</p>
<p>Hope you like this trick!</p>
<p>See you soon!</p>
<center>:marioCours:</center>
Project a mesh to another with Maya API (English Translation)urn:md5:0a8e2a12a431cfe2fc41231bce05af082011-07-31T20:28:00+02:002013-07-26T18:08:00+02:00NarannScript et codeapicodeenmayapythontranslationtuto<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_tn.png" alt="projection_mesh_api_tn.png" style="float:left; margin: 0 1em 1em 0;" title="projection_mesh_api_tn.png, fév. 2011" height="150" width="150" />This is the english translation of <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya">a tutorial</a> I've done that will allow you to project a mesh to another.</p>
<p>This is something that can be useful but most is pretty "fun" to do (some would say everything is relative :seSentCon: ) and it's a good way to learn fundamentals of matrice operations having a both simple and concrete example.</p>
<p>Amateur of the API, this tutorial is for you!</p> <center>:longBar:</center>
<blockquote><p>In advance, sorry for english. :baffed:</p></blockquote>
<p>Here is what we want to achieve:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_015.png" alt="projection_mesh_api_015.png" style="display:block; margin:0 auto;" title="projection_mesh_api_015.png, fév. 2011" height="378" width="511" /></p>
<p>A mesh projected on another.</p>
<p>And the scene from which we start:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_001.png" title="projection_mesh_api_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_001_m.jpg" alt="projection_mesh_api_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_001.png, fév. 2011" height="376" width="560" /></a></p>
<center>:longBar:</center>
<h5>Sommaire</h5>
<ul>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theory">Theory</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#startCode">Start code</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#attributesPreparation">Attributes preparation</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#prepareTheScene">Prepare the scene</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theComputeMethod">The compute method</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#matricesExplainedToCGArtists">Matrices explained to CG artists</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#backToTheCode">Back to the code!</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#goThroughEachVertexAndModifyIt">Go through each vertex and modify it</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#buildAMesh">Build a mesh</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#sourceCodeAndConclusion">Source code and conclusion</a></li>
<li><a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#doThisWithoutPython">Update: Do this without Python</a></li>
</ul>
<center>:longBar:</center>
<h5>Theory <a name="theory"></a></h5>
<p>Once again, we begin with theory.</p>
<p>In facts, you'll see it's pretty simple because the harder part (the intersection) will be handle through <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">an API call</a>:</p>
<pre class="python python"><span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span>.<span style="color: black;">closestIntersection</span><span style="color: black;">(</span> ... <span style="color: black;">)</span></pre>
<p>This <a href="https://secure.wikimedia.org/wikipedia/en/wiki/Method(computer_programming)">method</a> handle intersection of a ray (point+direction) on a mesh and return some infos (the most important: The position of the projected point).</p>
<p>Basically, we need three things:</p>
<ul>
<li>A point of origin (the one we want to project)</li>
<li>A direction (a vector)</li>
<li>A destination mesh</li>
</ul>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_002.png" title="projection_mesh_api_002.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_002_m.jpg" alt="projection_mesh_api_002.png" style="display:block; margin:0 auto;" title="projection_mesh_api_002.png, fév. 2011" height="410" width="560" /></a></p>
<p>The point of origin will be, of course, each vertex of the mesh to project (here, vertex numbers's are in yellow).</p>
<p>Direction will be the normal of the vertex to project (in green on the image. Okay, we do not see too much but put it a little good will damn! :cayMal: ).</p>
<p>And the destination mesh will obviously be the mesh that will receive the plan (in our case, a sphere).</p>
<p>So we recover, each time, a point and a normal (yellow crosses).</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_003.png" title="projection_mesh_api_003.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_003_m.jpg" alt="projection_mesh_api_003.png" style="display:block; margin:0 auto;" title="projection_mesh_api_003.png, fév. 2011" height="423" width="560" /></a></p>
<center><i>Forgive my "half-of-a-jpeg-dollar-moldy" diagram :pasClasse:</i></center>
<center>:longBar:</center>
<h5>Start code <a name="startCode"></a></h5>
<p>Here is the the base code:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
kPluginNodeTypeName = <span style="color: #483d8b;">"projectMesh"</span>
kPluginNodeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span> 0x80000 <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Node definition</span>
<span style="color: #ff7700;font-weight:bold;">class</span> projectMeshNode<span style="color: black;">(</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># constructor</span>
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #808080; font-style: italic;"># creator</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span> projectMeshNode<span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">return</span>
<span style="color: #808080; font-style: italic;"># initialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span> :
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span> kPluginNodeTypeName, kPluginNodeId, nodeCreator, nodeInitializer <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span>
<span style="color: #808080; font-style: italic;"># uninitialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span>:
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span> kPluginNodeId <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to unregister node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span></pre>
<p>If you already had wrote a Maya node in Python, this code shouldn't make you too afraid.</p>
<p>Quick to explain for others :baffed: .</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span></pre>
<p>Import the main modules.</p>
<ul>
<li>The <em>sys</em> module is used to create error message when Maya load/unload the plugin (see below).</li>
<li>The other two are used to call Maya API methods.</li>
</ul>
<p>Nothing complicated.</p>
<pre class="python python">kPluginNodeTypeName = <span style="color: #483d8b;">"projectMesh"</span>
kPluginNodeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span> 0x80000 <span style="color: black;">)</span></pre>
<ul>
<li><em>kPluginNodeTypeName</em> is a simple variable called below to give a name to you node type.</li>
<li><em>kPluginNodeId</em> is a value serving as an id for the node when it is written in binary files (mb). 0x80000 to 0xfffff are used by Maya samples. See (documentation|http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html] for more informations.</li>
</ul>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Node definition</span>
<span style="color: #ff7700;font-weight:bold;">class</span> projectMeshNode<span style="color: black;">(</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span> <span style="color: black;">)</span> :
<span style="color: #808080; font-style: italic;"># constructor</span>
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span> :
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span> <span style="color: #008000;">self</span> <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span></pre>
<p>Here, the class is an instance of the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_px_node.html">MPxNode</a> object which is himself a class done to create custom nodes (it's a fairly complex part I will not go througth in this tutorial as it deserves a full post :jdicajdirien: ).</p>
<p>The <em>__init__</em> method is the first executed when the class is created. It simply initialise the <em>MPxNode</em> class.</p>
<p>The <em>compute</em> method is the one we will work the most. It's a herited methode from <em>MPxNode</em>. The part of the code "which do something". :sourit:</p>
<p>If you're not familiar with classes and Python, this part of the code may seem complex, but don't worry, the only important part is the <em>compute</em> method.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># creator</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span> projectMeshNode<span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span></pre>
<p>A function, executed when initializing the plugin, which returns a pointer to the created class (and therefore, the node).</p>
<p>Python having no notion of pointer and Maya having need, in particular, to initialize the plugins, Autodesk has developed the OpenMayaMPx.asMPxPtr method (more informations <a href="http://autodesk.com/us/maya/2011help/files/Maya_Python_API_Using_the_Maya_Python_API.htm" hreflang="en">here</a>).</p>
<p>Once again, it's something basic, you put it, you do not think :bete: .</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">return</span></pre>
<p>This method is also called during the node creation and allows (among others) to initialize the node attributes. This will be the first one that we will fulfill. For now, it make nothing at all.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span> :
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span> kPluginNodeTypeName, kPluginNodeId, nodeCreator, nodeInitializer <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span>
<span style="color: #808080; font-style: italic;"># uninitialize the script plug-in</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span> mobject <span style="color: black;">)</span>:
mplugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span> mobject <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
mplugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span> kPluginNodeId <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to unregister node: %s<span style="color: #000099; font-weight: bold;">\n</span>"</span> <span style="color: #66cc66;">%</span> kPluginNodeTypeName <span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">raise</span></pre>
<p>Let me tell you, this is really a "copy-paste" I've done from examples. Basically, these functions are called when loading/unloading plugins. They are used to register/unregister the plugins from Maya sessions.</p>
<p>Their behavior is simple, I invite you to review this code snippet for yourself (it doesn't harm! :gniarkgniark: ).</p>
<p>At this point you should be able to create your python node and load it in Maya</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_004.png" alt="projection_mesh_api_004.png" style="display:block; margin:0 auto;" title="projection_mesh_api_004.png, fév. 2011" height="324" width="387" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_005.png" alt="projection_mesh_api_005.png" style="display:block; margin:0 auto;" title="projection_mesh_api_005.png, fév. 2011" height="465" width="476" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_006.png" alt="projection_mesh_api_006.png" style="display:block; margin:0 auto;" title="projection_mesh_api_006.png, fév. 2011" height="84" width="195" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_007.png" alt="projection_mesh_api_007.png" style="display:block; margin:0 auto;" title="projection_mesh_api_007.png, fév. 2011" height="75" width="433" /></p>
<p>And by creating it as follow:</p>
<pre class="mel mel">createNode projectMesh<span style="color: #339933;">;</span>
<span style="color: #666666; font-style: italic;">// Result: projectMesh1 //</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_008.png" alt="projection_mesh_api_008.png" style="display:block; margin:0 auto;" title="projection_mesh_api_008.png, fév. 2011" height="171" width="474" /></p>
<p>To the extent that there is no attribute, this node does absolutely nothing! :sourit:</p>
<center>:longBar:</center>
<h5>Attributes preparation <a name="attributesPreparation"></a></h5>
<p>As promised, we will start by the <em>nodeInitializer()</em> method which initialize the node attributes.</p>
<p>We will need three attributes:</p>
<ul>
<li>Two inputs: The mesh projecting his vertex and the mesh which recieve them.</li>
<li>An output: The output, projected mesh.</li>
</ul>
<p>Here we go! :grenadelauncher:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># initializer</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span> :
typedAttr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnTypedAttribute</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the input attributes</span>
projectMeshNode.<span style="color: black;">inputMeshSrc</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshSrc"</span>, <span style="color: #483d8b;">"inMeshSrc"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">inputMeshTarget</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshTarget"</span>, <span style="color: #483d8b;">"inMeshTrg"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the output attributes</span>
projectMeshNode.<span style="color: black;">outputMesh</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"outputMesh"</span>, <span style="color: #483d8b;">"outMesh"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setWritable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
typedAttr.<span style="color: black;">setStorable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Add the attributes to the node</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Set the attribute dependencies</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>The first line:</p>
<pre class="python python">typedAttr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnTypedAttribute</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Creates an "object" (known in the Maya API as "<a href="http://download.autodesk.com/us/maya/2009help/API/group___m_fn.html">Function Set</a>") that will help us to manipulate the attributes (especially build them):</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Setup the input attributes</span>
projectMeshNode.<span style="color: black;">inputMeshSrc</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshSrc"</span>, <span style="color: #483d8b;">"inMeshSrc"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">inputMeshTarget</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"inputMeshTarget"</span>, <span style="color: #483d8b;">"inMeshTrg"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setReadable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># Setup the output attributes</span>
projectMeshNode.<span style="color: black;">outputMesh</span> = typedAttr.<span style="color: black;">create</span><span style="color: black;">(</span> <span style="color: #483d8b;">"outputMesh"</span>, <span style="color: #483d8b;">"outMesh"</span>, <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: black;">)</span>
typedAttr.<span style="color: black;">setWritable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span>
typedAttr.<span style="color: black;">setStorable</span><span style="color: black;">(</span><span style="color: #008000;">False</span><span style="color: black;">)</span></pre>
<p>We create attributes. Nothing complicated (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_typed_attribute.html#fc105a88372e84910bc56c74c973bc26">see documentation</a>).</p>
<p>Some details on used arguments:</p>
<ul>
<li>The full attribute name (long name).</li>
<li>The short attribute name (short name).</li>
<li>The attribute "type" (as <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_type_id.html">API type</a>).</li>
</ul>
<p>The following methods each attribute declaration (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#ced78c59e2a6b0395810705bb2896bfb">setReadable</a>, <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#5b89d32b1d6beebfa7b21a58d1d8d6f2">setWritable</a>, <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html#b0fe1b4159b4f4bcefd7c0cef9a7b389">setStorable</a>) add special things to the latest created attribute (<a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_attribute.html">see documentation</a> for more precisions).</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Add the attributes to the node</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">addAttribute</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>As the comment said, this part add/connect attributes created above to the node.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Set the attribute dependencies</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshSrc</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
projectMeshNode.<span style="color: black;">attributeAffects</span><span style="color: black;">(</span> projectMeshNode.<span style="color: black;">inputMeshTarget</span>, projectMeshNode.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span></pre>
<p>This part is very important! :papi:</p>
<p>It allows you to define "dependencies" between attributes.</p>
<p>In our case:</p>
<ul>
<li>If the <em>inputMeshSrc</em> attribute change, <em>outputMesh</em> attribute also change.</li>
<li>If the <em>inputMeshTarget</em> attribute change, <em>outputMesh</em> attribute also change.</li>
</ul>
<p>If these lines are not set, the <em>compute</em> method of the node we are creating will never be launched. The node will never be updated.</p>
<p>You can download the python node in the current state here:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projectMesh001.7z">>> projectMesh001.7z <<</a></p>
<center>:longBar:</center>
<h5>Prepare the scene <a name="prepareTheScene"></a></h5>
<p>Before we actually code the behavior of the node, we need to connect some geometry to our future node.</p>
<p>Create a scene that looks like this:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_001.png" title="projection_mesh_api_001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.projection_mesh_api_001_m.jpg" alt="projection_mesh_api_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_001.png, fév. 2011" height="376" width="560" /></a></p>
<p>Also create a third mesh, which will return the geometry of our future node (which will be the projected mesh).</p>
<p>In my case: A pSphere. But it can be anything. As it is a mesh node.</p>
<p>You can even create the mesh node by hand.</p>
<p>Place it to the center of the scene (0,0,0) so there have no offset between the projected mesh and his target.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_009.png" alt="projection_mesh_api_009.png" style="display:block; margin:0 auto;" title="projection_mesh_api_009.png, fév. 2011" height="549" width="733" /></p>
<p>To avoid having to create the connections each time, here are two small MEL codes that help you to test your node (your nodes must be well named).</p>
<p>To load all:</p>
<pre class="mel mel">loadPlugin<span style="color: #009900;">(</span> <span style="color: #ff0000;">"monRepertoire/projectMesh.py"</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span>
createNode projectMesh<span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f projectMesh1.<span style="color: #202020;">outputMesh</span> pSphereShape2.<span style="color: #202020;">inMesh</span><span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f pPlaneShape1.<span style="color: #202020;">worldMesh</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> projectMesh1.<span style="color: #202020;">inputMeshSrc</span><span style="color: #339933;">;</span>
connectAttr <span style="color: #339933;">-</span>f pSphereShape1.<span style="color: #202020;">worldMesh</span><span style="color: #009900;">[</span><span style="color: #0000dd;">0</span><span style="color: #009900;">]</span> projectMesh1.<span style="color: #202020;">inputMeshTarget</span><span style="color: #339933;">;</span></pre>
<p>And unload all:</p>
<pre class="mel mel">delete projectMesh1<span style="color: #339933;">;</span>
flushUndo<span style="color: #339933;">;</span>
unloadPlugin<span style="color: #009900;">(</span> <span style="color: #ff0000;">"projectMesh"</span> <span style="color: #009900;">)</span><span style="color: #339933;">;</span></pre>
<p>And that's it! Now take a deep breath, we jump!</p>
<center>:longBar:</center>
<h5>The compute method <a name="theComputeMethod"></a></h5>
<p>The first thing to test is the presence of a connection on your outputMesh attribute. Actually, if your node is connected to anything, it should not be calculated:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> compute<span style="color: black;">(</span> <span style="color: #008000;">self</span>, plug, data <span style="color: black;">)</span> :
<span style="color: #ff7700;font-weight:bold;">if</span> plug == <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span>:
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"compute"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kUnknownParameter</span>
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span></pre>
<p>Once we are sure connections are made, we get input attributes:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> plug == <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span>:
<span style="color: #808080; font-style: italic;"># get the inputMeshTarget (return MDataHandle)</span>
inMeshSrcHandle = data.<span style="color: black;">inputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">inputMeshSrc</span> <span style="color: black;">)</span>
inMeshTargetHandle = data.<span style="color: black;">inputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">inputMeshTarget</span> <span style="color: black;">)</span></pre>
<p>This is a "connection" to the block of data attributes. This is the first step to get the value (here it's a kMesh so it will be a little different) of an attribute.</p>
<blockquote><p>Python being an untyped language (both its main strength but also its main flaw ...), I tend to write in comments the Maya API type of data that I get.</p>
<p>
Otherwise, it's quick to not knowing at all what type is what variable (types in the Maya API is no lack :aupoil: ).</p>
<p>
However, everyone has their own method! If you have a over-developed cortex, if you want to play the "I dont care types are for noobs", if a code that you are the only person who can read it give you the feeling to be a strong male and if you want to justify your BAC +5 (in this day and age it's certainly not your salary that should do it). Do not hesitate, code as <del>TD</del> pork: No comments, one letter variables and whatnot like that... Your colleagues will return the favor. :sourit:</p>
<p>
But if you're more modest and would quickly learn the Maya API, I urge you to write Maya API types directly in comments of your code. Besides being clearer, it still requires to known/search, when writing variables, what type it is.</p></blockquote>
<p>After that, we check our two connected attributes are meshs:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#we check the API type we've got here (we need kMesh)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> inMeshSrcHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: #ff7700;font-weight:bold;">and</span> inMeshTargetHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> :
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"cool"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kInvalidParameter</span></pre>
<p>And we get them as such:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#we check the API type we've got here (we need kMesh)</span>
<span style="color: #ff7700;font-weight:bold;">if</span> inMeshSrcHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> <span style="color: #ff7700;font-weight:bold;">and</span> inMeshTargetHandle.<span style="color: #008000;">type</span><span style="color: black;">(</span><span style="color: black;">)</span> == <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnData</span>.<span style="color: black;">kMesh</span> :
<span style="color: #808080; font-style: italic;"># return a MObject</span>
meshSrc = inMeshSrcHandle.<span style="color: black;">asMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
meshTarget = inMeshTargetHandle.<span style="color: black;">asMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">"cool"</span>
<span style="color: #ff7700;font-weight:bold;">else</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kInvalidParameter</span></pre>
<p>I invite you to look at the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> documentation just to see what we can get from an attribute.</p>
<p>As mentioned in the comment, we get a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_object.html">MObject</a>. This object type is the "all-purpose-type" of Maya.</p>
<p>This MObject is just a transitionnal object. Actually, it is rarely used directly.</p>
<p>In Maya, to modify/malipulate objects, we often use <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_base.html">"Function Set"</a>. They follow the pattern: MFn*Type*.</p>
<p>Here, to manipulate meshs, we will get a function set of mesh: <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html">MFnMesh</a>.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># get the MFnMesh of the twice attr</span>
mFnMeshSrc = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> meshSrc <span style="color: black;">)</span>
mFnMeshTarget = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span> meshTarget <span style="color: black;">)</span></pre>
<p>What interests us now is to have a list of all vertices of the "source mesh" (one which will be projected to the "target mesh") to create another array that will contain changed vertex positions:</p>
<pre class="python python">outMeshMPointArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPointArray</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># create an array of vertex wich will contain the outputMesh vertex</span>
mFnMeshSrc.<span style="color: black;">getPoints</span><span style="color: black;">(</span>outMeshMPointArray<span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># get the point in the space</span></pre>
<p>The first line creates an array of type <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point_array.html">MPointArray</a>.</p>
<p>The second line fills it with the values of the "source mesh" vertex.</p>
<p>How to write is a bit confusing ("upside down" will say some :reflechi: ) but that's the way <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#54dc2a4415a365c7193936dad6643b47">getPoint</a> works, as many other functions of the Maya API.</p>
<p>Rather than return the result, it is stored in the variable given as argument.</p>
<p>We now have a MPointArray filled with his vertex positions.</p>
<p>The idea now is to change vertex positions to make them matching the projected position on the "target mesh".</p>
<p>But here... The vertex positions that you get in your MPointArray are in "object space". That mean, relative to the center of the object.</p>
<p>Nous allons nous heurter à un vrai problème. The big one! The ultimate: The matrices! *Voix qui résonne* :enerve:
We will face a real problem. The big one! The ultimate: The Matrix! *Voice echoing* :enerve:</p>
<center>:longBar:</center>
<h5>Matrices explained to CG artists <a name="matricesExplainedToCGArtists"></a></h5>
<p>When you are CG artists, we hear about it without really knowing what it is :bete: .</p>
<p>Add to that what we find on the net is very academic and "too mathematical" (It shows how to multiply a matrix without explaining why it is necessary to do so).</p>
<p>All this so that we don't necessarily see the connection with our work.</p>
<p>I'll modestly try to explain this from a "CG artist" point of view :mayaProf: .</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/photoMatrix.jpg" title="photoMatrix.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.photoMatrix_m.jpg" alt="photoMatrix.jpg" style="display:block; margin:0 auto;" title="photoMatrix.jpg, fév. 2011" height="416" width="560" /></a></p>
<center><small><i><a href="http://www.flickr.com/photos/trinity-of-one/20562069/">Photography</a> by <a href="http://www.flickr.com/photos/trinity-of-one/">My Melting Brain</a> under Creative Common <a href="http://creativecommons.org/licenses/by-nc-sa/2.0/deed.fr">by-nc-sa</a>. Thanks to him! :sourit:</i></small></center>
<p>Create a cube.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_010.png" alt="projection_mesh_api_010.png" style="display:block; margin:0 auto;" title="projection_mesh_api_010.png, fév. 2011" height="284" width="463" /></p>
<p>You may have noticed, once your cube is created, that it has a "pivot point" with informations (position, rotation, scale, etc. ...). Well this point can be a "mathematical link" between the vertex points of your cube and "the world" (center coordinates of the "world" are 0,0,0).</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_011.png" alt="projection_mesh_api_011.png" style="display:block; margin:0 auto;" title="projection_mesh_api_011.png, fév. 2011" height="291" width="404" /></p>
<p>If this "transform" node didn't exist, your object would be at center of the scene. And to move it, it should move the all vertex positions of the cube.</p>
<p>The transform node acts like a "parent" of the vertex of your cube. In this way, the vertex positions of the cube doesn't move. For example, a vertex of a cube placed at 1,1,1 (relative to the pivot of the cube so) will remain at 1,1,1 regardless of the position of the transform.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_012.png" alt="projection_mesh_api_012.png" style="display:block; margin:0 auto;" title="projection_mesh_api_012.png, fév. 2011" height="246" width="305" /></p>
<center><i>Here, we move the pivot, not the cube vertices's that they don't change position relative to the pivot, the pivot that changes position relative to the world.</i></center>
<p>But some operations (in our case, get if the vertex is face to a different mesh) requires that the coordinates of all the entities are a common reference, the reference world.</p>
<p>Comic demonstration:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_001.png" alt="projection_mesh_api_bd_001.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_001.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Hi! I am vertex[0] and I'm on (1,1,1).</br>
- Hi! I am vertex[0] and I'm on (1,1,1).</br>
Two vertices at different coordinates in "world space"...</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_002.png" alt="projection_mesh_api_bd_002.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_002.png, fév. 2011" height="340" width="553" /></p>
<center><i>...but at the same place relative to their respective centers.</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_003.png" alt="projection_mesh_api_bd_003.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_003.png, fév. 2011" height="340" width="553" /></p>
<center><i>- (insult)</br>
- (insult)</br>
Not easy to make calculations.</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_004.png" alt="projection_mesh_api_bd_004.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_004.png, fév. 2011" height="340" width="553" /></p>
<center><i>You see the prob?</i></center>
<p>Whereas if we chose, as common reference point, the center of the world.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_005.png" alt="projection_mesh_api_bd_005.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_005.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Hi! I am vertex[0] and I'm on (4,2,3) relative to world.</br>
- Hi! I am vertex[0] and I'm on (3,1,2) relative to world.</i></center>
<p>It's much easier.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_006.png" alt="projection_mesh_api_bd_006.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_006.png, fév. 2011" height="340" width="553" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_007.png" alt="projection_mesh_api_bd_007.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_007.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Friend!</br>
- Friend!</i></center>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_008.png" alt="projection_mesh_api_bd_008.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_008.png, fév. 2011" height="340" width="553" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/bd/projection_mesh_api_bd_009.png" alt="projection_mesh_api_bd_009.png" style="display:block; margin:0 auto;" title="projection_mesh_api_bd_009.png, fév. 2011" height="340" width="553" /></p>
<center><i>- Nevertheless, I am still higher than you!</br>
- Whait... What? o_O</i></center>
<p>Okay, now we know the vertex coordinates in their "object space", we must know how get them in "world space".</p>
<p>The basic principle that immediately comes to mind is: We add vertex positions (in object space) to its pivot point (him, in world space).</p>
<blockquote><p>Example:</p>
<p>
If pVertex the position of a vertex and cubePosition the position, in world space, of the pivot point of the cube:</p>
<p>
cubePositionX + pVertexInCubeX = pVertexInWorldX</p></blockquote>
<p>But surely you can imagine, it's more complicated... :siffle:</p>
<p>Indeed, in the case of rotations and scale, it is not enough of a few additions to solve the problem.</p>
<blockquote><p>Note: Whether a translation, a rotation, or scaling a mesh, all comes down to vertex moves in the world.</p></blockquote>
<p>The truth is that all this "parameters" can be put into a single "object" that we call a matrix. This matrix will help us to make calculations (as Maya saves us but if you're interested, here is an example of <a href="http://jeux.developpez.com/faq/math/?page=bases#Q4">calculates a rotation matrix</a>) to get vertex positions in world space.</p>
<p>As I said, Maya gives us easely access to this object through <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> (There are several matrix types, we will focus on this one).</p>
<p>So at this stage, we have two things:</p>
<ul>
<li>Vertex positions relative to the object (in "object space").</li>
<li>A matrix of the object (which is world relative, "world space").</li>
</ul>
<p>We need to "convert" vertex positions from "space object" to "world space". A "change of reference" (sorry guys, don't know the english word for that :pasClasse: ). So you get an absolute position ("world space").</p>
<p>And to get vertex position in the "world space", "simply" multiply vertex position matrix {x, y, z} by inclusion matrix of the object. (I wrote "simply" in quotes because multiplying a matrix is not as simple as 2x2... :redface: )</p>
<blockquote><p>Note: I would not do demonstration on "how to calculate a matrix", internet is full of examples and explanations.</p></blockquote>
<p>And here's the formula:</p>
<pre>
positionGlobale = positionLocale * inclusiveMatrix
</pre>
<p>It's almost like the Pythagorean theorem. Who cares how it works, until you know when to use it! :baffed:</p>
<p>Voila! I hope this little explanation you will shed some light on why the matrices are. :sourit:</p>
<center>:longBar:</center>
<h5>Back to the code! <a name="backToTheCode"></a></h5>
<p>Get inclusive matrix is simple.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># get MDagPath of the MMesh to get the matrix and multiply vertex to it. If I don't do that, all combined mesh will go to the origin</span>
inMeshSrcMDagPath = mFnMeshSrc.<span style="color: black;">dagPath</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># return MDagPath object</span>
inMeshSrcInclusiveMMatrix = inMeshSrcMDagPath.<span style="color: black;">inclusiveMatrix</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># return MMatrix</span></pre>
<p>The first line retrieves the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html">dagPath</a> of the source mesh. We can consider the dagPath as the equivalent of the transform node of an object in Maya. Where all the informations about transformations (positions, rotations, scales, etc ...) are stored.</p>
<p>The second line retrieves the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_dag_path.html#7cd82c63d68ff186865b3a2c9340a5ae">inclusiveMatrix</a> of the source mesh as a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_matrix.html">MMatrix</a> type.</p>
<p>Now we can go through each vertex:</p>
<ul>
<li><a href="http://download.autodesk.com/us/maya/2011help/API/class_m_point.html#1fdf65c85cf27f6ce4ddef0a72aac5d3">Multiply it by the matrix</a> to get his position in world space.</li>
<li>Get his normal.</li>
<li>Also <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_vector.html#d22edbae3de3e0742534acff6b4f6227">multiply it</a> bye the matrix.</li>
<li>Get the collision point, store it as the current point.</li>
</ul>
<center>:longBar:</center>
<h5>Go through each vertex and modify it <a name="goThroughEachVertexAndModifyIt"></a></h5>
<p>The beginning of the main loop looks like this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">(</span> outMeshMPointArray.<span style="color: black;">length</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: black;">)</span> :
inMeshMPointTmp = outMeshMPointArray<span style="color: black;">[</span>i<span style="color: black;">]</span> <span style="color: #66cc66;">*</span> inMeshSrcInclusiveMMatrix <span style="color: #808080; font-style: italic;"># the MPoint of the meshSrc in the worldspace</span></pre>
<p>The loop is quite simple: "i" is incremented by 1 each "lap" to browse the vertex array (MPointArray).</p>
<p>The first thing we do is point multiplication (<em>outMeshMPointArray[i]</em>) by the matrix (<em>inMeshSrcInclusiveMMatrix</em>) to obtain a vertex (<em>inMesgPointTmp</em>) in world space ( with coordinates relatives to the world, 0,0,0).</p>
<p>Now we've (finally) the vertex positioned relative to the world, we will "intersect" it with the target mesh.</p>
<blockquote><p>As chrysl666 told <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#c3024">in his comment</a>, Maya give a simple way to get informations without have to extract matrix and multiply it yourself. Just call your points (and others stuffs) with:</p>
<p>
getPoints(OpenMaya.MSpace.kWorld).</p>
<p>
This will return points position directly in world space. Thanks to him for this information. :bravo:</p></blockquote>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/intersection.jpg" title="intersection.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/.intersection_m.jpg" alt="intersection.jpg" style="display:block; margin:0 auto;" title="intersection.jpg, fév. 2011" height="420" width="560" /></a></p>
<center><small><i><a href="http://www.flickr.com/photos/mynamemattersnot/2470352700/">Photography</a> by <a href="http://www.flickr.com/photos/mynamemattersnot/">MyNameMattersNot</a> under Creative Common <a href="http://creativecommons.org/licenses/by-sa/2.0/deed.fr">by-sa</a>. Thanks to him!</i></small></center>
<p>Well, go look at arguments of the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">OpenMaya.MFnMesh.closestIntersection()</a> method we <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theorie">seen above</a>.
Bon, allez regarder les arguments de la méthode <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#bbb75ee7d06620f430a660ad0017f909">OpenMaya.MFnMesh.closestIntersection()</a> que <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#theory">nous avont vu plus haut</a>.</p>
<p>You see there are quite a few. :sourit:</p>
<p>Don't worry, we can ignore many of them. What interests us is the original vertex, his direction (in our case: normal) and the collision point (the hitPoints).</p>
<p>But more subtle, look at the type of the first argument (raySource) expected by the method.</p>
<p>It's a MFloatPoint!</p>
<p>But how convert <em>inMeshMPointTmp</em> (a MPoint) to a MFloatPoint? It's pretty easy in C++, you have to use doubles. After much research, I found a solution. I give it to you:</p>
<pre class="python python">raySource = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span> inMeshMPointTmp.<span style="color: black;">x</span>, inMeshMPointTmp.<span style="color: black;">y</span>, inMeshMPointTmp.<span style="color: black;">z</span> <span style="color: black;">)</span></pre>
<p>It's not as complicated but if you don't know...</p>
<p>So we have our raySource.</p>
<p>Now the normal:</p>
<pre class="python python">rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MVector</span><span style="color: black;">(</span><span style="color: black;">)</span>
mFnMeshSrc.<span style="color: black;">getVertexNormal</span><span style="color: black;">(</span> i, <span style="color: #008000;">False</span>, rayDirection<span style="color: black;">)</span>
rayDirection <span style="color: #66cc66;">*</span>= inMeshSrcInclusiveMMatrix
rayDirection = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatVector</span><span style="color: black;">(</span>rayDirection.<span style="color: black;">x</span>, rayDirection.<span style="color: black;">y</span>, rayDirection.<span style="color: black;">z</span><span style="color: black;">)</span></pre>
<p>At this point, you should understand:</p>
<ul>
<li>We create the MVector.</li>
<li>We store the current vertex (i) normal of the source mesh in it.</li>
<li>We multiply by the matrix ot get this vector in space world.</li>
<li>We "convert" it to MFloatVector.</li>
</ul>
<p>The hitPoint is a simple MFloatPoint:</p>
<pre class="python python">hitPoint = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFloatPoint</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>And the remaining arguments are:</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># rest of the args</span>
hitFacePtr = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MScriptUtil</span><span style="color: black;">(</span><span style="color: black;">)</span>.<span style="color: black;">asIntPtr</span><span style="color: black;">(</span><span style="color: black;">)</span>
idsSorted = <span style="color: #008000;">False</span>
testBothDirections = <span style="color: #008000;">False</span>
faceIds = <span style="color: #008000;">None</span>
triIds = <span style="color: #008000;">None</span>
accelParams = <span style="color: #008000;">None</span>
hitRayParam = <span style="color: #008000;">None</span>
hitTriangle = <span style="color: #008000;">None</span>
hitBary1 = <span style="color: #008000;">None</span>
hitBary2 = <span style="color: #008000;">None</span>
maxParamPtr = <span style="color: #ff4500;">99999999</span>
<span style="color: #808080; font-style: italic;"># http://zoomy.net/2009/07/31/fastidious-python-shrub/</span>
hit = mFnMeshTarget.<span style="color: black;">closestIntersection</span><span style="color: black;">(</span>raySource,
rayDirection,
faceIds,
triIds,
idsSorted,
<span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MSpace</span>.<span style="color: black;">kWorld</span>,
maxParamPtr,
testBothDirections,
accelParams,
hitPoint,
hitRayParam,
hitFacePtr,
hitTriangle,
hitBary1,
hitBary2<span style="color: black;">)</span></pre>
<p>Great thanks to <a href="http://zoomy.net/about/">Peter J. Richardson</a>! Sans <a href="http://zoomy.net/2009/07/31/fastidious-python-shrub/">son billet</a>, I would never have succeeded to code this stuff. This is why we need to "share what you know" on the internet! ;)</p>
<p>Once called <em>closestIntersection</em>, you get a hitPoint in MFloatPoint that you convert into MPoint:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> hit :
inMeshMPointTmp = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MPoint</span><span style="color: black;">(</span> hitPoint.<span style="color: black;">x</span>, hitPoint.<span style="color: black;">y</span>, hitPoint.<span style="color: black;">z</span><span style="color: black;">)</span></pre>
<p>We replaces the current point by our new one:</p>
<pre class="python python">outMeshMPointArray.<span style="color: #008000;">set</span><span style="color: black;">(</span> inMeshMPointTmp, i <span style="color: black;">)</span></pre>
<p>And this is the end of the loop! :D</p>
<p>At this point you've got a MPointArray <em>outMeshMPointArray</em> with values of projected vertices on the target mesh.</p>
<p>So now we need to rebuild the mesh.</p>
<center>:longBar:</center>
<h5>Build a mesh <a name="buildAMesh"></a></h5>
<p>I will not go througt details on "how to create and arrange the variables in the case of the creation of a mesh".</p>
<p>Basically we need:</p>
<ul>
<li>The number of vertices.</li>
<li>Le number of ploygons (polygons + triangles if there is).</li>
<li>A array of points (All vertices one behind others with they XYZ coordinates).</li>
<li>An array listing the number of vertices by polygons (Example: 4,4,4,4,3,4,3,4,3,4,4,etc...).</li>
<li>An array of vertex indices (Example: 1,2,3,4,3,4,5,6,5,6,7,8, etc...).</li>
</ul>
<p>You will have understood, we already have the points array (the third point). Otherwise, we silly gets values from the original mesh.</p>
<p>Let's start! :hehe:</p>
<p>At first, we must create a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh_data.html">MFnMeshData</a> function set.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># create a mesh that we will feed!</span>
newDataCreator = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMeshData</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>This function set will allow us to create a MObject that we can "fill" with data of the future mesh.</p>
<pre class="python python">newOutputData = newDataCreator.<span style="color: black;">create</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># Return MObject</span></pre>
<p>As I said above: We need to get all informations (except the vertex array) of the source mesh:</p>
<pre class="python python">outMeshNumVtx = mFnMeshSrc.<span style="color: black;">numVertices</span><span style="color: black;">(</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;"># outputMesh will have the same number of vtx and polygons</span>
outMeshNumPolygons = mFnMeshSrc.<span style="color: black;">numPolygons</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;"># create two array and feed them</span>
outMeshPolygonCountArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MIntArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
outMeshVtxArray = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MIntArray</span><span style="color: black;">(</span><span style="color: black;">)</span>
mFnMeshSrc.<span style="color: black;">getVertices</span><span style="color: black;">(</span>outMeshPolygonCountArray, outMeshVtxArray<span style="color: black;">)</span></pre>
<p><a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#5f014e7c511cf86978a765a932ce04d0">mFnMeshSrc.getVertices()</a> filled two arrays with informations needed to create the mesh. See <a href="https://www.fevrierdorian.com/blog/post/2011/07/31/Project-a-mesh-to-another-with-Maya-API-%28English-Translation%29#construire_un_mesh">above</a>.</p>
<p>Once we have that, we create the mesh:</p>
<pre class="python python">meshFS = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MFnMesh</span><span style="color: black;">(</span><span style="color: black;">)</span>
meshFS.<span style="color: black;">create</span><span style="color: black;">(</span>outMeshNumVtx, outMeshNumPolygons, outMeshMPointArray, outMeshPolygonCountArray, outMeshVtxArray, newOutputData<span style="color: black;">)</span></pre>
<p>The principle is quite simple:</p>
<ul>
<li>We create a <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html">MFnMesh</a> function set.</li>
<li>We create the mesh giving all arguments re le mesh en donnant tout les arguments (collected above) via <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_fn_mesh.html#6994886ddf6ea46baf399ff40d30bc32">meshFS.create()</a>.</li>
</ul>
<p>Once the mesh is created, we get the <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html">MDataHandle</a> from the <em>outputMesh</em> connection to put the MObject we just fill <a href="http://download.autodesk.com/us/maya/2011help/API/class_m_data_handle.html#959379b4d93358519ccc99696031756c">in it</a>: The mesh!</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># Store them on the output plugs</span>
outputMeshHandle = data.<span style="color: black;">outputValue</span><span style="color: black;">(</span> <span style="color: #008000;">self</span>.<span style="color: black;">outputMesh</span> <span style="color: black;">)</span>
outputMeshHandle.<span style="color: black;">setMObject</span><span style="color: black;">(</span> newOutputData <span style="color: black;">)</span></pre>
<p>Once this is done, we say, via <a href="http://download.autodesk.com/us/maya/2010help/api/class_m_data_block.html#0d8faafb64e70cf0a579532bb033c98f">MDataBlock.setClean()</a>, to the dependency graph that the connection has been updated.</p>
<pre class="python python"><span style="color: #808080; font-style: italic;"># tell to the dependency graph the connection is clean</span>
data.<span style="color: black;">setClean</span><span style="color: black;">(</span> plug <span style="color: black;">)</span></pre>
<p>It's over! :youplaBoum:</p>
<p>If you well followed the tutorial (and if I did not make errors ( :baffed: ), you should have a node that works correctly (place the plan so that it "target" the sphere):</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_013.png" alt="projection_mesh_api_013.png" style="display:block; margin:0 auto;" title="projection_mesh_api_013.png, fév. 2011" height="390" width="635" /></p>
<p>Of course, if vertices aren't projected on the sphere, it return to their original positions:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_014.png" alt="projection_mesh_api_014.png" style="display:block; margin:0 auto;" title="projection_mesh_api_014.png, fév. 2011" height="406" width="487" /></p>
<center>:longBar:</center>
<h5>Source code and conclusion <a name="sourceCodeAndConclusion"></a></h5>
<p>At this point, you should have understood the principle of matrices (If this is not the case, don't hesitate to go deeply, you will see 3D in a different way! :sourit: ), be able to recover components of a mesh and create a mesh from scratch.</p>
<p>There is the source code: <a href="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projectMesh.7z">projectMesh.7z</a></p>
<p>And voila! I just finished another big tutorial. I hope it has been informative and that you can begin to do interesting things with Maya API. :banaeyouhou:</p>
<p>Such informations is lacking on the Internet on CG web site.</p>
<p>I encourage all seniors who would pass by and who would learn something interesting reading this post to do not hesitate to "give back": If you are competent in a field, share! I did. :hihi:</p>
<p>If I'm wrong somewhere, if a point does not seem clear or if there is an error, please tell me. :pasClasse:</p>
<p>See you soon!</p>
<center>:marioCours:</center>
<blockquote><p>Note: As you could read, my english is far from being perfect. Don't hesitate to leave a comment if you found a better way to explain some stuff.</p></blockquote>
<center>:longBar:</center>
<h5>Update: Do this without Python <a name="doThisWithoutPython"></a></h5>
<p>Well, it's not really the goal but since Kel Solar talk about <a href="https://www.fevrierdorian.com/blog/post/2011/02/20/Projeter-un-mesh-sur-un-autre-avec-l-API-Python-de-Maya#c205">in comments</a> I also give you a way to do this using the built in Maya. :seSentCon:</p>
<p>Select the target mesh:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_016.png" alt="projection_mesh_api_016.png" style="display:block; margin:0 auto;" title="projection_mesh_api_016.png, fév. 2011" height="453" width="603" /></p>
<p>Select the source mesh:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_017.png" alt="projection_mesh_api_017.png" style="display:block; margin:0 auto;" title="projection_mesh_api_017.png, fév. 2011" height="409" width="554" /></p>
<p>Open transfert attributes options:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_018.png" alt="projection_mesh_api_018.png" style="display:block; margin:0 auto;" title="projection_mesh_api_018.png, fév. 2011" height="232" width="259" /></p>
<p>Set options as follow:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_019.png" alt="projection_mesh_api_019.png" style="display:block; margin:0 auto;" title="projection_mesh_api_019.png, fév. 2011" height="359" width="551" /></p>
<p>Basically, we only transfer the vertex positions in world space by projecting them along their normal.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_02_05_projection_mesh_api/projection_mesh_api_020.png" alt="projection_mesh_api_020.png" style="display:block; margin:0 auto;" title="projection_mesh_api_020.png, fév. 2011" height="399" width="622" /></p>
<p>And that's it!</p>
<p>You can rotate the source mesh and it's updated! :laClasse:</p>
<p>Voila! That way, people who came to find a quick solution will not be frustrated! :sourit:</p>Tales of the Night, summary of a technical supervisionurn:md5:9ae2161d6ae68c514e18540d668a0aed2011-07-21T16:41:00+02:002013-07-26T18:07:43+02:00NarannInfographie 3D - Boulotcgfxenocelotpost-mortemsupervisingtales of the night<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/lcdln_resume_superviseur_tn.png" alt="lcdln_resume_superviseur_tn.png" style="float:left; margin: 0 1em 1em 0;" title="lcdln_resume_superviseur_tn.png, juil. 2011" height="150" width="150" />This is an english traduction of <a href="https://www.fevrierdorian.com/blog/post/2011/07/20/Les-Contes-de-la-Nuit-resume-d-une-supervision-technique">a post</a> I've made some time ago.</p>
<p>As you maybe know, the feature film <a href="http://www.imdb.com/title/tt1828229/" hreflang="en">Tales of the Night</a> (<em>Les Contes de la Nuit</em> in french) from <a href="http://www.imdb.com/name/nm0643664/" hreflang="en">Michel Ocelot</a> (<a href="http://www.imdb.com/title/tt0181627/" hreflang="en">Kirikou</a>, <a href="http://www.imdb.com/title/tt0439123/" hreflang="en">Azur et Asmar</a> and others) has been released in August.</p>
<p>I was fortunate to supervise the technical aspects of production. Beyond that I think this film is a real artistic success, it was definitely one of my most beautiful experience. :sourit:</p>
<p>In this post I purpose to go around the means in place to achieve that beautiful, what do I say? Wonderfull project!</p>
<p>(And do little advertising too :siffle: )</p> <h3>Note</h3>
<blockquote><p>Although the content of this blog is licensed under a Creative Common, all images on this post are the property of their respective authors. Violators may be subject to prosecution and could be kidnapped in their sleep, stoned in the public place and deprived of candies. :gniarkgniark:</p></blockquote>
<h3>How I arrived on the project</h3>
<p>First, replacing the context seems to me interesting.</p>
<p>All start in the end of 2009, I'm working in <a href="http://www.def2shoot.com/" hreflang="en">Def2Shoot</a> the animated feature <a href="http://www.imdb.com/title/tt1039651/" hreflang="en">Yona Yona</a> (old project name). Following several french-Japanese production meetings, the choice to do animation of the main characters in Maya and sets in XSI is made. I find myself being the only TD in Maya (D2S is on XSI).</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/yona.jpg" title="yona.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/.yona_m.jpg" alt="yona.jpg" style="display:block; margin:0 auto;" title="yona.jpg, juil. 2011" height="560" width="411" /></a></p>
<p>Franck Malmin (<a href="http://www.excessif.com/cinema/actu-cinema/dossiers/la-naissance-de-yona-par-franck-malmin-producteur-executif-5668560-760.html">read his post-mortem</a>, really honnest, about the film, in fr), D2S CEO , purpose to the Yona Yona Surfacing TD's to help "a production" to start. This one begins to spend few afternoons in <a href="http://www.nord-ouest.fr/" hreflang="fr">Nord Ouest Film</a>, where a part of "Michel's team" is already in place.</p>
<p>At that point I knew no more. But difficulties that would encounter Yona Yona began to be felt and production ask for the full-time return of his surfacing TD on the project.</p>
<p>In Def2Shoot, the Maya part is less of a problem (thanks to the arrival of a second TD) and Frank asked me to continue the work begun at Nord Ouest.</p>
<p>It was at this time I meet peoples I knew as the "Michael's team", all great persons. :sourit:</p>
<p>At first, my job was not to last long and was very basic. I had to understand the project needs and refer them to quick solutions (help to convert mental ray's .map because some matte painting was monstrously large, define a folder hierarchy and explain the "why" and the "how" of each file, etc. ...).</p>
<p>Despite the rather limited technical needs that require this project (Tales of the Night, it's not Transformers with raytracing everywhere), the team, actually rather small and composed of <del>few</del> no technicians, could, in long-term, end up spending more time dealing with technical problems rather than really work peacefully. :reflechi:</p>
<p>There was still, at that time, 10 episodes of 13 minutes each release in full HD.</p>
<p>This concern was shared with the rest of the team and after few conversations between Frank and production, it was decided that I will continue the project until the end (the project lasted one year and a half and it should be noticed that deadlines were met).</p>
<p>I was now officially part of the team!</p>
<center>:youplaBoum:</center>
<h3>Drastic measures</h3>
<p>This decision will quite change my involvement on the project and I could see "farther", do things more advanced, manage more complex cases etc...</p>
<p>Michel Ocelot's Director sssistant came from 2D and was used to do a <del>heavy</del> amazing preprod work. Thus, storyboard and directing was already made and technical stripping was already in Excel files of the director of production (sorry, I don't really know english terms for this) even before an episode would only go in Layout.</p>
<p>This kind of thing quite assured me because have to think about changing everything at the last moment (<del>very</del> too present attitude in advertisement and the cinema aka "à l'américaine") would not led me to take some risks improving the production and it conditions. Naturally when you have some assurance that what you want to do is what we gonna do, you provide less time for "bad crafting".</p>
<p>That's when I could begin defining the outline of the workflow.</p>
<h3>Use scripts</h3>
<p>Let's don't turn around the bush: Scripting was everywhere no one dared venture. :sourit:</p>
<p>The importance of a technically coherent folder hierarchy was thus essential in order to automate tasks, while keeping in mind that it would be handled by "human" and, therefore, should also remain comprehensible.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/noShowPlans_001.png" alt="noShowPlans_001.png" style="display:block; margin:0 auto;" title="noShowPlans_001.png, juil. 2011" height="246" width="338" /></p>
<p>The preferred language was MEL in all Maya aspects (I was not yet comfortable with "Python in Maya") and Python for everything related to file manipulation (<em>os</em> and <em>re</em> are your friends).</p>
<p>Scripting usage was mainly made for "crossing points" of information/files. I will often refer about it during the rest of this post because I almost passed this production my fingers on the keyboard. :pafOrdi:</p>
<h3>Pipeline summary</h3>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/Schema_pipeline.png" title="Schema_pipeline.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/.Schema_pipeline_m.jpg" alt="Schema_pipeline.png" style="display:block; margin:0 auto;" title="Schema_pipeline.png, juil. 2011" height="215" width="560" /></a></p>
<center><i>A plan that basically sums up the different steps of the pipeline</i></center>
<h4>Decorators</h4>
<p>"Decorators" (mappers) made their sets in Photoshop (in very huge and very heavy files ^^ ), they exported their work in different layers in tiff in a dedicated folder following ad naming convention specified in advance.</p>
<p>At first, they converted their .tiff in .map (and .dds) themselves but this highly repeteative task took time. So I quickly made script that was going through all textures of the current project and checked that each .map (or .dds) was older than her .tiff. Otherwise, it made a conversion. :dentcasse:</p>
<h4>Characters</h4>
<p>I wasn't directly involved in the characters creation.</p>
<p>A 2D artist did the Photoshop version of the characters, burst them on a plan, a character TD, temporarily joint bye an animateur had set up an automated rig system to generate animable characters.</p>
<p>Character maps was converted the same way than decors.</p>
<p>My part, in the pipeline started at Layout stage with a tool to list characters and import them in the Maya scenes depending on the episode we was working on.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/layoutGen_001.png" alt="layoutGen_001.png" style="display:block; margin:0 auto;" title="layoutGen_001.png, juil. 2011" height="341" width="412" /></p>
<p>So it was a big communication work to know who list and in which case do not list a particular character.</p>
<h5>Notice about .dds format</h5>
<p>Michel Ocelot validate animation on playblasts. So it need that playblast look "much as possible" near the final render.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/playblast001.png" title="playblast001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/.playblast001_m.jpg" alt="playblast001.png" style="display:block; margin:0 auto;" title="playblast001.png, juil. 2011" height="315" width="560" /></a></p>
<center><i>This is a playblast. :sourit:</i></center>
<p>For this, textures used in the scene had to be thoses used in render, dealing with a resizing.</p>
<p>So I've coded a CgFX shader which I gave the .dds texture versions to reduce their load in GPU memory.</p>
<p>This technique worked very well on sets but a bug I was unable to resolve made some textures disappear during controller movements of the characters.</p>
<p>I had to hack a kind of surface shader for the viewport but we loose the advangate of .dds textures because it seems that Maya uncompress and place the entire texture in GPU memory. :pasClasse:</p>
<p>It's a shame because it was character textures size that take more memory actually. So I had to make some "low" version of .dds texture (which already was in low def) and a tool to switch between versions (some shot had a lot of characters)...</p>
<h4>Layout</h4>
<p>So I've made the character importer but also a "set generator". Each shot in the film is actually a layering of maps:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/orgaDesPlans001.png" title="orgaDesPlans001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/.orgaDesPlans001_m.jpg" alt="orgaDesPlans001.png" style="display:block; margin:0 auto;" title="orgaDesPlans001.png, juil. 2011" height="339" width="560" /></a></p>
<p>As file naming convention was clear, it was very simple, using a script, to know where were the different layers of the current shot.</p>
<p>It was enough then to specify the episode number and the shot number we want to generate, and click ok.</p>
<p>A set rig was imported and adapted to the shot. Layers of set maps was load on their own plane and the camera's image plane displayed the animatic exported from the montage.</p>
<p>All that remained was "only to" put the characters.</p>
<p>I've also write a "transfer to shot" script to make layouters don't have to reposition every character whereas their was in the same position two shot later...</p>
<h4>Animation</h4>
<p>There was nothing "pipelined" concerning the layout's scene publication. Animators just copy the layout scene and start to animate they shot from this.</p>
<p>I've coded a character panel which was mainly used for hands. Hands simply textures on faces displayed/hidden during the animation.</p>
<p>Hand textures and numeric attributes for "display/hide" followed naming convention. So, each time a new hand was created by mappers, I just had to add a line in my panel's code to make everything appear in the panel.</p>
<p>However, responding to the render automation constraint required that scenes was published in specific folders where the script could find them.</p>
<p>So I've made a publication tool which save the current Maya scene and a little empty file (who played the role of "marker" to specify this scene had just been republished).</p>
<h4>Rendering</h4>
<p>There was no human need for this part. All had been scripted by me.</p>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/script001.png" title="script001.png"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/.script001_m.jpg" alt="script001.png" style="display:block; margin:0 auto;" title="script001.png, juil. 2011" height="509" width="560" /></a></p>
<center><i>jEdit was my friend! :laClasse:</i></center>
<p>I had to launch a script getting the whole scene from animation who had a "marker", open them and apply another script which do many things (some examples):</p>
<ul>
<li>Change texture path (replacing ".dds" by ".map" for examples).</li>
<li>Change texture options (to use <a href="http://download.autodesk.com/us/maya/2011help/mr/manual/node24.html" hreflang="en">elliptical filtering</a>).</li>
<li>Set optimal render parameters.</li>
<li>Go lookup, in a text file, if the actual shot had motion blur and activate it.</li>
<li>Apply some modifications (animation offset of hands of 0.5 frame if render with motion blur to avoid "poping effect" during image rotation, hide some visible controlers, etc...).</li>
<li>Do some hack depending of the episode/shot (apply a different blur to the "Garçon Tamtam" hands for example).</li>
<li>Generate job file containing command lines to launch (we used <a href="http://www.uberware.net/smedge/" hreflang="en">Smedge</a>) and a .bat for special case we need to render locally.</li>
<li>Remove the "marker" file.</li>
<li>Save the "ready to render" scene in the good folder.</li>
</ul>
<p><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/batchSmedge001.png" alt="batchSmedge001.png" style="display:block; margin:0 auto;" title="batchSmedge001.png, juil. 2011" height="224" width="238" /></p>
<center><i>Job file generated by the script</i></center>
<p>When lunch time came, artists launched a Smedge client on they worksation to let renders be computed.</p>
<p>Mental ray was the main render engine and it did it well (it was my favorite render engine...). Once the .map created and the elliptical filter set, everthing was fine!</p>
<h4>Compositing</h4>
<p>Compositing était fait sous After Effect et se divisait en deux parties.
Compositing was made with After Effect and was divided in two parts.</p>
<ul>
<li>A first one containing every "a little special things" (smoke effects, morphings, some particular animations, etc...) was done by a 2D animator.</li>
<li>A second one representing the most of the shots (80%) was mainly quick improvements, global effects (depth of field), etc...</li>
</ul>
<p>I've made a After Effect script (my first one) who created a compo for the shot, import every layers, stacked them in the good order and created an export set.</p>
<p>It was also using this script we could quickly had "raw" renders: We launched this script for a list of shot to render, click the "Render" button and the computing of everything just start. It only remained to check the editing project (Final Cut) which was automatically updated.</p>
<p>A second script (used by the first one actually) generate export sets from selected compos in After Effect. This simplify compositor life which never loose time to correctly export they work.</p>
<h4>All the rest</h4>
<p>Of course, there was many little task to do during the project:</p>
<ul>
<li>Episode backup.</li>
<li>Disque space management on servers.</li>
<li>Technical storyboard reading.</li>
<li>etc...</li>
</ul>
<h3>Passage du stade de série au stade de long métrage en relief
From a serie to a relief full feature film</h3>
<p>Yeap! Because this is what make this project interesting. The original idea was to do a full HD serie for a french TV channel (Canal+) and it terminate with a full feature film in relief. This is not something you work on everyday. :redface:</p>
<p>I remember that it's following a projection for Canal+ that the idea of a full feature film has begun, without be really official (there was no talk about relief at this time). In practice, this change really few things for us.</p>
<p>But when we started to talk about relief, things have changed a little. <a href="http://www.macguff.fr/fr#/jobs/456-les-contes-de-la-nuit" hreflang="fr">Mac Guff</a> was on it. We gave them some shots sufficiently representative of the movie and they've done "real" (<em>not-like-clash-of-the-titans</em>) relief.</p>
<p>I insist on the word "real" relief because Mac Guff had all layers (and our After Effect files) and was able to work the relief without having to artificially recreate the depth.</p>
<p>During the first projection we were 5-6 et I must say I was really impressed by the result. The depth and the black gave a real theatrical effect. The relief team had done an excellent job and I think everyone was won over. (Avatar could go get dressed, he'd just take a low kick reverse rotation there! :baffed: )</p>
<p>Once the relief has been officially validated, the number of layer had exploded. It happened that After Effect projects beating down our little network. Finally, compute images locally was a simple and cheap solution that solves in part the problem.</p>
<p>The production is very well done, without pain and it's rare enough to be notified.</p>
<h3>What I would have liked to do better</h3>
<p>Of course, with retrospective, I think that some points would have deserved a little more attention.</p>
<ul>
<li>I wish I solve the texture problems (cgfx + .dds) that "pop" in the viewport when animators were moving their controllers. The solution found was not optimal and would increase the Maya load when there was a lot of characters. <em>seg faults</em> were too recurrent...</li>
<li>I've choose to use .png at every step of the project (excepted Photoshop because .tiff file format was the only one that keep alphas during .map conversions) because this format was very "simple" (RGBA and that's all!) and I wanted to avoid the .tiff which was much more "opaque" (many kind of possible compression, number of channel, can't be opened everywhere, etc...). This is something I see as a "rookie" mistake now because the .png file format is extremely slow to open. The. tiff, if it is well managed in all steps of the pipeline, offers a much better speed.</li>
<li>Probably other things...</li>
</ul>
<h3>Conclusion</h3>
<p>As you maybe understood, this was a great both professional and personal experience. Work with Michel Ocelot is very pleasant.</p>
<p>It was also a first technical supervision for me on a feature film.</p>
<p>The movie will be <a href="http://www.allocine.fr/film/fichefilm-183772/palmares/" hreflang="fr">8 times nominated</a> to the International Festival of Berlin which is still a great achievement when I remember the actually low number of peoples who worked on this project (compared to other productions of film animation).</p>
<p>Press reviews are very good and I hope this post, a little technique, will make you want to see this film made with love. :sourit:</p>
<div class="external-media" style="margin: 1em auto; text-align: center;">
<iframe width="640" height="390" src="http://www.youtube.com/embed/ZoZGOtCzGLc" frameborder="0" allowfullscreen></iframe>
</div>
<p><a href="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/lescontes.jpg" title="lescontes.jpg"><img src="https://www.fevrierdorian.com/blog/public/billets/2011_04_06_lcdln_resume_superviseur/.lescontes_m.jpg" alt="lescontes.jpg" style="display:block; margin:0 auto;" title="lescontes.jpg, juil. 2011" height="560" width="411" /></a></p>
<p>My favorite tales? :sourit:</p>
<ul>
<li>The Werewolf: Because the work done on the leaves makes great stereo.</li>
<li>The Chosen One of the Golden City: Because in the movie theater, with the music, your adult heart will necessary let fall some tears of happiness.</li>
</ul>
<p>See you!</p>
<p>Dorian</p>
<h3>Liens</h3>
<ul>
<li><a href="http://www.imdb.com/title/tt1828229/fullcredits" hreflang="en">IMDb Les Contes de la nuit</a></li>
<li><a href="http://www.imdb.com/title/tt1723666/fullcredits" hreflang="en">IMDb Dragons et Princesses</a></li>
<li><a href="http://en.wikipedia.org/wiki/Dragons_et_princesses" hreflang="en">Wikipedia Dragons et Princesses</a></li>
</ul>My impressions about mental ray's futur (English Translation)urn:md5:993e273f90a68ceeaf2188d0c0e4ede32011-07-17T21:50:00+02:002013-07-26T18:07:31+02:00NarannInfographie 3D - Boulotenmental raynvidiavray<p><img src="https://www.fevrierdorian.com/blog/public/divers/mental_ray_its_over_tn.png" alt="mental_ray_its_over_tn.png" style="float:left; margin: 0 1em 1em 0;" title="mental_ray_its_over_tn.png, juil. 2011" height="150" width="150" />
I use mental ray since 2004 and it quickly become my favorite render engine. I attended, as a lot of you, to his slow decline over years... I have opportunity to work with Vray for a vfx feature film. The pipeline is around Vray and uses many new features highly "prod oriented" that make our lives easier.</p>
<p>It was my first experience with this engine and I must unfortunately admit it made me realize that mental ray can never back up the hill... :neutral:</p>
<p>I propose you my little impression on all of that in this post.</p> <h3>Some illusions...</h3>
<p>At first, I was pretty confident about mental ray's futur. Each new version add somme little cool features and the enemy to be defeated was that Autodesk which was unable to make a correct integration of this engine in they softwares. You had to tinker to use the latest features but you could get by. (<a href="http://forum.mentalimages.com/showthread.php?7835-Why-not!#post_32500" hreflang="en">Read</a>, the very informative history of the integration of mental ray in Maya by one of its devs).</p>
<p>I saw many active <a href="http://forums.cgsociety.org/showthread.php?t=639480" hreflang="en">threads</a> about some "heads" leaving the project or the futur of mental ray following the purchase of Nvidia.</p>
<p>Nvidia had Gelato and, peoples making the project quitting it, found themselves without any GPU rendering solution under hand, hence the acquisition of mental image to retrieve devs of mental ray and make them work with CUDA.</p>
<p><img src="https://www.fevrierdorian.com/blog/public/logos/Nvidia_Cuda_logo.jpg" alt="Nvidia_Cuda_logo.jpg" style="display:block; margin:0 auto;" title="Nvidia_Cuda_logo.jpg, août 2010" height="303" width="500" /></p>
<p>At first, everyone thought, eyes full of stars, that mental ray went "CUDA accelerated" but just think for two seconds to realize that the problem was actually more complicated... :tuComprendRien:</p>
<p>Paolo Berto (<a href="http://forums.cgsociety.org/member.php?u=124550" hreflang="en">jupiterjazz</a>), an influent member <a href="http://www.jupiter-jazz.com/group" hreflang="en">working</a> for the jupiter jazz group (<del>a</del> THE group of vfx dev), and obviously well informed <a href="http://forums.cgsociety.org/showpost.php?p=5203466&postcount=14" hreflang="en">wrote</a> this:</p>
<blockquote><p>No, mental ray won't be ever ported on cuda, there is no intention and it simply can't. Only some parts could, and if so it will be done just for marketing reasons.</p></blockquote>
<p>A member ask him about that and <a href="http://forums.cgsociety.org/showpost.php?p=5204780&postcount=18" hreflang="en">he give</a> a little more details:</p>
<blockquote><p>So, to make things work on cuda you need to have a lot of *coherent* computations to perform: the problem of mental ray is that it is casting one ray then calling a native C shader to shade the intersection then maybe the shader casts another batch of rays and so on. Very non coherent.<br />
This is just not suited for gpu computation unless if a massive rewrite/refactoring of the pipeline is done by mental images, which will not happen soon enough (I am speaking about years).<br />
Some specific task like fast AO could be definitely done though (and also this won't happen soon).<br />
<br />
Also another problem of mental ray is that the codebase is 15 years old (and it's not a Whiskey), native C for the CPU, full of pointers mem alloc and other shit, so to use an euphemism is a fcking mess.</p></blockquote>
<p>It is quite clear:</p>
<ul>
<li>Not GPU thinked.</li>
<li>Too old.</li>
<li>"Stop dreaming!"</li>
</ul>
<p>Everyone wondered what Nvidia was going to do of a full CPU render engine that did not seem to be able to change...</p>
<p>All indulged in some speculations but until then, there was nothing let guessing what would happen.</p>
<p>My doubts really began in 2009 at the Nvidia's <a href="http://www.youtube.com/watch?v=TQsXualxLVs" hreflang="en">GPU Technology Conference</a>. I saw one of the single "old dev" of mental ray and I learned that he had worked "several years" on the thing we all expected (irony): iRay and Reality Server.</p>
<p>Maybe I was the only one but I immediately thought that putting the last "heads" (there is not many) on a project full CUDA that will be used to sell entire plants which are more for "Arch and Viz" than anim/vfx did not bode well for mental ray.</p>
<h3>...to desillusions</h3>
<p><img src="https://www.fevrierdorian.com/blog/public/logos/mental_image_logo.png" alt="mental_image_logo.png" style="display:block; margin:0 auto;" title="mental_image_logo.png, juil. 2011" height="40" width="175" /></p>
<p>And that's what happened. Nvidia announced, <a href="http://blogs.nvidia.com/2011/05/nvidia-integrates-workstation-software-efforts/" hreflang="en">very politely</a> it <del>dismantled</del> reorganized mental image to, I quote :</p>
<blockquote><p>integrating it into our other activities focused on software solutions for design professionals</p></blockquote>
<p>It follows:</p>
<blockquote><p>The combined group brings together mental images with related efforts in the Quadro group focused on the world’s most demanding design professionals, from feature film artists to architects and product designers.</p></blockquote>
<p>Basically, they bring closer mental picture from "Quadro Group" to "focus" on the needs of film artists... :septic:</p>
<p>Mhum... Personally, the more you avoid Quadros, CUDA and other proprietary technologies which are a misery to deploy and very expensive, the more I am satisfied ...</p>
<p>So for this time, it's completely missed... I think many big CAD companies invest in there. But a VFX studio? Seriously?</p>
<p>Nvidia left mental image alone for a while (about four years during which I kept hope), to let them keep their commitments (this is often what happens for big acquisitions) but now they resume the reindeer and <a href="https://twitter.com/#!/virtualritz/status/72603197121896448" hreflang="en">it hurts</a>:</p>
<blockquote><p>Mental images is gone. Rumor has it ~30 people were laid off, management was dispersed. The corporate bullshit version: <a href="http://blogs.nvidia.com/2011/05/nvidia-integrates-workstation-software-efforts/" title="http://blogs.nvidia.com/2011/05/nvidia-integrates-workstation-software-efforts/">http://blogs.nvidia.com/2011/05/nvi...</a></p></blockquote>
<p>As we are in the rumor, it seems that the mental ray team has not been dismantled (although there were some voluntary departures). But insofar Nvidia is a very opaque compagnie you can't have more informations.</p>
<p>That said, I would be in bad faith if I said that <a href="http://www.mentalimages.com/products/mental-ray/mental-ray-39.html" hreflang="en">the latest mental ray features list</a> was empty, far from it!</p>
<p>Mental ray is a very good render engine (I suspect to be faster than Vray in pure raytracing...) but the big studios do not make prods on a "good engine" but rather "an engine they considers to be the best". What mental ray is no longer.</p>
<p>If you have some times, <a href="http://forum.mentalimages.com/showthread.php?7402-Iray-For-OpenCl" hreflang="en">read this four messages's short thread</a>... It summarizes perfectly in what mental image is sinking.</p>
<p>In short, a person asks a simple argued question to iray's devs about OpenCL (it will include reference to Chaos Group). No dev will respond (it's pretty rare, they often take the time to answer, even briefly to such issues) but a person, who appears to be more a Nvidia's commercial than a dev answer a marketing pitch promoting CUDA face OpenCL:</p>
<blockquote><p>iray uses C for CUDA because it needs the highest performance and greatest capabilities available to it.</p>
<p>
While NVIDIA leads the industry in the broadest OpenCL support, the language is several years behind C in both capabilities and tools, and it advances at the speed of open standards. A CPU fallback is unnecessary for iray as it supports x86 directly - far more efficiently than a fallback could. In using C for CUDA, iray ensures you have the very latest GPU capabilities as soon as they come online, while having direct influence on its evolution.</p>
<p>
With C for CUDA, there are over 1/2billion NVIDIA GPUs that can increase iray performance. I believe you would find the %increase from AMD to be quite small as their OpenCL support is limited to their latest offerings.</p>
<p>
As for CPUs, iray runs as well on AMD as Intel, taking full advantage of multiple cores and sockets.</p>
<p>
- Phil
NVIDIA</p></blockquote>
<p>I think, for a support forum, it's stain. Especially as the defenders of CUDA on <a href="http://www.chaosgroup.com/forums/vbulletin/showthread.php?53415-Cuda-4.0&p=451532#post451532" hreflang="en">the Chaos Group forum</a> debit the same kind of nonsense to finally take a big setback from Vlado, senior member of the Chaos Group forum:</p>
<blockquote><p>- (Membre): I had a feeling CUDA is gonna kick OpenCL in the ass and it is ! I hope that OpenCL will be able to share memory too at some point... <br />
- (vlado): We do have a CUDA version of V-Ray RT that we use internally, so if we see that there are significant benefits of going this way, it is certainly something that we would do without too much hesitation.<br />
- (Membre): Wow... Release it ploxxxxxxxxx, isn't it a lot more responsive(refresh speed) then opencl btw?<br />
- (vlado): No, not really. In some of the last tests, OpenCL was a tad bit faster.</p></blockquote>
<p>So here we are: For Chaos Group, released a CUDA version of V-Ray RT is useless because it is less efficient...</p>
<p>And while we're talking about that: The arrival of Vray for Maya could had be done without too much noise... It was expected by many but not necessarily the VFX industrie at that time. Chaos Group seems to have understood that the needs of Maya users are not quite the same as 3dsMax users where Vray was considered as the rendering engine "for archviz"...</p>
<p>Who would have thought, with the release of Vray for Maya that he would serve Digital Domain to make Tron Legacy's shots:</p>
<iframe src="http://player.vimeo.com/video/20014133?title=0&byline=0&portrait=0" width="590" height="332" frameborder="0"></iframe>
<p>Indeed, this version is very "prod oriented". They did not try to "copy" their render engines from 3dsMax to Maya. They didn't try to uniform it, they adapted it. And very cleverly adapted...</p>
<h3>Conclusion</h3>
<p>In short, the purpose of this post was not to praise Vray but to explain why I can't believe in mental ray anymore...</p>
<p>The fact it's still an excellent render engine and it's integrated to Maya and 3ds Max leaves him a bright future and he will not go away at once. But I think the studios will turn away slowly, as though faithful for years, I began to do so.</p>Creating Custom Locator with Maya's Python API (English Translation)urn:md5:c82400507ef5dded320bdb670f4eacfb2010-02-12T22:36:00+01:002013-07-26T22:32:05+02:00NarannScript et codeapicppenmayamelopenglpythonscripttranslation<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocator001.png" alt="pythonLocator001.png" style="float:left; margin: 0 1em 1em 0;" title="pythonLocator001.png, mar. 2009" height="150" width="150" />This post is a english translation of <a href="https://www.fevrierdorian.com/blog/post/2009/04/15/Cr%C3%A9er-un-Custom-Locator-avec-l-API-Python-de-Maya">this post</a>. I'd wrote it after post <a href="http://forums.cgsociety.org/showthread.php?t=845916" hreflang="en">this on CGTalk</a>. Great thanks to Daisy C. Lyle. She translate all the french version to english and make a great work! She save me hours doing this! :sourit: . Hope that this tutorial will be usefull.</p> <p>As <a href="https://www.fevrierdorian.com/blog/index.php?post/2009/03/31/Python%2C-Maya-et-sont-API...">I mentioned before</a>, I’m starting to use the Maya API Python binding. So I had a look at <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/mayaapi.html#2" hreflang="en">Rob Bateman’s</a> sources (which I found incomprehensible a year ago) and “translated” them (not without difficulty) into Python…So I’ve created a little script with a custom locator. It’s obviously not the simplest thing in the world to begin with (I did have some grounding thanks to <a href="http://nccastaff.bournemouth.ac.uk/jmacey/RobTheBloke/www/research/maya/mfn_traversing.htm" hreflang="en">other tutorials</a>, the Python “plug-ins” already incorporated into Maya and the OpenGL tutorials I’d done) but once the code is up and running, it’s quite fun to modify it to make your own locator…</p>
<p>So, on the menu we have:</p>
<ul>
<li>OpenGL (Well, how else are you going to draw your locator? :hehe: )</li>
<li>Color change as a function of the selection state of the locator.</li>
</ul>
<p>It’s not much but you’ll see it’s enough to be getting on with!</p>
<center>:longBar:</center>
<h5>Scripting is pointless, it’s for newbies!</h5>
<p>No! If you think that, then you haven’t understood anything. The two are closely related. If I chose the example of the locator, it’s not just because it’s “cool” but also because it’s an example of a thing it is only possible to do with the API…</p>
<p>But the API doesn’t do everything! :cayMal:</p>
<p>As you doubtless already know, the Maya interface is entirely written in MEL, which allows a scripter to quickly create a GUI for his or her scripts. Not all software packages offer this (did anyone say XSI?) and Maya is one of the few that boasts it.</p>
<p>Most Maya function calls are also scripted. This means you can “batch” your operations (when you have 50 scenes to open to change the value of an attribute, you won’t be laughing so hard! :hehe: )</p>
<p>I can only invite you to read <a href="http://www.creativecrash.com/forums/api/topics/new-to-the-api-then-read-this-33" hreflang="en">my mentor’s post</a> on the subject (displaying the C++ equivalent of a MEL code).</p>
<p>And finally: Python is a scripting language! Even if it can be “pre-compiled” by the interpreter, it’s still script.</p>
<p>That said, I think that this argument is a bit fruitless as even if you script using the Maya API Python binding, once the basics are understood you realize that the differences in syntax between a piece of C++ code and its equivalent in Python are minimal.</p>
<p>A little example of variable declaration:</p>
<center><I>C++</I></center>
<pre class="cpp cpp">MFnPlugin fnPlugin<span style="color: #008000;">(</span>obj<span style="color: #008000;">)</span><span style="color: #008080;">;</span></pre>
<center><I>Python</I></center>
<pre class="python python">fnPlugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span></pre>
<p>C++ is still the most user-friendly, I think (I’m not saying that because of this examples but in a more general way), which seems logical because that it's the historically used implementation, added to the fact that it remains a language fairly close to machine.</p>
<p>Then it all depends on whether or not you want to go fast. Compiled code (C++) will obviously go faster (about ten time) but I'm supposing that if you want to go fast, either you’re not good and you’re writing rotten code which leaves you no other solution than to resort to a lower-level language to speed it up, or you’re a “real” developer… If that’s the case, I don’t see what you’re doing on the blog of a Sunday coder! (Me! :aupoil: ).</p>
<p>What the Hell, one day I’m going to make a bench between MEL, pythonForMel, C++ and pythonForMayaApi :sourit: .</p>
<center>:longBar:</center>
<h5>Code basics</h5>
<p>Before we start messing around with the locator, we need the base code. Here we go!</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>Bang! Not acting so clever now are we? (I wasn’t acting clever the first time around either! :baffed: ).</p>
<p>Right, in fact it’s not very complicated. Explanation:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender</pre>
<p>Here we are importing the main modules. The first (“sys”) is the system module which we will use to write the plug-in initialization error messages (that’s all.)</p>
<p>The next three are Maya API Python modules. They have been divided into several modules in order to clarify their use:</p>
<ul>
<li>OpenMaya is the module for classes relating to node and command definitions and their “assembly” into plug-ins.</li>
<li>OpenMayaMPx is a Python-specific module. It contains the Maya’s proxy (or MPx class) bindings.</li>
<li>OpenMayaRender is the module for classes relating to render (hardware or software).</li>
</ul>
<pre class="python python">nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span></pre>
<p>This variable (I declare it at the beginning) will be used to give a name to the type of node you wish to create.</p>
<pre class="python python">nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span></pre>
<p>This variable (I also declare it at the beginning) will be used to give an ID (identifier) to our type of node.</p>
<pre class="python python">glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>This is where it gets complicated (especially for me.) This command is not referred to in the documentation (even though it appears in the example C++ codes.) This command makes it possible to retrieve a pointer to the class in charge of the render (hardware) of the viewports. It’s thanks to this that we are going to be able to “draw” our locator (using OpenGL commands.)</p>
<pre class="python python">glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The glFunctionTable() method returns a pointer to an object containing all the OpenGL instructions handled by Maya.</p>
<p>Now here we’re really getting stuck into OpenGL (well perhaps not quite, as you’re about to see :sourit: ):</p>
<center>:longBar:</center>
<h5>OpenGL, some informations</h5>
<p>What is OpenGL? (<a href="http://fr.wikipedia.org/wiki/OpenGL" hreflang="en">Wiki</a>) To summarize, it’s a way of communicating with the graphics card. It involves simple commands, e.g. to display points, lines, triangles, quads, textures, etc…</p>
<p>Before we go any further, we’re going to have to understand how this all works. If you want to “draw” locators, you have to know how to use the pen that is going to do it.</p>
<p>As a piece of code is worth more than a long lecture, here is how to “draw” a line in OpenGL (this is a C source. For Maya, we will see that there are two or three things you have to know first):</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>This technique is the “basic”, or “immediate mode” technique.</p>
<pre class="c c">glBegin<span style="color: #009900;">(</span>GL_LINES<span style="color: #009900;">)</span></pre>
<p>This command allows us to “go into primitive draw mode”(In a 3-D space.) We’re putting the pencil to the paper, if you like. The argument (GL_LINES) enables us to say how the following commands will be interpreted, inline here. Every two vertices, we write another line. One line per pair of vertices.</p>
<pre class="c c">glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color: #339933;">-</span><span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span>
glVertex3f<span style="color: #009900;">(</span><span style="color:#800080;">0.0</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.5</span><span style="color: #339933;">,</span> <span style="color:#800080;">0.0</span><span style="color: #009900;">)</span></pre>
<p>These two commands are the commands which “place” the vertices in space. The command is glVertex, the number is the number of arguments (here, three: X,Y and Z) and the last letter is the <a href="http://www.cplusplus.com/doc/tutorial/variables/" hreflang="en">data type</a> (here, “f” for float.)</p>
<p>Basically, we’re placing two vertices in space. And given that we have put ourselves into “line draw” mode (per vertex pair), we have just drawn a line.</p>
<pre class="c c">glEnd<span style="color: #009900;">(</span><span style="color: #009900;">)</span></pre>
<p>And that’s just a way of “shutting down communications” with the graphics card and returning to the main program.</p>
<p>As you see, OpenGL isn’t as hard as all that! This was also how I understood that it’s the processor that calculates all the vertices’ positions in each image (well, that’s the old method; there are others, especially for static objects which are stored in the memory of the graphics card and called by a single instruction. Having said that, a large part of the work is done by the processor).</p>
<p>With this, you are almost ready to draw your own locator. I have just one point left to address.</p>
<center>:longBar:</center>
<h5>OpenGL, the Maya version! <a name="openGLVersionMaya"></a></h5>
<p>In fact, the code that I have shown you is a C program. I am now going to explain to you some of the few OpenGL peculiarities specific to Maya.</p>
<p>Maya, out of concern for interoperability (I suppose there are other reasons) has its own OpenGL implementation. So, when we wish to draw in the viewport, we don’t directly use the Windows or Mac implementation but in fact Maya’s own. (This can be done in C++, which makes it possible to dispense with the Maya wrapper, but you have to handle the interoperability in your code. It should also be possible to do this in Python but I haven’t tried). That doesn’t change much in the code but the constants (GL_LINES for instance) have another name.</p>
<p>As written in the Maya API documentation:</p>
<blockquote><p>The naming convention used in this file is to take the regular GL* definitions and add a "M" prefix</p></blockquote>
<p>Translation: “We put an ‘M’ in front of all the OpenGL constants”. :seSentCon:</p>
<p>So Maya won’t understand GL_LINES, only OpenMayaRender.MGL_LINES :baffed:</p>
<p>And here is how we write a line in OpenGL in Maya:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>So no big differences then ^^. Any experienced OpenGL user should be able to do it with their eyes closed!</p>
<center>:longBar:</center>
<h5>So shall we write this locator then?</h5>
<p>No! Not yet! There are still things to learn! After this little introduction to OpenGL, let’s go back to our Python script.</p>
<p>To be able to draw the locator, we have to derive the “draw” function (yes, because Maya allows you to derive functions…Not everyone can say as much, eh, XSI? :siffle: ). This function is used to…draw custom geometries using OpenGL functions.</p>
<p>Here is the basic code for the “draw” method:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The beginGL( ) and endGL( ) functions make it possible (well, this is my interpretation) to tell Maya that we are going to use OpenGL commands between them. According to the documentation, if they aren’t included in the right place, the program will exceed its allocated memory the program will therefore crash.</p>
<p>I’m deliberately going to forget a few functions for the moment, we’ll look at them later.</p>
<center>:longBar:</center>
<h5>Go Pawaaaah!</h5>
<p>So our code now looks like this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">sys</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMaya</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMaya</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: #dc143c;">OpenMayaMPx</span> <span style="color: #ff7700;font-weight:bold;">as</span> <span style="color: #dc143c;">OpenMayaMPx</span>
<span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaRender</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaRender
nodeTypeName = <span style="color: #483d8b;">"myCustomLocator"</span>
nodeTypeId = <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MTypeId</span><span style="color: black;">(</span>0x87079<span style="color: black;">)</span>
glRenderer = OpenMayaRender.<span style="color: black;">MHardwareRenderer</span>.<span style="color: black;">theRenderer</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT = glRenderer.<span style="color: black;">glFunctionTable</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">class</span> myNode<span style="color: black;">(</span><span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">def</span> <span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>:
<span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxLocatorNode</span>.<span style="color: #0000cd;">__init__</span><span style="color: black;">(</span><span style="color: #008000;">self</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> draw<span style="color: black;">(</span><span style="color: #008000;">self</span>, view, path, style, status<span style="color: black;">)</span>:
view.<span style="color: black;">beginGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
view.<span style="color: black;">endGL</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeCreator<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">asMPxPtr</span><span style="color: black;">(</span>myNode<span style="color: black;">(</span><span style="color: black;">)</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> nodeInitializer<span style="color: black;">(</span><span style="color: black;">)</span>:
<span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #dc143c;">OpenMaya</span>.<span style="color: black;">MStatus</span>.<span style="color: black;">kSuccess</span>
<span style="color: #ff7700;font-weight:bold;">def</span> initializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">registerNode</span><span style="color: black;">(</span>nodeTypeName, nodeTypeId, nodeCreator, nodeInitializer, <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MPxNode</span>.<span style="color: black;">kLocatorNode</span><span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to register node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">def</span> uninitializePlugin<span style="color: black;">(</span>obj<span style="color: black;">)</span>:
plugin = <span style="color: #dc143c;">OpenMayaMPx</span>.<span style="color: black;">MFnPlugin</span><span style="color: black;">(</span>obj<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">try</span>:
plugin.<span style="color: black;">deregisterNode</span><span style="color: black;">(</span>nodeTypeId<span style="color: black;">)</span>
<span style="color: #ff7700;font-weight:bold;">except</span>:
<span style="color: #dc143c;">sys</span>.<span style="color: black;">stderr</span>.<span style="color: black;">write</span><span style="color: black;">(</span> <span style="color: #483d8b;">"Failed to deregister node: %s"</span> <span style="color: #66cc66;">%</span> nodeTypeName<span style="color: black;">)</span></pre>
<p>You can download it here:</p>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode001.7z">CustomLocatorNode001.7z</a></p>
<p>Load it (as a plug-in), and enter:</p>
<pre class="mel mel">createNode myCustomLocator<span style="color: #339933;">;</span></pre>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocLineFirstLocator.png" alt="pythonLocLineFirstLocator.png" style="display:block; margin:0 auto;" title="pythonLocLineFirstLocator.png, avr. 2009" height="194" width="222" /></p>
<p>We’ve just made out first locator! :sourit: With this we can already do a few things… If you give it some elbow grease, you can get this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">random</span></pre>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>, <span style="color: #dc143c;">random</span>.<span style="color: black;">uniform</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode002.7z">CustomLocatorNode002.7z</a></p>
<p>You’ll have a wicked locator! ^^</p>
<p>Right, it’s all very well making lines but that’s not all there is to it… I suggest we do… a quad! :laClasse:</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode003.7z">CustomLocatorNode003.7z</a></p>
<p style="font-style:italic; text-align:center;">Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuad.png" alt="pythonLocQuad.png" style="display:block; margin:0 auto;" title="pythonLocQuad.png, avr. 2009" height="276" width="405" /></p>
<p style="font-style:italic; text-align:center;">Paf le locator! <img class="smiley" alt=":baffed:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/baffed.gif"/></p>
<p>OK, it’s a bit flashy but we’re going to change that. :reflechi: We’re going to change the color and add transparency.</p>
<p>First, the color. As default, Maya uses the interface colors (blue when unselected, green when selected, pink when ‘templated’ etc….) To change the color, all you have to do is add the command glColor3f( )</p>
<pre class="python python">glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#on change la couleur</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span></pre>
<p>The code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode004.7z">CustomLocatorNode004.7z</a></p>
<p style="font-style:italic; text-align:center;">Re-Paf le code!</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColor.png" alt="pythonLocQuadColor.png" style="display:block; margin:0 auto;" title="pythonLocQuadColor.png, avr. 2009" height="342" width="507" /></p>
<p style="font-style:italic; text-align:center;">Re-Paf le locator! <img class="smiley" alt=":aupoil:" src="https://www.fevrierdorian.com/blog/themes/blueSilenceCustom/smilies/aupoil.gif"/></p>
<p>Still just as flashy, huh? You’ll notice that only the line changes color as a function of the selection state. To ‘lighten up’ our locator a bit, we’re going to add transparency to our quad.</p>
<center>:longBar:</center>
<h5>The Alpha</h5>
<p>We are going to “enable” one of OpenGL's features: GL_BLEND (Don’t forget to disable it at the end):</p>
<pre class="python python"><span style="color: #808080; font-style: italic;">#We activate feature</span>
glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor3f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#Change color</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
<span style="color: #808080; font-style: italic;">#Don't forget to unactivate in the end!</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>When this feature is enabled, OpenGL blends the given color with the resulting color (the background) according to a final factor (the Alpha.) If you run the code as it is, you won’t see any change. So we must add one last component to our color:</p>
<pre class="python python">glFT.<span style="color: black;">glEnable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_LINES</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#Change color and add alpha</span>
glFT.<span style="color: black;">glBegin</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_QUADS</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, -<span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span><span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glVertex3f</span><span style="color: black;">(</span>-<span style="color: #ff4500;">0.5</span>, <span style="color: #ff4500;">0.0</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glEnd</span><span style="color: black;">(</span><span style="color: black;">)</span>
glFT.<span style="color: black;">glDisable</span><span style="color: black;">(</span>OpenMayaRender.<span style="color: black;">MGL_BLEND</span><span style="color: black;">)</span></pre>
<p>Le code: <a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode005.7z">CustomLocatorNode005.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocQuadColorAlpha.png" alt="pythonLocQuadColorAlpha.png" style="display:block; margin:0 auto;" title="pythonLocQuadColorAlpha.png, avr. 2009" height="298" width="435" /></p>
<p style="font-style:italic; text-align:center;">Et voila! Better no? ;)</p>
<p>We are coming to the last part: the selection states.</p>
<center>:longBar:</center>
<h5>Selection</h5>
<p>In fact we can change aspects of our locator depending on its selection state. Here, we're going to change... The color! (very original... -_-).</p>
<p>First of all, there are several "states" of a "drawn" object (Listed in M3dView::DisplayStatus). I'm going to give you the two main ones, the ones we’re going to be using:</p>
<ul>
<li>kActive -> The active (selected) objects. (Be careful, this is tricky!)</li>
<li>kLead -> The last object selected.</li>
<li>kDormant -> The inactive objects.</li>
</ul>
<p>So, let’s explain the subtle difference::</p>
<p>In Maya, when you select an object, it becomes green. And when you add another object to the selection, this second object becomes green and the previous, white. In fact the green object is “in DisplayStatus” kLead, and the white object in kActive. Here’s a picture to explain (or remind you of?) the principle:</p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelectionState.png" alt="pythonLocSelectionState.png" style="display:block; margin:0 auto;" title="pythonLocSelectionState.png, avr. 2009" height="160" width="418" /></p>
<p>So we are going to be able to change the color as a function of the selection state.</p>
<p>As seen above, the derived function is draw, and the argument we use to know the state of the locator is "status". And nothing could be easier than using all this:</p>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">import</span> maya.<span style="color: black;">OpenMayaUI</span> <span style="color: #ff7700;font-weight:bold;">as</span> OpenMayaUI <span style="color: #808080; font-style: italic;"># on top</span></pre>
<pre class="python python"><span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kLead</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.3</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#rouge</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kActive</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">0.4</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#jaune</span>
<span style="color: #ff7700;font-weight:bold;">if</span> status == OpenMayaUI.<span style="color: black;">M3dView</span>.<span style="color: black;">kDormant</span>:
glFT.<span style="color: black;">glColor4f</span><span style="color: black;">(</span><span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0</span>, <span style="color: #ff4500;">1</span>, <span style="color: #ff4500;">0.5</span><span style="color: black;">)</span> <span style="color: #808080; font-style: italic;">#mauve</span></pre>
<p><a href="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/CustomLocatorNode006.7z">CustomLocatorNode006.7z</a></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection001.png" alt="pythonLocSelection001.png" style="display:block; margin:0 auto;" title="pythonLocSelection001.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection002.png" alt="pythonLocSelection002.png" style="display:block; margin:0 auto;" title="pythonLocSelection002.png, avr. 2009" height="216" width="382" /></p>
<p><img src="https://www.fevrierdorian.com/blog/public/pythonLocatorTutorial001/pythonLocSelection003.png" alt="pythonLocSelection003.png" style="display:block; margin:0 auto;" title="pythonLocSelection003.png, avr. 2009" height="216" width="382" /></p>
<p>OK, it’s not that pretty but it works… :baffed:</p>
<p>I hope that this tutorial will shed some light on a few areas of the Python Maya API... Note however that I didn't choose the easiest thing to start with (if you know nothing about the API... Otherwise, it's quite easy I think.) But with this, you've already got a first toolkit that will enable you to take your first steps... :sourit:</p>
<p>Don’t hesitate to ask if you have questions on this tutorial (if the examples lack clarity or you have trouble making them work…)</p>
<p>See you soon!</p>
<p>Dorian</p>
<p>PS: As you could see, I'm not english so if I'd made redundant errors, don't hesitate to notice me! :sourit:</p>
<center>:longBar:</center>
<h5>Edit - Blog wich talk about this tutorial:</h5>
<ul>
<li><a href="http://marc.dubrois.free.fr/?p=179" hreflang="en">http://marc.dubrois.free.fr/?p=179</a></li>
</ul>