- How to feed the Table COMP
- Table COMP structure and principles
- Evaluate DAT
- Panel Execute DAT
- Table COMP customization
- Getting values / actions out of the Table COMP
I love me some referencing. The more you work with TouchDesigner, the more you’ll find that you need a solid understanding of how referencing connects the various parts of your network. Better still is getting a better handle on how Python scripting works in TouchDesigner – especially dot notation. At the heart of what we’re after is making sure that our networks can start to feel a little more interconnected. Hard coding values makes for tedious programming, especially if you’re building something you’d like to reuse. By starting to think about how to build some logic into the system we’re making we begin to build reusable tools. If you’re still getting a handle on how referencing works, then it’s a good idea to get started here (Understanding Referencing) to get your bearings.
Let’s start with a simple example. I’ve got five images that I’d like to cycle through when I click a button. I know that I can use a Count CHOP and a Switch TOP to help with this but how can I make it so that my Count CHOP knows when I add another input to my Switch TOP? In a perfect world I could add or remove images or movies, and the network would just “know” what was going on, right? So how can we program something like this? Let’s start by taking a moment to visit my favorite part of working with TouchDesigner, the wiki. Specifically we need to look at our Switch TOP’s page.
The most important part of this page for us today is here at the top where it says “switchTOP_Class” next to the Python symbol. In object-oriented programming a class is a kind of “extensible template for creating objects.” The exciting part of knowing this is that a class comes with all sorts of methods and values that we can quickly call when scripting. With that in mind let’s take a closer look at the Switch TOP Class page. At this point you’re either beside yourself with excitement, or weeping with confusion. Let’s take a look at a quick example, and see if we can make some sense out of what we’re reading.
One of the first things on the Switch TOP Class page is a sentence that says “This class inherits from the TOP class.” This means that there are several things that work for the Switch TOP that also work for all TOPs. This means we can practice on a regular movie in TOP, and what we learn should also work for our Switch TOP. Okay, let’s open up a new network in TouchDesigner, and let’s add a movie in TOP and a Text DAT. We’ll also need to open up our Textport.
When you open your textport you should see a floating window that looks like this:
In our Text DAT we’re going to write a very simple little script that’s going to print in our textport. In Python we can print something to the textport with the command print(). Whatever we put in the parenthesis gets printed to the textport. You can practice this in the textport, just make sure that you use quotes around your text. For example:
We can also complete this same operation using our text DAT. In the text DAT write:
Now right click on the DAT, and select “Run Script” from the drop down menu. You should see the text appear in your textport:
Congratulations, you just ran a Python Script from a Text DAT. We’re going to use our ability to print to the textport to learn a few more things about our TOP class. We should still have a Movie In TOP in our network. My Movie In TOP is called “moviein1”, knowing the name of our operators is how we call point to them when we’re referencing. Looking back to the general TOP Class Wiki Page, I can see that there’s a member called width and one called height.
For this next step we’re going to use dot notation to get to the width and height information about our operator. For example, I can get that information by starting with the operator name, followed by a dot, followed by the member I want:
Let’s copy that exact line of code into our Text DAT. Next right click, and select Run Script. Nothing happened… what gives?! In this case, just because we asked for that value, doesn’t mean that TouchDesigner knew that we wanted it to be printed to the text port. In order to see these values in the textport we need to encapsulate our script in the print command. When you do this, you should have a script that looks like this:
Now if you run the script you should see:
Let’s try one more. Let’s add one more line of code:
Let’s run the script one more time and you should have something that looks like this:
Believe it or not, we just made some huge progress. Understanding how dot notation works gives you access to a HUGE treasure trove of information that you can use when scripting or writing references.
Let’s return to our example and see if we can’t use what we’ve just learned. Let’s start by adding a few things to our network. I’m going to add five Text TOPs, and I’m going to set them to be the numbers 1 – 5. Next I’m going to wire all of those to a Switch TOP, and then finally to a Null TOP,
Now let’s add a Text DAT to our network, and then take a quick moment to go back to our Switch TOP Class Page. Of particular interest to me is the Connections subsection:
Using what we just learned, write the following script into your text DAT:
When you run this script you should see something like this:
Well, that doesn’t look useful at all, does it? In fact, it’s tremendously useful! Python uses lists to store information, and what we’ve printed out is a list of connections to the Switch TOP. We don’t really want all of this information that we’re getting about the inputs, we just want to know how many connections there are. As luck would have it, there’s a way to get just that information. In Python we can ask for the length (the number of entries) of a list with the len() command. Alright, let’s make a quick change to our script and add the length command. You should have a script now that looks like this:
When you run the script you should see this:
Alright! Now we can start to really make some magic happen. Using .inputs and the len() command we’re able to now see the number of connections to our switch TOP. If you disconnect one of the inputs and run the command again you’ll see the number go down. Add a few more inputs, run the command, and you’ll see the number go up.
How do we use this? Okay, well let’s think back to where we started. We want to use a button to toggle through all of our inputs, and then cycle back to the front of the line. Let’s add a Button component to our Network, and a Count CHOP. Make sure to wire the Button to the top most input on the Count CHOP:
Let’s also make sure that the button is set to momentary:
Looking at our Count CHOP we want to set the Limit Method to “Loop Min/Max.” For the Limit Maximum value we’re going to use the script that we just wrote (only without the print command). We’re also going to subtract 1 from that number. We do this because 0 is still a valid input number for our switch, so while there are five total inputs those are associated with the numbers 0 1 2 3 4. The reference in the Limit Maximum field should look like this:
len(op(‘switch1’).inputs) – 1
Your Count CHOP parameters dialog should look something like this:
And now we have a little bit of black magic happening. If you add more inputs to your switch you’ll see the Limit Maximum number go up automatically, remove some inputs, and you’ll see it go down. Clicking on the button should cycle you through all of the inputs:
Now you know a little more about referencing, dot notation, and how to find more information in the wiki. Happy programming!
Check out the TOE file if you’d like to see what I made: Referencing Part 2
If you spend much time programming in TouchDesigner, eventually you’ll want to start doing some real-time rendering. When dealing with rendering some 3D geometry we need to get a few pieces of our network correctly set-up to get started. There are, of course, lots of different methods for rendering but we can get started with some rendering basics.
Like working in any rendering environment, we need a few things in place to actually draw an image. We need to have the actual object that we want to draw, we need a light source (not always, but for now let’s pretend), and we need to know where we are looking at the scene from. In TouchDesigner we can think of these elements in the following way:
Geometry Component – this is our object that we’re drawing. This component holds a Surface Operator (SOP), that is the object we are drawing.
Camera Component – this is the vantage point we are drawing out scene from.
Light Component – this is our light source for our scene. There are some cases where we don’t need a light, but it’s a good rule of thumb to assume that you’ll need to light your scene.
Render TOP – this texture operator is the drawing / video that is the result of your render set-up. This is the TOP that you’ll end up using in your network / performance / etc.
Using these three components and one operator, we end up with a basic network set-up that looks something like this:
At this point you’re probably familiar with patch cords (called wires in TouchDesigner’s official documentation) that connect operators within a single family (CHOPs, TOPs, SOPs, DATs, etc), but you may not be as familiar with the dotted lines with arrows that you see in the image above. These are called data links, and their visibility can be toggled on and off with the short cut key “x”. Unlike wires, data links help us see when operators from different families are connected in some way. We can also see if data is actively being passed through these connections – if the arrow head is moving, then new information is being passed, static arrows tell us that we’re not actively passing new data.
Let’s take a look at a quick example – here we can see that the Geo is constantly sending information to the render TOP. When we change the position of the camera Comp, we can see the arrow heads move. While this might not seem like something worth caring about in this very moment, there will surely come a day when you’re debugging a scene and the ability to quickly see what’s passing new data and what’s not will be the blessing that helps you keep your sanity.
Let’s take a moment to give our Render TOP a closer look. The Render TOP has five different pages of parameters, and while all of them are important we can start by looking at the Render Page and the Common Page.
The Render page contains all of the information about what components we’re currently using. Here we can see that we’re using cam1 as our Camera, * as our Geometry, and * as our Lights. What is this gibberish? Our Render TOP follows many of the pattern matching rules as the rest of TouchDesigner. In this case a * means, “any geometry component.” You can test this by adding another geo to your scene. You’ll see another set of data link lines drawn to the Render TOP, but you won’t see another torus. Why? Well, you won’t see another Torus because it’s being drawn in exactly the same dimensions in the same place.
At this point you could tell your Render TOP which Geo to use by typing in the name of the Geo component. For example, if we write “geo1” in our Geometry Field we’ll only see data links drawn from geo1 to our Render TOP.
Why do we care about this? This is useful as it has large scale implications for lots of different rendering situations – advanced lighting, camera actions, etc. This also gives you a way to divvy up rendering between TOPs and composite the video later.
In addition to the Render Page, we should also take a moment to look at the Common Page.
On the common page we can specify the resolution of our final image, as well as a few other parameters. Rendering can be a very GPU heavy task, so keeping an eye on your rendering resolution can be extremely helpful in managing the performance of your network. Alright, with all of that general information out of the way, let’s make something with our new found knowledge. Let’s start by rendering a simple sphere.
We can begin by opening up an empty network and adding a Camera COMP, a Geometry COMP, a Light COMP, and a Render TOP. This should now be looking very familiar.
This, however, is not a sphere… this is a Torus, so how do we change what we’re drawing? To do that we need to look inside of our Geometry Component. Using either the quick key or the zoom method let’s take a look inside of our Geo.
Here inside of our Geometry is just a single lonely torus. We should also notice that the display and render flags for this SOP are turned on. What we can learn from what we’re seeing here is that whatever is in the Geo Component with a render flag (purple) turned on will be used when rendering our scene. As a note about efficiency, we can technically render any SOP without placing it inside of a Geometry COMP. The difference is that SOPs located in a TouchDesigner network outside of a Geo are rendered on the CPU, while SOPs inside of a Geo are rendered on the GPU. Generally, the most efficient use of your system’s resources is going to be achieved by making sure that you’re taking as much advantage of your GPU as possible.
Inside of our Geo lets make a few changes. We can start by deleting the Torus SOP. Next lets add a Sphere SOP, a Box SOP, and a Null SOP. For now lets wire the Sphere SOP to the Null, and turn on the display and render flags for the Null as well. When you’re done you should have something that looks like this:
Now if we zoom out of our Geo we can see that we’re rendering a Sphere instead of a Torus.
Suppose that we want to see the box instead? To do that we would zoom inside of the Geo, and wire the Box to the Null instead of the Sphere. This is a prime example of how the use of a Null can be powerful in programming in TouchDeigner. If our geometry chain was much more complicated, or if we wanted to further change our source Surface Operator we could make all of those changes up steam of the Null. Practically this would mean that all of the alterations that were made did not require any changes in order to be targeted for rendering. Let’s take a look at how this might work using a single Null. First let’s add a Merge SOP to our chain, we’ll wire both the Box SOP and the Sphere SOP the Merge, and then wire the Merge to the Null. When we’re done we should have something that looks like this:
Well, surely something has gone wrong, right? No. Things are just as they’re supposed to be, it just happens that our Box in currently located inside of our Sphere. Let’s change the origin position of our Sphere and our Box so they’re not overlapping. To do this lets start with our Sphere. Clicking on the Sphere SOP let’s start by changing its radius to 0.5 for x y and z, and changing its center to -1 0 0.
Next let’s change the origin of our Box to 1 0 0.
Now we should be seeing something like this:
Alright. Now let’s back out of our Geo.
Excellent. Now we’re getting somewhere. Okay. This is all well and good, but what if I want to have different kinds of materials associated with my Geometry? Our last stop on looking at what we can do with our Geometry Component is going to be materials. Let’s click on our Geo and look at the Render Page. Here you’ll notice that you can assign a material to your geo.
You can assign a material to a geo by dragging and dropping it onto the geo, or by typing the name of the material into the Material Field. Let’s start by assigning our Phong1 to our geo1.
Well, that’s a subtle change. A Phong shader comes along with some lovely tools for shaded and more realistic rendering. That’s a topic all to itself, so for now let’s just look at how we can change the color of our Phong. On the RGB page of our Phong let’s click on the diffuse swatch, and change our color.
You’ll notice here that it’s changed the color of both our Box and our Sphere. The material that we apply to our Geo will be allied to all of the contents of our component. We can definitely apply different materials to different portions of our geo, but we’ll talk about that another day. If you’re feeling adventurous take some time to play with some of the different settings here in the Phong Mat.
Next let’s look at what happens when we apply the Constant.
Here we can see that the constant applies a single color to the whole surface of our Geo. It’s worth noting that at this point that we don’t need a Light Component when using a constant as a material. Unlike a Phong which uses a light source when rendering, the constant just emits constant color. Just like the Phong, we can also change the color of our constant. Let’s take a moment to try that out. Just like with the Phong you’ll click on the Constant, and look at the Constant Page:
Alright, now it’s time to look at what happens when we apply the Wireframe to our Geo.
The wireframe, like the constant, does not require a light in the scene to be rendered. In my opinion, some of the most interesting work comes out of mixed materials in rendering – combining phongs and constants, phongs and wireframes, et cetera. You’ll remember that back when we were putting our scene together we merged our Sphere and our Box. The diagonal line connecting them in the wireframe illustrates how they’re still connected. Before we leave the wireframe material let’s change just a few things. Like with our Phong and Constant, we can change the color. We can also change the thickness of the line. Let’s take a closer look at the Wireframe Page of the parameters and make some changes.
Let’s take a quick look at what our Camera can do for us. First, I’m going to switch back to the Phong as a material. In our Camera settings let’s move back slightly, and set our z rotation to me.time.absFrame.
Manipulating our camera changes our relationship to the geo in our scene, giving us the ability to shift our perspective. We can further alter this relationship by adding a Null Comp to our scene. After we add the Null Comp let’s tell our Camera Comp to look at the Null. We can do this by typing in the name of the Null in the Look At field in the Xform page of the Camera’s parameters:
Okay, so why do this? Well, before when we altered our camera’s location it was only pointed straight ahead. By adding a null for the camera to look at, we now have a focal point that we can manipulate. This is especially apparent if we translate our camera in either the x or y dimensions. Even thought the camera is moving left / right or up / down, it still stays pointed at our sphere and box.
Alright, let’s wrap this up by quickly looking at what our lighting has to offer us. Like all of the other Components we’ve looked at in this post, we are just scratching the surface of what’s possible. This look at rendering is intended just to get you started on the basics. Lets start by looking in the Light page of the Light’s Parameters.
Take some time to play with the color and dimmer settings the light to get started. When you’ve got a sense of how those work, the next attribute to take a closer look at is the Light Type. I’m going to change color of my light to a soft mauve, the light type to cone, and the cone angle to 8.
Now that you have a rough idea of what goes into rendering a scene in TouchDesigner, it’s your turn to play and discover. Happy real-time rendering. Want to see what I made, look at the Rendering TOE File.
Working with live streaming data is about as good as it gets when it comes to programming – this is especially true if you’re working on a project that looks to create a recognizable relationship between live data and media. What, then, is a person to do when placed in a situation without access to a live source of data? Whatever the project, the best way to tackle this problem is to find a source of prerecorded information. If you’re working on something like motion tracking, using a pre-recorded video is an excellent solution to this problem. What about sensors that aren’t image based? What if I’m dealing with a series of floats? What happens if those floats just come to me in a table? How can I take a series of recorded data points that live in an text file and make them move? That’s exactly one of the problems that came up for me recently, and I’ve got a handy trick that will make it easy to work with a data set in a table as though it were streaming into your TouchDesigner network live.
To get started, we need a file with some data in it. I made a quick spreadsheet with two columns. One starts at 0.01 and goes up to 1, awhile the other column starts at .99 and counts down to 0. If you’re following along, you can download that text file here (tabledata). In broad terms, what were going to make is a simple network of operators that moves through a table, pulling one row of data at a time, and then converting that table information into CHOP data. We can see where we’re headed if we look at our whole completed network:
So what’s happening here? In the DAT called “data” we have a table of recorded values. Next I use a select to remove the header row from the data, and another select to move through the rows of data. Using another table, a transpose, and a merge gives us a table that’s easy to convert into a CHOP. Now that we have a general sense of what’s happening in this network, let’s dig-in and get to work.
We’ll start by adding a Table DAT to an empty network. Rather than entering data by hand, we can instead just point TouchDesigner to a file that we want it to use. In the Table DAT’s parameters dialogue we’ll click on the plus button to the right of the “file” field and then locate the file that we’re looking to use.
In order to see our table data we need to click on the button “Reload File” so that our table will be populated with the information from the file that we’re using. Next we’re going to use a few Select DATs to manipulate the contents of our table. We’re going to use the first select to remove the header row of our table. To set this up, we’ll set our select to extract rows by index, starting at 1.
You’ll also notice that specifying that we’re extracting rows by index turns on a End Row Index value that’s driven by an expression (me.inputs.numRows – 1). We’re going to use the logic from this expression a little later on, so tuck that into the back of your mind for just a moment.
Next we’ll use another Select Table to move through the rows of our table. In adding another Select, let’s again set it up to extract Rows by Index. This time, however, we’re going to change the value of the start row and end row index to be the same. Doing things, you should notice that we get only one row of our table. Try changing the values of these parameters – as long as both fields contain the same number you’ll see only one row of information. We’ll animate that in just a moment taking advantage of this.
The next operator that we’ll add to this network is a Transpose DAT. A transpose will change rows into columns, giving us a result that’s two rows, rather than two columns of data.
While just these changing values are ultimately what I’m after, I would also like my values to have names. To do this I’m going to add another table DAT, creating two rows: xPos and yPos. I’m going to use a Merge DAT to combine these two tables – to make this work properly we’ll need to set the Merge to append columns. When we’re doing we should have something that looks like this:
Alright, not that we have our DAT string’s set up, let’s animate this table, and look at how to get some CHOP data out of these DATs. First let’s start by adding a Constant CHOP to our network. Let’s give our first channel a useful name, and then call our absolute frame count (me.time.absFrame).
Why use absolute frame? I’d like a steadily increasing integer that can be used to drive our progression through the rows of our table. Our absolute frame is an excellent candidate for this need – except that I don’t want to exceed the maximum number of rows in my table. To do this let’s add a Limit CHOP. First up I’ll need to set this Operator to Loop, I’ll also want to set this operator to start at 0 (Minimum).
For the maximum value, I want to use the total number of rows in our second table (the table that contained only data, without a header). I could hard-code this by entering 200 into the Maximum parameter of our Limit, but then I have to change this number whenever my table changes. A better solution would be to use an expression to pull this number from the table in question – which is exactly what that expression we saw earlier does. The expression we want to use then for our Maximum parameter is: op(‘select1’).numRows.
Now it’s the moment we’ve been waiting for. Lets make that table move! To do this we’ll use the row counter in to drive our location in our table – we’ll write some relative references in our select2 DAT to make this happen. In the Start Row and End Row Index values let’s use the reference op(‘limit1’)[‘row’] to drive the change in our table.
The last step here is to add a DAT to CHOP to our network. We’ll add this at the end of our network, and drag the target DAT onto the CHOP.
There we have it. We’ve just taken a static table full of data, and turned it into a channel data that changes over time. For extra credit, add a Trail CHOP to the DAT to see what your data looks like.
Back when I was working on The Fall of the House of Escher I became obsessed with live z-displacement in the media. That particular show was wrestling with questions of quantum physics, time, reality, and all manner of perceptual problems and so it made a lot of sense to be love the look and suggested meaning inherent in the displacement of an image in a third dimension. This particular technique is perhaps most well known to us because of the Rutt Etra Video Synthesizer. This video synth was an attempt to begin manipulating video in same manner as sound was being manipulated in the 1970s, and was truly a ground breaking examination of our relationship to video – live video especially. One of the most famous elements of the Rutt Etra Synth was z displacement, the change in the depth of an image based on its luminance.
You can get a better sense of what this kind of effect looks like by playing with the Rutt Etra-izer online. This simple tool lets you play with one slice of what the original video synth did. Additionally, if you’re a mac user you might want to check out what v002 had to share when it comes to plug-ins, as well as some thoughts from Bill Etra. You can find more from v002 here: Rutt Etra v002
So that’s all well and good, but what am I after? Well, in the pursuit of better understanding how to program with TouchDesigner, as well as how to explore some of the interesting ideas from the 1970s, I wanted to know how to replicated this kind of look in a TouchDesigner network. There are plenty of other people who have done this already, and that’s awesome. I, however, happen to subscribe to the kind of art philosophy asks students to copy the work of others – to practice their hands at someone else’s technique, to take what works and leave what doesn’t. Art schools have often required students to copy the work of masters in order to foster a better appreciation and understanding of a particular form, and I think there’s a lot too that in programming. So today, we’ll look at how to make this kind of effect in TouchDesigner and then ask how we might manipulate this idea in ways that differ from how the original method was intended to work.
To begin, this idea started when I was looking through the wiki page about Generative Design. Specifically, the sample network talking about image manipulation really got my attention. Buried in this example network is something that’s Rutt Etra flavored, and it’s from this example that I started to pull out some ideas to play with. As we work our way through this example it’ll be easy to get lost, frustrated, or confused. Before that happens to you, take a moment to think about what we’re trying to do. Ultimately, we want to take an image (video later, but for now an image) and add together the RGBA values of an individual pixel and use that number to transform said pixel in a z dimension. In other words, we’re really after creating a faux depth map out of an image. It’s also important to remember that using an image that’s 1920 x 1080 means we’re talking about 2,073,600 pixels. That’s a lot of points, so we’re going to start by creating a grid that simplifies those dimensions – partially for our own sanity, partially for the sake of the processing involved, and partially to remain true the aesthetic of the original idea. Once we replicate the original, then we can start to talk about where we can start to play. That’s enough disclaimers for us to get started, so let’s do some programming.
Let’s start by looking at the whole network:
From this vantage point we can see that we’re going to make a network that uses a little bit of everything – some texture operators (TOPs), some channel operators (CHOPs), and some surface operators (SOPs).
First things first, let’s make a new container and dive inside. To our empty container let’s start by adding an In TOP as well as a Movie In TOP. We’re going to start by connecting the Movie In to the second input on the In TOP. Why? The Movie In TOP is going to allow us to pass a video stream into this container. We may, however, want some image to show up while we’re working or when we don’t have a video stream coming in; this is where that second input comes in handy. This gives us a default image to use when we don’t have a stream coming into the container. Here’s what you should have so far:
Next, we want to down sample some of this image. This step is actually helping us plan ahead, we’re going to use some expressions to set the parameters of some of our other operators, and this step is going to help us with that. To do this down-sampling, we’re going to use the Resolution TOP. After that we’ll end our TOP string in a Null TOP. All of that should look like this:
Before we move on, let’s make on change to the resolution TOP in our string. Open up the parameters window for your resolution TOP and on the common page change the Output Resolution parameter to Quarter:
There are a number of different transformations that we’ll need to do with the data from our image, we’ll start this process by first moving to Channel operators. If we think back to where we started this process with an intent to transform luminance into depth, then we know that one of the operations we need to complete is to add together the RGBA values of our pixels in order to have a single number we can use to manipulate a surface. To get us started we first need to convert our image into channel data that we can manipulate. To do this we’re going to add a TOP to CHOP to our network. This operator is going to allow us to look at our TOP as if it were all Channel data. We’ll see what that looks like in a moment. First, however, let’s make a few changes to our operator. In the Image page for the TOP to make sure that you’re set to “Next Frame” as the download type. On the Crop page you’ll also want to make sure that you’re set to the full image:
Next we need to assign a TOP so we have a texture that we’re converting into Channel data. You can do this by dragging the TOP onto this CHOP, or you can enter in the name of the target TOP on the image page. Your TOP to CHOP, should now look something like this:
This viewer on this operator can be taxing on your system, so at this point I’d recommend that you click the small bulls-eye in the upper left hand corner of your CHOP to turn off the viewer. This will save you some system resources as you’re working in your network and keep you from seeing too much lag while you’re editing this network.
The next series of channel operators is going to allow us to make some changes to our data. First we’ll use a Shuffle CHOP to reorganize our samples into sets of channels, then we’ll use some Math CHOPs to add together our pixel values and to give us some control over the z-displacement, and finally we’ll use a Rename CHOP to make it easy to use this data.
Let’s start this process by connecting our Top to CHOP to a Shuffle CHOP and setting our method to be Sequence Channels by Name:
Next we’ll add our Math CHOP and set the combine channel operation to Add, let’s also set the multiply value to 1 /3:
Next we’re going to add a slider, and another Math CHOP. Why? Great question. For starters, I want to be able to control the strength of this effect with a slider. At some point I’m going to want to be able to drive the strength of this effect, and a slider is a great way for us to do that. Why another Math CHOP? Another excellent question. While we could just use one Math CHOP and apply our slider there, that also means that there’s no way to isolate the effect of the slider. There’s some redundancy here, but there’s also a little more flexibility in the isolation of applying different alterations to our data set. Is this the best way to code a final component, maybe not, but it is a fine way to work with something that’s still being developed. Alright, let’s add our slider and second Math CHOP:
Next we need to write a simple expression in our math2 operator in order to be able to use the slider as an input method. On the Multi-Add page we’re going to use the out value from the slider as the value that the incoming channel information is multiplied by – if we think about the structure of our slider’s output we’ll remember that it’s a normalized value ranging from 0 – 1, and we can think of this as being the same as 0 – 100%. Alright, here’s our simple reference expression:
op( ‘slider1/out1’ ) [ ‘v1’ ]
In plain English this expression reads: give me the number value of the channel called ‘v1’ from operator called ‘out1’ that’s inside of ‘slider1’. If you click on the + sign on the bottom right of your slider (making it viewer active) you can now move it left and right to see the change in the number value of your math2 CHOP.
Before we move away from this portion of our network we need to add one final operator, a Rename CHOP. The Rename CHOP allows us to rename the channels inside of an operator. Later we’ll want to be able to use this number to replace an value from a surface operator chain – in order to do that easily we need to rename this value. In the to field of the rename CHOP type tz:
Now that we have started the process of converting our Texture into a channel data, we need to think about what we’re going to do with that information. We’re going to start by adding a Grid SOP to our network. We’d like this operator to pull some of its dimensions from the our video – this will make sure that we’re dealing with a surface that’s the correct aspect ratio. In our Grid SOP we’re going to use some simple expressions to pull some information from our Null1 TOP (remember that our Null1 is at the end of our TOP chain). First we’ll use the height and width of our Texture to set the number of rows and columns – we can use the dot call method to ask for the height and width of our TOP with the following expressions for rows and cols:
op( ‘null1’ ).width
op( ‘null1’ ).height
Next we can use internally call the number of rows and columns to for our height and width with the expressions:
We’ll also want to make sure that we’ve set our grid to be a Polygon with Rows as the connectivity type.
Now we’ll need to get ready for our next step by adding a CHOP to SOP. This operator is going to allow us to create some geometry out our CHOP data.
What gives? Well, in order for this operator to work properly, we need to have some feed it some CHOP data.
Now we’re finally making progress, even if it doesn’t feel like it just yet. For starters, we have our Texture Operators converted into channel data, we have a piece of geometry that we can alter based on the dimensions of the input Texture, and we’re ready to combine our Channel data with our Surface data, we just have some final house keeping to do.
Let’s start by first converting our Grid SOP to a CHOP with a SOP to CHOP.
Like we did before we’re going to use a Shuffle CHOP, but this time we’re going to set it to Sequence all Channels:
Next we’ll use a Math CHOP to find the absolute value of our shuffle – we can do this by setting the Channel Pre Op to be Positive:
Next we’ll use an Analyze CHOP to find the maximum value coming out of our Math CHOP.
Now we’re going to normalize our data against itself. We’re going to add another Math CHOP to our network. We’ll connect it’s input to our sopto1 CHOP and in this case we’ll use the Multi-Add page and use 1 / the maxim from our analyze CHOP. We can do this with a simple reference expression:
1 / op( ‘analyze1’ ) [ ‘tx’ ]
All of that should look something like this:
We can finally start putting all of this together with one more Math CHOP. This final Math CHOP is going to combine our SOP to string, and our TOP to string. You’ll want to make sure that the SOP string is in the first place on the Math CHOP, with the TOP string coming in underneath. Next make sure that Combine Channels is set to Add, and that Match By is set to Channel Name.
Now let’s end this set of operations in a Null, and move back to where we left off with our surface operators. Back in our Chop to SOP, let’s set our CHOP reference to be our last CHOP null (in my case it’s Null2). All of that should finally look like this:
At long last we’ve finally transformed a grid in the z direction with the information from an input Texture. Ba da bing bang boom:
Dear flying spaghetti monster help us… this is has been a lot of work, but so far it’s not very fancy. What gives? Well, if we really want to make something interesting out of this, we need to render it – that is, we need to change it from being just some geometric information back into some pixels. If we think back to what we learned while we were playing with Instancing, rendering isn’t too hard, we just need a few things to make it work (some geometry, a camera, and a light source… this time we’ll skip that last one).
For our render to work properly, we need our chopto SOP to be inside of our Geo. At this point you can use your favorite piece of profanity and get get ready to remake this network OR you can HOLD YOUR HORSES and think about how we might solve this problem. My favorite way to address this kind of issue is to jump inside of the geo component, add an In SOP and set it to render and display. This means that we can pass our geometry into this component without needing to encapsulate all of the geometry inside.
Now let’s connect our chopto to our inlet on the Geo comp:
Ideally we want the image from our original texture to be used when rendering our z transformation. To do this, let’s jump back inside of our Geo COMP and add a constant Material. A Constant, unlike a phong, isn’t shaded. In other words, it doesn’t render with shadows. While this isn’t great in some respects, the pay off is that it’s much cheaper to render. While we’re getting started, this is a fine material to start with. We’ll also want to make sure that our Constant is using our original TOP as a color Map. In the Color Map field we can tell this operator to look for the operator called in1 in the directory above with the following call:
Next we need to apply this material to our Geo. To do that let’s jump out of the Geo comp and head to the render page of the parameters window. We can tell our Geo to look for the material called constant1 that’s inside of the geo comp like this:
Holy macaroni, we’re almost there. The last step we need to take is to add a Render TOP to our network:
At long last we have finally replicated the z-translation aesthetic that we set out to emulate. From here you might consider changing the orientation of the geo comp, or the camera comp for a more interesting angle on your work.
That’s all well and good, but how can we turn it up a little? Well, now that we have a portion of this built we can start to think about what the video stream going into this operation looks like, as well as how we modify the video coming out of it. Let’s look at one example of what we can do with the video coming out.
I’ve made a few changes to our stream above (some simple moving animation, and changed the resolution op), to have something a little more interesting to play with but it’s still not quite right. One thing I want to look at is giving the lines a little more of a neon kind of look and feel. To do this I’m going to start by adding a Blur TOP to my network:
Next I’m going to add an Add TOP and plug both of my TOPs into it, adding the blur back to the original render:
Now it’s your turn to play. What happens when you play with the signal processing after your render, what happens when you alter the video stream heading into this component. Also, don’t forget that we built a slider that controls the strength of the effect – play and make something fun.
Looking to take a closer look at what makes this process work? Download the tox file and see what makes this thing tick: rut
“What if I want ten-thousand. Literally. Ten Thousand.” Back when Wonder Dome was in full swing I had the great honor of meeting some of the folks from Concordia’s Topological Media Lab. While some of their folks were in residence with ASU’s school of Arts Engineering and Media. They’ve spent a significant chunk of time building a system that they call Ozone, which is largely powered by Max MSP and Jitter. While they were visiting with us at Wonder Dome, they had lots of questions about TouchDesigner, how it works and how it was working for us. We talked bout a number of different things that day, but my favorite moment came over lunch. We were talking about making surfaces, and the flexibility and challenges involved. After we had added a sphere to our network, one of the team members asked me “what if I want to make a lot of them. What if I want to make ten-thousand. Literally. Ten Thousand.” There are lots of ways you might do this, but one of my favorite ways to think about making multiple copies of the same object is with instancing.
Say what now?
Instancing is a fun way to make lots of copies of a piece of geometry and to place those copies very specifically. On the face of it, that sounds a bit academic. Let’s look at a specific example to unwrap that idea a little bit. Let’s look at a raspberry. We can think of a raspberry in a few different ways. On the one hand it’s a small oval like berry. At the same time if we look closer we can think of this single object as an array of spheres tucked next to one another in a patterned way. Instancing, as a method, allows us to do something like this relatively easily and quickly. On top of that, it’s a ton of fun, and unlocks a whole new set of possibilities when it comes to creating geometry in TouchDesigner.
So where do we start?! Well, we need to start by taking some time to do a little bit of thinking about how instancing works. For starters, instancing is something that you do to a Geometry COMP. As a component, we can do things with Geo’s that we can’t do with regular surface operators. A Geometry COMP is also one of the main ingredients when it comes to rendering surfaces. First we’ll look at how we can work with the Geometry COMP, then we’ll talk about what that means when it comes to rendering.
Okay. Let’s start in an empty network and create a new Geometry COMP.
We could just work with the torus that’s in our Geo to get started, but let’s make a few changes so we have something that’s a little more fun to play with. Dive into your geo, and let’s get situated.
Inside our geo you should only see a single surface operator, a Torus SOP. You should also notice that there’s a little purple dot, and a little blue dot that are in the bottom right hand corner. These are two of the Operator’s flags – the purple dot is the render flag, and the blue dot is the display flag. Flags let you change the behavior of your operators in some interesting ways. The render flag, like it’s name, tells you if something is being rendered or not. Why not render something?! Well, you might find yourself in a situation where you need a SOP to help control the movement of another piece of geometry, but you don’t necessarily want that guiding element to be rendered. In this case you’d want to make sure the render flag is off. The display flag allows a SOP to be seen in the Geometry viewer. Like our other example, there might be a time when you don’t want to display a particular SOP while you’re making a change, in this case you’d want to make sure the display flag is off. There’s lots more to say about Flags, but that should be enough to make you dangerous.
Alright, now that we know a little more about what’s happening in our Geo let’s make some changes. For starters, let’s delete the Torus, and add a Sphere SOP and a Null SOP. Let’s connect these two operators, and make sure that we turn on the display and render flags on the bottom right corner of our Null. While we’re here let’s change the name of our null to DISP for Display.
Let’s also change the size of our sphere. In the radius parameter field lets change the three values to 0.15.
Now when we back out of our Geo we should see our sphere instead of a torus.
Alright. Now we’re going to add another sphere to our network, but this time at the same level as our Geo. Let’s also connect that Sphere to a null.
The next part is gonna start to get funky, so let’s help ourselves out before the world starts to turn upside down on us. Head over to your sphere, and make it viewer active (hit the small + in the bottom right corner), next hit the letter w on your keyboard. What you should now see is the wire frame of your Sphere SOP.
Okay. Let’s just leave that be for now, but know that we’re going to come back to it so we can better understand what’s coming next. Now we’re going to add a SOP to CHOP to our network.
The SOP to CHOP is a special kind of operator that allows us to change one type of operator to another. In our case, we’re going to change our null1 into CHOP data. To do this, we’re going to grab our SOP null1 and drag it on top of our CHOP sopto1.
What manner of witch-craft is this?! Alright, what we’re looking at is the channel information from our SOP. Let’s think back to our wire frame Sphere for a moment. Each one of those intersecting lines represents a point. Each point has three values associated with it in terms of position – x, y, and z. Our SOP to CHOP is giving us a graph that represents all of those points. That’s awesome.
Why is that so awesome? Because now we can instance like there’s no tomorrow. Let’s head over to our Geo COMP and take a look at a few things. If we look at the parameters of this COMP, we’ll notice a page called Instance. Click that bad boy. Here we want to do a few things. First we want to turn on Instancing. Next we’re going to tell our Geo that we’d like to use our CHOP called sopto1. The last thing we need to do here is to help our Geo understand what to do with our CHOP data. In our case we’re going to tell the Geo to use tx for tx, ty for ty, and tz for tz – we’re matching up the location of our points from our Sphere to the points where we’d like instances of our geo. If that doesn’t make sense, that’s okay – for now follow along and this should make more sense soon.
Now if we look at our network we can see some of the magic that’s happening with instancing.
To get a sense of how powerful this really is, let’s go back to our Sphere here in our parent network. In the parameters page, let’s change this sphere from being drawn as a mesh to being drawn as polygons.
We can now see that our instances are happening at each of the vertices of our polygon. Let’s try one more change to see how this powerful this is. For starters, let’s turn up the frequency count – I’m going to turn mine up to 10. Next let’s add a Noise SOP to our chain of operators between the Sphere and the Null.
Now we’re starting to see some magic happen. At this point we’re now transforming the position of our instanced spheres with a noise algorithm that’s being applied to our original geometry.
An Aside About Efficiency
As a quick note, it’s important to consider that this is not the most efficient way we could have programmed this. In TouchDesigner SOPs are a CPU operation, unless they’re encapsulated inside of a Geo COMP. What does this mean? Well, it means that the noise SOP in our network is a CPU operation, and not a GPU operation and depending on your machine for this example your mileage may vary.
Alright, so now we’ve made something awesome… but how do I use it?! Well, in order to actually use something like this in a project we need to render this geometry. Rendering requires a few different things to be in place, we need something to render (a Geometry COMP), a perspective to render it from (a Camera COMP), and a light (a Light COMP, we don’t always need a light but for now lets add one anyways). Let’s start by adding those COMPs to the network.
Alright, now we can add our final ingredient to our network, a Render TOP. Our render TOP is going to use the three COMPs in our network to actually draw the pixel values of our scene. At this point your network should look something like this:
Nice work. You’ve not instanced your first set of geometry and rendered it. Now for extra credit, go back and consider changing some of your source SOPs… what happens if you change what’s in your Geo into a box or torus? What happens if you change your Sphere in the parent network into a grid instead? Happy instancing.
The other day I took a moment to write down how to change a horizontal slider into a vertical one in TouchDesigner. This got me thinking, and I realized that in addition to a good ol vertical slider, a 2D slider (or an XY slider) would be another handy tool to have your disposal. Why make these yourself when there are a ton of tools already made in the TUIK tool set? Good question – there are a ton of wonderful pre-made control tools at our disposal there, but if we want to take some time to better understand how those tools work and and the programming logic behind them then it’s worth taking some time to make our own versions. It’s part mental exercise, part challenge, all fun. Maybe.
For starters, why use a 2D slider anyway? There are lots of things you can do with these kinds of interface objects. These are especially handy for corner pinning objects – this control surface reports out an x and y value, which you can then scale and apply to the corner of an image. With four of these 2D sliders you now can control the four vertices of an image. More generally, these kinds of controls are useful when you want two values to come out of a single control surface. That’s all well and good, but lets get to making one, shall we?
We’re going to start this process with a regular horizontal slider. Let’s begin by adding a Slider COMP to your network.
Eventually, we’re going to make some changes to the parent object for our slider, but for now we’re going to dive inside and re-arrange the guts of our component just a little. When you get inside of the Slider you should see a familiar set-up: a Panel CHOP, an Out CHOP, and a Container called Knob.
In another post I talked about how we can decode what’ s happening in the expression in the x parameter field of our Knob. In summary we can decode the expression this way. The expression:
In English this might read – my parent’s panel u value (.5) * my parents width (100) – my panel’s width / 2. Great. Why? Well by multiplying the u value (a normalized value) with the width of the panel I get my position in terms of pixels – almost. The last thing I need to do is to take into consideration the width of the knob (divided by 2 so you split it’s value equally left and right).
This is all excellent news, but how does it help us with a 2D slider? First we need to think about what’s happening in a 1D slider vs. a 2D slider. In our regular horizontal (or vertical) slider we’re just tracking changes in a single direction – left and right, or up and down. In a 2D slider, we want to track both of those changes – up and down and left and right at the same time. To do this, we’re going to take our existing expression, and the expression that we used in our vertical slider and use both of them.
We already have an expression that allows us to track horizontal change, so how do we change that to track vertical change? In the expression above we’re going to make the following changes – first we want to use the value “v” instead of “u” that’s coming out of our panel (If you just had a moment when you said – “u, v, what now?!” take a break and read the Wikipedia page about uv mapping). We’ll also need to look at panelh (panel height) instead of panelw (panel width). This means our new expression for watching the change in a vertical dimension is:
So far so good? Okay, next we’re going to use this expression in the Y parameter field of the knob component. This means that we have the two following expressions that are in the x and y parameters of our knob component:
x = me.parent().panel.u.val*me.parent().par.panelw-me.par.panelw/2
y = me.parent().panel.v.val*me.parent().par.panelh-me.par.panelh/2
Okay, now we’re going to make one more change to our knob’s parameters while we’re here. Instead of this control object being a rectangle, let’s make it a square. We can notice that its current width and height are 5 and 20 respectively. Let’s change that to 10 and 10. You certainly don’t have to work with a square knob, but I like the way this looks for this type of control. We also need to make some changes to our Panel CHOP.
The panel chop gives us all sorts of useful information about what’s happening in a panel at any given moment. In our case we can use this CHOP to tell us the horizontal and vertical position of the knob that for our slider. If we look at how our panel is currently set up we can see that it’s selecting the u value and renaming it “v1.” We want our slider to send out both u and v information, so let’s change the select to read “u v” – separating our variables with spaces tells TouchDesigner that we’d like both to be selected. We can also rename this values while we’re here. I choose to rename them xPos and yPos (x Position, Y Position). Choose whatever name makes sense for you, and that it’s too long – if you end up needing to call these values in another expression having a shorter name is helpful.
Now let’s take a moment and head back up to our parent container. Here in our parent let’s change the width and height to be 100 and 100. We should now see a square panel with a square inside of it, that we can drag about.
If we connect a Null CHOP or a Trail CHOP we can see the values reported out of our new tool. Sweet.
Alright, this is pretty cool, and I’m mostly happy with it… but it’s missing something. One of the things you’ll see with other 2D sliders are guild lines that pass through the middle of the central knob – these help you visually maintain a sense of where your control object is pointed, and let’s face it they just look cooler.
Okay, so let’s add some guild lines, where do we start?
We can begin by diving back into our Slider component. Let’s work the easy way, and take our knob component, and make two copies of it. We can call these two new additions xWire and yWire. Instead of hard coding in the dimensions of these wires, we are instead going to use some expressions to define what they look like. Why? Well, while we certainly could do this by hard coding the numbers it also means that if we make any changes to the parent component, it also means we need to make changes several pieces inside of the component. This works just fine if you’re only making something you want to use once, but if you want to make a component that you can use and reuse then using some expressions is going to save you a ton of time – and as a bonus you’ll learn more about using python expressions in TouchDesigner. Enough of my soap box, let’s do this.
Le’ts start with our xWire component. I’d like my guild lines to be one pixel tall (or wide for the yWire), and the same width as the parent component. To make this happen let’s use the following expressions in the Width field:
In plain English this reads – what is my parent component’s panel width parameter. By using this expression we know that the width of this object will always match the width of our parent. Perfect. Before you celebrate too soon we need to add one more expression. We’d like the line that we’re drawing to stay aligned with our central knob (no really, we do want that). For this we need to keep in mind that our xWire that stretches the width of the parent component needs to move vertically – this may seem counter intuitive, but it’ll make perfect sense here in one moment. How do we do that? Well, luckily we were just practicing ways to make a knob stay aligned to a position when we made a vertical slider. We’re going to use some of the same ideas from that expression here. In the Y paramter field we’re going to use this expression:
op(‘panel1’) [ ‘yPos’ ] * me.parent().par.panelh
Say what now?! In plain English we’re saying – look at the operator called “panel1” and find the value called “xPos”, multiply that by the panel height of my parent. Now we should have a working xWire component. Before jump up to look at our 2D slider, here’s what you should have so far:
If jump out of our 2D slider and take a look at what we have so far, we should see something like this:
This is almost what we want, right? Now we just need to repeat this process to add our yWire guideline. Let’s dive back inside of our slider to finish up. For our yWire we’re going to set the width of our component to 1 and use an expression to change the height:
We’re also going to use an expression to change the X position of our component:
op(‘panel1’) [ ‘xPos’ ] * me.parent().par.panelw
These are like our other expressions just calling different values. At this point you should have something that looks like this:
Now we’re finally ready to back out of our 2D slider, and admire our hard work.
Alright, the last thing to do after you’ve done all of this hard work is to save this component as a .tox file that you can reuse. If you’ve never done that before, you can read how that works here.