5 Lessons from Building Media Server Tools

[Editor note from Elburz: Hi all! We have a guest post from Tim Franklin this week about his experiences, lessons learned, and tips he picked up while building media server tools in TouchDesigner. Tim has been an amazing member of the TouchDesigner community and has created a lot of great tools available for free on his GitHub and his website including the tool he’s talking about today as well as a wonderful new timeline tool.

One of my initial frustrations moving from a media server background into TouchDesigner was the overall approach to the layout of media across multiple displays.  When I first started I was running shows on Millumin and Watchout, both of which have incredibly versatile workspaces. In both Watchout and Millumin you can simply drag and drop a piece of media from a media bin and place it in your “stage”.  You can visually see a representation of your outputs and even position them to match the physical layout of your show.

I created the “Stage Window” tool to replicate some of these features, but more importantly, as a learning tool for myself.  Coming into this project I had limited experience with the features used here and learned a tremendous amount about render picking, python, and project structure along the way.

1 – Render Picking – An Amazing Feature

The big breakthrough for me was the concept of using render picking for 2D work using orthographic cameras. I knew it was commonly used with 3D and VR but found that it can be very beneficial in 2D projects as well.

After watching the Render picking workshop on the HQ Pro, I realized I was very much mistaken and that it does in fact have uses in all kinds of projects.

2 – Inheritance in Extensions

Early on I realized that media items and outputs were essentially the same object (i.e. same parameters, same type of geometry, etc) but at the same time needed to be treated slightly differently. For this reason I ended up putting most of my class methods into a parent class, “Manager”, and specialized methods into child classes.

An example is the inherited “InitializeNewOP” method. All the work making new ops is handled by the base class, but at the very end it runs InitializeNewOp where you put specialized operations.

Scenario  – We have just created a new media item or output (depending on the button we pressed) and after we do the copy operation we run “InitializeNewOp”

Base Class – Manager.InitializeNewOp
Media Item – eCue.InitializeNewOP
Output – eOutput.InitializeNewOp

3 – Internal Parameters

After looking through how TimeBase was built I started getting into using Internal parameters to house all my component wide settings. In the current build I have “Stage” and “User” settings as two different internal parameter bases.

User and system settings accessible by any object in the tool

As long as I’m within my main component where these are defined, I can access them anywhere with the below expressions.

An output object referencing an internal parameter

4 – Get the Code Out of the Render Pick Callback!

I found that render picking can end up requiring a decent amount of code.  I started writing everything inside the callback and it quickly became hard to manage so I moved everything to three separate extensions all handling different cases:

  1. Render Picking                   [RenderPick]
  2. Transformations / Math   [Transformer]
  3. Stage Item Management  [Manager]

In my opinion, it’s much easier to pass the callback data up to the extension and handle it in there.

Render Pick Callback (bottom left) passing data to it’s parent’s RenderPick Extension

5 – TDU.Vector, TDU.Position, and TDU.Matrix

The discovery of the Vector, Position, and Matrix classes and their methods seriously blew my mind. One example usage is with the multi select and drag functionality. Smoothly dragging stage items around with the mouse requires that you calculate the offset between the click position and the object’s center. The below two operations made this relatively straightforward.

Setting up multi selection and translation via mouse drag

1. Let’s get the local transform matrices for the selection.

localTransform → tdu.Matrix

2. Retrieve the Scale, Rotation, and Translate from the Matrix class and convert it to Position type.

decompose()→ Tuple(Tuple, Tuple, Tuple)

3. Subtract the pick position from the object’s position.

 tdu.Position – tdu.Position→ tdu.Vector

4. Find the new position for each item taking this offset into account.

tdu.Vector + tdu.Position→ tdu.Position

Now we just assign these positions to our selection and we have translation capability on any item in our scene. Sweet!

Wrap up

Elburz here again! Thanks again to Tim for this great post! Lots of really interesting techniques he’s applied in his tools. If you’d like to check out more of Tim’s work or download the tools he’s been working on you can use the links below. Enjoy!

Website – http://timsfranklin.com/tdtools

Github – https://github.com/franklin113