viernes, 25 de noviembre de 2016

TACTIC Python API Tweak: Hack To Report Copied Byte Amount To Qt Widget

During the development of some Maya Tools that used the Southpaw Tactic Python API I bumped into the following, at first simple, problem: I wanted to give a visual report of the uploading progress process. Each artist had to check-in their work to the asset management system via internet.

The first version of the tool only gave report of the progress by means of a progress bar that visually was enough to notify when the upload had finished. This worked ideally for multiple tiny files. But soon Groom & Hair artists, as well as VFX artist where generating a lot of huge simulating data that needed to be uploaded.

We were working remotely and uploading the artist's work could easily take a couple of hours. The first approach was to use HTTP protocol to transfer those huge amounts of files. There we found a bug in the Python API of Tactic v4.4.04 that limited the file size to 10 MB (10*1024*104 bytes) that forced us to look in the documentation and upgrade to a newer version of Tactic that had this bug fixed. But that's another story.

What interests me here is that the Python Tactic API upload functions dont give any report of the number of bytes uploaded. It only gives a report of when an entire file has been checked-in, this is, by doing a Piecewise Check-In.

So we changed the upload method to use Tactic's handoff dir which consists basically on replacing the HTTP protocol by a protocol like CIFS or NFS where you just perform a copy from your local to the server's directory just like you would between two directories on your local filesystem.

That was the first step.

Now once, definitely using the most powerful transfer method. I only needed to have a look at the API. The "tactic_client_stub.py" module and the "TacticServerStub" class. The Piecewise Check-in works as explained here.



You can see that the API uses the "shutil.copy" and "shutil.move" methods to upload. I cannot tweak the "shutil" module, since it's a built-in one that comes by default with the Maya Python Interpreter. But i can build my own :))!!

My goal is to be able to report the amount of bytes transferred using a Qt Widget so basically i have to simulate a Signal/Slot behaviour from the copy/move methods. It would be nice if i could add a callback inside that method that triggered a Qt Signal, isnt it?!


A LEAST INTRUSIVE SOLUTION



The shutil module uses a lot of different methods to copy files considering the metadata, creation and last modification time, user owner and group owner and the permissions bits, etc. It is explained here.

All of them at last, call the "copyfileobj" method. That's the method i want to tweak.

Now, what kind of function can trigger a Qt Signal?? what are its requisites??

I remembered all Qt Classes inherit from the QObject Class.. A quick look at the PyQt Documentation explains it.


"The central feature in this model is a very powerful mechanism for seamless object communication called signals and slots"

So basically, the only thing i need is to define a class that inherits from QObject, define a custom signal and have the callback method to emit the signal!!. The following is not production code, it is just an example of how it would work.


All that is left is to catch the signal in the proper QWidget, with this information you can compute the time left for the upload to finish and hence give an estimate based on internet speed.

This solution is simple, straightforward and doesnt imply rewriting the TacticServerStub Class. Maybe if i find myself in the need of tweaking again i would consider writing my own TacticServerStub class.

Comments & Critics Welcome!!

viernes, 18 de noviembre de 2016

Animatable Pivot - Rolling Cube Demo

INTRODUCTION

During the production of "Deep" the movie, the rigging department had to design the rigs of ice cubes that some characters were pushing. In order to achieve this rolling cube the rig needed to dynamically change the rotation pivot.

I didnt have time to look further into it so i couldnt be of much help at the time. But since one of my interests is rigging i decided to dig deeper when i had enough time.

If you do a google search the problem of rolling cubes is something most Riggers and Character TDs have faced anytime. One of the most interesting articles on how it can be done is this one. But i wanted to do it my own way and in different ways. One using the Node Editor, Matrix Multiplication and Geometric Transformations and the other, by using constraints. I'll show both here.

The first approach is simple: animate the cube setting keys in the Rotate Pivot X,Y,Z  attributes. If you do that, you will notice that it doesnt work. Just when you change pivot, the cube suffers a translation due to the fact that the rotation is applied again but with the new pivot. This is, it doesnt remember the rotation you performed with the previous pivot.

So the solution is to calculate the translation difference between pivots and apply it to the top controller.


I started with the outliner configuration you can see above. This configuration is generic. It works for all kinds of meshes and any number of pivots, The cube pCube1 can be substitued by whatever mesh you want. Here to illustrate better, i have used a NURB Surface and positioned one in each corner of the cube.

The main transform group has an enum attribute to select the pivot. Once chosen, we calculate each pivots world position from the hierarchy and its local rotate pivot position. This is important because if you just simply use the pivots world rotate position you will cause a cycle in the transformations as it changes every time you rotate. The local rotate pivot position doesnt. So it becomes necessary to compute the world position traversing the hierarchy bottom-up. 

Here is the Node Graph.



Another way of doing it is, instead of using matrix multiplication, using the tools maya provides, this is by using constraints. We constrain from for example a locator to all the NURBS pivots. And instead of  using the Node Editor we manipulate the weights of the constraint of each of the pivots.

Both alternatives make use of scriptJobs. They are both here provided here.
The first one calculates the pivots difference in an accum buffer.



The second one modifies the contrain weights and applies the calculation to the Rotate Pivot of the transform group.



FURTHER DEVELOPMENT

I just wanted to play a bit with those concepts and figure out how i would tackle with the problem myself. Needless to say it still needs to be organised as most rigs are, which means being able to set keys in a Curve Control. This is not a big change though.

As I previously said, this configuration is generic. it would work for any kind of mesh and any number and distribution of pivots.

Here is the final video file.