TouchDesigner | FB HelpGroup | Presets

From the FaceBook TouchDesigner Help Group


In Looking at the design of a simple ue system..I can’t wrap my head around head following to start construction
Theoretically there would one dat table with the preset name description and values
One Table Per Preset or am I looking at setting and recalling the states wrong, and there is an easier way to save and set the state using storage ??
I will build it myself.. looking for the best approach to start working on this.

Matthew
There are lots of ways you might tackle this. Using a table – you could use a table per preset switching between tables, or one table with all presets where you select the row or column you need.

If you’re using storage you might think about how you store your presets as dictionaries or lists – depending on the complexity of your presets.

IMHO – tables tend to be more straight forward, and for simpler systems are fast to build. Tables are also useful for distributed systems when you need / want to use Touch Out DATs to push information around a network.

Dictionaries are great for complex systems that have lots of moving parts. It takes some additional time to set-up and debug, but is very flexible and extensible once you have the scaffolding in place. For a simple system, however, this might be a little overkill – it’d be easy to loose a lot of time in the building of your data structure, rather than in building your system.

I might start by considering scale first… how many key / value pairs per preset, and how many machines are you running this one. In my experience, the more I’ve spent time thinking through how the larger scope of a project is going to function, the easier it is to make a decision about the appropriate data structure for a project.

M
I too want to build my own left-mouse->recall, right-mouse->save -table preset system. I started one but got stuck with it unsure.

There’s this on the forums: https://www.derivative.ca/Forum/viewtopic.php?f=22&t=6582 It’s great, but it was also a bit too much to reverse engineer. I’d like to have my own system. Not meaning to hijack this thread but just thinking out loud. If i have say 8 tables with the correct amount of rows, and i use “rmouseclick” to save the CHOP values of my sliders to those tables, and then “lmouseclick”(or whatever it was called) to recall the correct table and overide the slider CHOP value, i can haz presets. Sadly i cant write the python to read/write from table. Probably isn’t too hard

Matthew
I think I have an example of something like this somewhere Marko – I’ll see if I can dig it out in the next couple of days.

M
Thanks Matthew… again.

Matthew
I’ll probably put together an example that does this with tables or with storage – it’s handy to see the guts of how both of these things work in a simple kind of configuration

J
Thanks, it would be an interesting read into both to see which approach I am going to use


For M and J – first installment. Storing presets in a table data structure.

Screenshot_072915_012758_AM

Move the sliders or type in values from 0 – 1.

Right click on the keys at the top to Record positions.
Left click on the keys to Recall positions.

Screenshot_072915_012822_AM

More thanks than I can ever express to Keith Lostracco – it was one of the example files he posted last year that pushed me to better understand the table COMP, this example pulls a lot of inspiration from the TOX he originally posted.

Next I’ll put together an example of working with dictionaries as a different kind of data structure.
Follow up for M and J here you’ll find a python dictionary approach.

Same as the other TOX:

Screenshot_072915_012844_AM

Right click records, left click recalls.

You’ll notice in dictMethod/table1/recall that a try and except is used for any keys that do not yet exist. Defaults from storage with a dictionary can be tricky, and this is one of many ways around this.

Screenshot_072915_012928_AM

The benefit of a method like this would be scale – dictionaries can hold other dictionaries (just like lists can hold other lists). This means that you could use another set of buttons to specify which dictionary was recalled. You might want to save presets by venue, or media type, or any number of attributes. You could achieve the same result with tables, you would just need to spend some time thinking about how to appropriately structure that approach.

Hope this helps.

Download the examples from GitHub

  • presetsDictMethod
  • presetsTableMethod

TouchDesigner | Email | Cube Faces

Original Email – Thu, Jul 23, 2015 at 6:01 AM

Hi Matthew,

i love to work along your tutorials and learn more  about my best new friend since last year “Touchdesigner”. Theres is one idea i take with me. thought about a cube geometry with different pictures on each side. thats for the moment no problem, but how can i get the pictures changing automatically after a few seconds out of a folderDat. Any ideas?


Reply – Thu, Jul 9, 2015 at 12:10 AM

There are countless ways that you might try to solve this problem – some will scale better than others. The example included here, does well for 1-5 cubes, but probably wouldn’t scale well for more than 10. At 10 or more, you’d probably want to consider some  of the challenges of instancing in a slightly different way.

Screenshot_072715_120916_AM

At any rate, here’s an example with several different considerations. The first is just placing different numbers of the different sides of a cube using a cube map.

The second is placing different images.

The third takes the contents of a folder DAT and changes all of the faces of the cube simultaneously. Every three seconds all of the images change.

The fourth example randomly changes the face of the cubes – both the order in which faces change is random, as is the selection of images. The images are shuffled so that you’re only likely to see as few repeated images as possible. I don’t know how strong your python is, but when you look at this example you’ll see that I wrote several functions in an embedded module that handle all of the logic of this operation.

Best,
Matthew

You can download the example file from GitHub – cube.zip

cubeGif

TouchDesigner | Email | Shrink Instance

Original Email – Mon, Jul 13, 2015 at 9:43 PM

Hey Matt!
So I’ve tried banging my brain around 10 different ways to where I’m sure it now resembles a sphereSOP – noiseSOP but I’m still coming up a little short. Naturally, I thought about your THP 494 Shape lesson but I was not able to figure out what I’m missing so I’m coming straight to teacher for some guidance.

I’m trying to recreate the attached image. I’m using a GridSOP, MetaballSOP – MagnetSOP which sort of works but I’m missing some very important part of the process. Can you have a look at this and give me a hint as to what I’m missing?

You certainly do not have to correct the work unless you want to but a point in the right direction (haha) would really help me sleep tonight!


Reply – Tue, Jul 14, 2015 at 12:23 AM,

That was a good brain teaser.

I’m including two different approaches to solve this problem. The first looks at using your magnet approach, and the second is more GPU focused.

Screenshot_072115_120220_AM

Following your model with magnet, you had just about nailed it – all of the information you needed was in that SOP to CHOP, it was just a matter of reformatting it. In the magnet base, I grabbed the tz channel of the SOP to CHOP, scaled it, and renamed it scale, merged it in your instance CHOP, then applied it to the scale parameter of your instances. Pretty right on with your existing approach. The draw back here is that the magnet SOP is very expensive – nearly 7 milliseconds by itself – bottle-necking your performance at about 30 fps. This also keeps you pretty limited in terms of the number of points you can work with – CPU bottlenecks can be tricky to work around.

MetaBall

So, I started to think about how I would solve this problem on the GPU, and remembered that an array of pixels is just a different kind of grid. The second approach translates a circle TOP, and then converts that to CHOP information, merges this with a SOP to CHOP (using the xyz data from a gird), and then instances from there. I was looking at over 1400 instances without a problem. The challenge you’ll encounter, however, is when you try to replicate that many source textures. I did a quick test, and things slowed down when I had that many texture instances drawn using the newer texture instance approach. I was, however, able to get performance back up to 60 FPS if I loaded a 3D Texture Array TOP, and then turned off it’s active parameter. Markus uses this trick often.

circleTOP

Anyway, that’s as far as a I got in the little bit of time that I carved out. The next steps (in terms of mimicking your source image) would be to get the displacement right pushing instances up and down to make an opening in the center of the array.

circleTOPdisplacement

Alright, well I put another hour into this because I got really interested in the idea of displacement. I think this still needs a little more work to really dial it in, but it’s a solid starting point for sure.

Screenshot_072115_120617_AM

Hope this helps.

Best,
Matthew

scaleDisplacementExample

Look at the example file on GitHub – shrinkInstance_locked

TouchDesigner | Email | Realtime DNA

DNArender

Original Email – Mon, Jun 22, 2015 at 11:53 PM

Hello,
First of all.. Thank you for the knowledge , that you share – It is great . I watch all your tutorials in Youtube and they help me a lot . I would like to ask for a tutorial or advice how to make something in Touchdesigner . Its a Audio reactive DNA . I have seen tutorials for Qaurtz composer and i have done it there . I found one in the TD forums , but its not so good in a way that there is a limitation in the twisting of it .

this is mine in QC. if you find it interesting enough to be recreated in TD it would be great.
Thank you .


Reply – Thu, Jul 9, 2015 at 11:30 PM

Sorry for the late reply, I was out of the country when you initially wrote and have been playing catch up with email since I returned.

This is an interesting challenge, and was fun to re-create in TouchDesigner. I didn’t work on the audio reactive components at all – I’ll leave that to you to explore. You should be able to see how this is set up with some instancing and lighting to create a dynamically changing scene. I think there’s a bit more to play with in terms of rendering and layering, but this should be a solid starting point for you.

I hope this helps you get moving in the right direction.

Cheers,
M

DNA

Download the example from GitHub – DNArender.toe

TouchDesigner | Email | Level of Detail

LOD

Original Email – Tue, Jul 7, 2015 at 10:27 PM

Hi Mathew, I’m a 3D artist trying to learn touch designer and your videos have made a huge difference for me (I’ve been at it for a while), you also even helped me on the forums recently figure out how to control rotation of my instances along a curve. (if you’d like to see the finished version of that project by the way you may here:

What I desperately want to learn how to do now is build an LOD system based on distance to the camera that would change the material and replace the geo with a polyplane textured to look like the full res geometry or along those lines. In general i’m having a lot of issues figuring out exactly how to optimize my scene, even after extensively cleaning and rebuilding some of my networks I only noticed minuscule performance changes. I would greatly appreciate any advice you may have on this subject? Thank you so much for your wonderful website!


Reply – Thu, Jul 9, 2015 at 12:10 AM

I’m glad all of the tutorials have been useful – I certainly enjoy making them.

Optimization is one of the hardest parts of working with touch, and I certainly feel your frustration in trying to get a system up and running that’s rendering high quality visuals at a respectable frame rate.

If you haven’t had a chance yet, I highly recommend reading through the Optimizing for Geometry for Rendering article on the wiki. I frequently return to that piece of documentation when I’m stuck.

Also look at:

The better your understanding of how Touch is rendering, the easier it becomes to think about how you optimize and reorganize your scenes. A general rule of thumb that’s important to keep in mind is also the importance of translating objects at the Geo level, rather than performing transformations or changes to SOPs (sorry if this is something you’re already thinking about). Geos are computed on the GPU, while SOPs are computed on the CPU – this is something that will make or break you. The less you’re actively cooking your SOPs the better your performance will be (in general). This also makes it extremely important to understand instancing (and I’d include texture instancing in here as well). Without seeing any of your scenes, it’s hard to tell you where your bottlenecks are, so these are mostly my best guesses.

When possible, use CHOPs for your animation and control. DATs and Python are excellent additions to TouchDesigner, but in general CHOP math is much faster – calling and interpreting your scripts is slower (generally, though not always) than the hard coded operations performed CHOPs.

Don’t forget that the performance you see in the network viewer is always slower than when you’re in perform mode. It’s hard to know exactly how well you’re performing, unless you add a performance monitor to your output window so you can watch your frame count while you’re not rendering your programming environment.

Remember that Touch is a pull based system, not a push system. Only the computation needed for rendering the scene is run – this, however, can get you into trouble if you’re using complex sets of composites and crossing elements. In general, I try to always use select operators when possible, rather than ins and outs. It’s also a good idea to think about deck systems (AB crossing) when possible.

Finally, your question about LOD systems. I haven’t done much with this, so I’m doing a bit of guessing here. In the attached TOE file there are two different examples – project1 and project2. In both projects, make the Container’s viewer active, and click on the geometry and then on empty space.

Project 1 takes the tack of adjusting the level of detail for the selected geometry in relation to the distance from the camera. In general, I don’t know that this is a good approach. SOPs are the most expensive operators, and my best guess is that this approach wouldn’t necessary scale. I don’ t know that for sure, but that’s my hunch.

Project 2 takes the approach of subbing in a high resolution image for a piece of geometry based on camera distance. This would take some futzing, and would get complicated the more complex your scene is… but without knowing what direction you’re headed exactly – this is my best guess at how you might think about it.

Hope this pushes you in the right direction… or at least in a direction.

Keep up the good work – your video looks awesome.

Cheers,
M

Download the example from GitHub – LODinitialIdeas

TouchDesigner | Understanding Extensions

genGeoClassSo you’ve made a killer component that you love using, but you suddenly find yourself wondering how best to re-use it in future projects. You could make a killer control panel for it, or create a more generalized method for passing in values with in CHOPs or DATs. You could just resign yourself to some more complex scripting – reaching deep into your component to set parameters one at a time. You could hard code it, you’ll probably be making some job specific changes to your custom component anyway, so what’s a little more hard coding? The 50000 series now features custom parameters, or you could use variables, or storage. Any one of these options might be right for your component, or maybe they’re just not quite right. Maybe what you really need is a little better reach with Python, but without as much head scratching specificity. If find yourself feeling this way, than extensions are about to make your TouchDesigner programming life so, so much better.

Using extensions requires a bit of leg work on your part as a programmer, it also means that you’ll want to do this for components that you’ll find yourself reusing time and again – after all, if you’re going to take some time to really think about how you want a reusable piece of code to work in a larger system it only makes sense to do this with something you know will be useful again. That is to say, this approach isn’t right for every circumstance, but the circumstances it is right for will really make a difference for you. We’re going to look at a rather impractical example to give us a lay of the land when it comes to using extensions – that’s okay. In fact, as you’re learning how to apply this approach to your workflow it’s worth practicing several times to make sure you have a handle on the ins and outs of the process.

Before we get too much further, what exactly is this extension business? If you’re to the point with TouchDesigner where you’re comfortable using Python to manipulate your networks, you’ll no doubt have come to rely on a number of methods – anything with a . followed by a call. For example:

  • op(‘moviefilein1′).width – returns the width of the file
  • op(‘moviefilein1′).height– returns the heightof the file
  • op(‘table1′).numRows – returns the number of Rows
  • op(‘table1′).numCols – returns the number of Columns

In each of these examples, it’s the .operation that extends how you might think of working with an operator. Custom extensions, means that you, the programmer, are now free to create your own set of extensions for a given component. The classic example for this kind of custom component and custom extension set for TouchDesigner would be  a movie player. Once you build a movie player that cross fades between two videos, wouldn’t it be lovely to use something like op(‘videoPlayer’).PlayNext() or op(‘videoPlayer’).Loop(). The big idea here is that you should be free to do just that when working with a component, and custom extensions are a big part of that puzzle. This keeps us from reaching deep into the component to change parameters, and instead allows us to write modular code with layers of abstraction. If you’re still not convinced that this is a powerful feature, consider that when you start a car you’re not in the business of specifying how precisely the starter motor sequences each electrical signal to help the car turn over, or which spark plugs fire in which order – you issue a command car.start() with the expectation that the start() function holds all of the necessary information about how the vehicle actually starts. While this example might be reductive, it helps to illustrate the importance of abstraction. It’s impractical for you, the driver, to always be caught up in starting sequences in order to drive a car (one might make an argument against this, but for now let’s roll with the fact that many drivers don’t understand the magic behind a car starting when they turn the key), this embedded function allows the driver to focus on higher order operations – navigation, manipulation, etc. At the end of the day, that’s really what we’re after here – how do add a layer of abstraction that simplifies how we manipulate a given component.

That’s all well and good, but let’s get to the practical application of these concepts. In this case, before we actually start to see this in action, we need to have a working component to start working with. We are going to imagine that we want to build a generative component that’s got faceted torus that we use in lots of live sets. We want to be able to change a number of different elements for this Torus – its texture, background, rotation, deformation, to name a few. Let’s begin by putting together a simple render network to build this component, and then we can look at how extensions complement the work we’ve already done.

First let’s add an empty Base COMP to our network.

emptyBase

Inside of our new base let’s add a Camera, Geo, and Light COMP, as well as a Render TOP connected to an Out TOP. We’re building a simple rendering network, which should feel like old hat.

simpleRender

Let’s add a movie file in TOP, and a Composite TOP to our network. We’ll composite our render over our movie file in, so we have a background. In the image below only the changed parameters for the Composite TOP are shown.

simpleRenderWithComposit

Next let’s look inside of our geo COMP, and make a few changes. First let’s change our geo to be a polygon rather than a mesh. We’ll also turn off the display and render flags for the torus (don’t worry, we’ll turn them on further down the chain.

torus

Next we’ll add a noise SOP.

Noise SOP

Next we’ll add a facet SOP, turning on unique points and compute normals.

facetSOP

Finally, let’s add a null SOP. On the null, let’s turn on the display and render flags. When it’s all said and done we should have something that looks like this.

noiseTorusChain

Let’s move up one layer out of our geo, back into the base we started in. Here let’s add a phong Material and apply it to our geo. Let’s also add a movie file in TOP connected to a null TOP, and set it as the color map for our phong.

colorMapAndMaterial

While we’re still thinking about our material, lets make a few changes. Let’s set our diffuse color to white, our specular color to a light gray, and turn up our shininess to 255.

phongNonDefaults

Shown are the non default parameters for the Phong Material.

Let’s also make a few changes to our light COMP. I’m after a kind of shiny faceted torus, so let’s change our light to a cone light, place it overhead and to the right of our geometry, and set it to look at our geo.

Shown are the non default parameters for the Light Component.

Shown are the non default parameters for the Light Component.

I’ve gone ahead and changed file in my movie file in TOP to a different default image so I can see the whole torus. In the end you should have a network that looks something like this.

textureTorus

Thinking ahead, I know that I’m going to want to have the freedom of changing a few parameters for this texture. I’d like to be able to control if it’s monochrome, as well as a few of the levels of the image. Let’s add a monochrome TOP and a level TOP between the movie file in and the null TOP.

postProcess

We’re almost ready to start thinking about extensions, but first we need to build a control network to operate our component. Let’s start by adding a constant CHOP and calling it attrAssign. Here I’m going to start thinking about what I want to control in this component. I want to drive the rotation of the x y and z axis for our geo, I want to control the amplitude of the noise, the saturation of our image, the black level, brightness, and opacity. I’m going to think of those parameters as:

  • rx
  • ry
  • rz
  • noiseAmp
  • monoVal
  • blkLvl
  • bright
  • opacity

I’ll start out my constant CHOP with those channel names, and some starting values.

attrAssign

For this particular component, I want to be able to set values, and have it smartly figure out transitions rather than needing it constantly feed it a set of smoothly changing values. There are a couple of different ways we might set this up, but I’m going to take a rout of using a speed CHOP for one set of operations, and a filter CHOP to smooth everything out. First I want to isolate the rx ry and rz channels, we can do that with a select CHOP. Next we’ll connect that to a speed CHOP. We can merge this changed set of channels back into the stream with a replace CHOP – replacing our static rx ry rz channels with the dynamic ones.

selectSpeedReplace

Finally, we can smooth out our data with a Filter CHOP, and end our chain of operations in a null CHOP.

controlChops

Our last step here is to export or reference to each of our control parameters. Our rotation channels should be referenced by our Geo1 for rx, ry, and rz. The Noise SOP in Geo1 should be connected to the channel noiseAmp, and our image controls should be connected to their respective parameters – Monochrome, Black Level, Brightness, and Opacity. In the end, you should end up with a complete network that looks something like this.

complete BaseCOMP

Alright, we now finally have a basic generative component set up, and we’re ready to start thinking about how we want our extensions to work with this bad boy. Let’s start with the simplest ideas here, and work our way up to something more complex. For starters we need to add a text DAT to our network. I’m going to call mine genGeoClass.

genGeo

Let’s add our first class to our genGeoClass text DAT. Our class is going to contain all of our functions that we want to use with our component. There are a few things we need to keep in mind for this process. First, white space is going to be important – tabs matter, and this is a great place to really learn that the hard way. Namespace also matters. We’re eventually going to promote our extensions (more on that later on down), and in order for that to work correctly our functions need to begin with capital letters. That will make more sense as we see that in practice, but for now it’s important to have that tucked away in your mind.

Let’s begin by adding a simple print command. First we define our class, and place our functions inside of the class. When we’re writing a class in Python we need to explicitly place self in our definitions. There are a number of very good reasons for this, and I’d encourage you to read up on the topic if you’re curious:

Why ‘self’ is used explicitly
Why the explicit self has to stay

For our purposes, let’s get our class started with the following;

class GenGeo:

    def SimplePrint( self ):
 
        print( 'Hello World' )
        
        return

Before we can see this in action, we need to set up our base COMP to use extensions. Let’s back out of our base, and take a look at our component parameters.

baseExtensions

Here I’ve set the module reference to be the operator called genGeoClass inside of base1. We can also see that we’re specifcally referencing the GenGeo() class that we just wrote. I’ve also gone ahead and turned on promote extensions. Make sure you click “Re-Init” Extensions at the bottom of the parameters page, and then we can see our extension in action.

Next let’s add a text DAT to the same directory as our base1. Here we’ll use the following piece of code to call the SimpleText() function we just defined:

op( 'base1' ).SimpleText()

Let’s open our text port, and run our text DAT.

SimpleText

That should feel like a little slice of magic. If you choose not to promote your extensions, the syntax for calling a function looks something like this:

op( 'base1' ).ext.GenGeo.SimplePrint()

Okay, this has been a lot of work so far to only print out “Hello World.” How can we make this a little more interesting? I’m so glad you asked. Here’s  a set of functions that I’ve already written. We can copy and paste these into our genGeoClass text DAT, and now we suddenly have a whole new host of functions we can call that perform some meta operations for us.

class GenGeo:

    def SimplePrint( self ):
        print( 'Hello World' )
        return

    def TorusPar( self , rows , columns ):
        op('geo1/torus1').par.rows = rows
        op('geo1/torus1').par.cols = columns
        return

    def TorusParReset( self ):
        op('geo1/torus1').par.rows = 10
        op('geo1/torus1').par.cols = 20 
        return

    def Texture( self , file ): 
        op('moviefilein1').par.file = file
        return

    def TextureReset( self ):
        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'
        return

    def Rot( self , rx , ry , rz ):
        attr = op('attrAssign')
 
        attr.par.value0 = rx
        attr.par.value1 = ry
        attr.par.value2 = rz
        return

    def RotReset( self ):
        attr = op('attrAssign')
        speed = op('speed1')
        filterCHOP = op('filter1')
        attr.par.value0 = 0
        attr.par.value1 = 0
        attr.par.value2 = 0
        speed.par.resetpulse.pulse()
        filterCHOP.par.resetpulse.pulse()
        return

    def TorusNoise( self , noiseAmp ):
        op( 'attrAssign' ).par.value3 = noiseAmp
        return

    def Mono( self , monoVal ):
        op( 'attrAssign' ).par.value4 = monoVal
        return

    def Levels( self , blkLvl , bright , opacity ):
        attr = op('attrAssign')
        attr.par.value5 = blkLvl
        attr.par.value6 = bright
        attr.par.value7 = opacity
        return

    def PostProcessReset( self ):
        attr = op('attrAssign')
        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1
        return

    def Background( self , onOff ):
        op('comp1').bypass = onOff
        return

To better understand what all of these do let’s look at a quick cheat sheet that I made:

# Test Print Statement
op( 'base1' ).SimplePrint()

# Set Rows and Columns
op( 'base1' ).TorusPar( 20 , 20 )

# Reset Rwos and Columns to 10 x 20
op( 'base1' ).TorusParReset()

# Set the texture of a movie file in TOP
op( 'base1' ).Texture( 'https://farm4.staticflickr.com/3696/10353390565_1fa6dbf704_o.jpg' )

# Reset the Texture of movie file in TOP
op( 'base1' ).TextureReset()

# Set the Rotation Speed for the x y and / or z axis
op( 'base1' ).Rot( 10 , 15 , 20 )

# Reset the Rotation speed to 0, and the rotation values to 0
op( 'base1' ).RotReset()

# Set the Amplitude paramater of the Noise SOP for the Torus
op( 'base1' ).TorusNoise( 0.8 )

# Make the texture Monochrome
op( 'base1' ).Mono( 1.0 )

# Control the Black Leve, Brightness, and Opacity of the Texture
# that's applied to the Torus
op( 'base1' ).Levels( 0.25 , 1.5 , 0.8 )

# Reset all post process effects
op( 'base1' ).PostProcessReset()

# Turn off Background Image - 0 will turn the Background back on
op( 'base1' ).Background( 1 )

This is wonderful, but there’s one last thing for us to consider. Wouldn’t it be great if we had some initialization values in here, so at start-up or when we made a new instance of this comp we defaulted to a reliable base state? That would be lovely, and we can set that with an __init__ definition. Let’s add the following to our class:

    def __init__( self ):
 
        print( 'Gen Init' )
        attr = op('attrAssign')

        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'

        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1

        return

That means our whole class should now look like this:

class GenGeo:

    def __init__( self ):
        print( 'Gen Init' )
        attr = op('attrAssign')

        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'

        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1
        return

    def SimplePrint( self ):
        print( 'Hello World' )
        return

    def TorusPar( self , rows , columns ):
        op('geo1/torus1').par.rows = rows
        op('geo1/torus1').par.cols = columns
        return

    def TorusParReset( self ):
        op('geo1/torus1').par.rows = 10
        op('geo1/torus1').par.cols = 20 
        return

    def Texture( self , file ): 
        op('moviefilein1').par.file = file
        return

    def TextureReset( self ):
        op('moviefilein1').par.file = app.samplesFolder + '/Map/TestPattern.jpg'
        return

    def Rot( self , rx , ry , rz ):
        attr = op('attrAssign')
 
        attr.par.value0 = rx
        attr.par.value1 = ry
        attr.par.value2 = rz
        return

    def RotReset( self ):
        attr = op('attrAssign')
        speed = op('speed1')
        filterCHOP = op('filter1')
        attr.par.value0 = 0
        attr.par.value1 = 0
        attr.par.value2 = 0
        speed.par.resetpulse.pulse()
        filterCHOP.par.resetpulse.pulse()
        return

    def TorusNoise( self , noiseAmp ):
        op( 'attrAssign' ).par.value3 = noiseAmp
        return

    def Mono( self , monoVal ):
        op( 'attrAssign' ).par.value4 = monoVal
        return

    def Levels( self , blkLvl , bright , opacity ):
        attr = op('attrAssign')
        attr.par.value5 = blkLvl
        attr.par.value6 = bright
        attr.par.value7 = opacity
        return

    def PostProcessReset( self ):
        attr = op('attrAssign')
        attr.par.value4 = 0
        attr.par.value5 = 0
        attr.par.value6 = 1
        attr.par.value7 = 1
        return

    def Background( self , onOff ):
        op('comp1').bypass = onOff
        return

Alright, so why do we care? Well, this application of extensions frees us to think differently about this component. Let’s say that I want to make a few changes to this component’s behavior. First I want to set a new image to be the texture for the torus, next I want to change the rotation speed on the x and y axis, and finally I want to turn up the noise SOP. Previously, I might think about this by writing a series of scripts that looked something like:

op( 'base1/attrAssign' ).par.value0 = 20
op( 'base1/attrAssign' ).par.value1 = 30
op( 'base1/attrAssign' ).par.value3 = 0.8
op( 'base1/moviefilein1' ).par.file = 'https://farm4.staticflickr.com/3696/10353390565_1fa6dbf704_o.jpg'

Instead, I can now write that like this:

op( 'base1' ).Texture( 'https://farm4.staticflickr.com/3696/10353390565_1fa6dbf704_o.jpg' )
op( 'base1' ).Rot( 20 , 30 , 0 )
op( 'base1' ).TorusNoise( 0.8 )

That might not seem like a huge difference here in our example network, but as we build larger and more complex components, this suddenly becomes hugely powerful as a working approach.

extensionsInAction

Check out the example file on GitHub if you get stuck along the way, or want to see exactly how I made this work.

TouchDesigner | Modules as Reference Expressions

modHaving the flexibility to write expressions in reference fields for an operator is an incredibly powerful feature in TouchDesigner. This can free you from the necessity of doing computation outside of your reference, making for much tidier networks and programming. However, if you’ve ever tried to write a long expression, or a multi line logical test you might find yourself scratching your head about how to best make that work. Today we’ll look at how you can write a function as a module which you can then call in a parameter. First we’ll take a look at the basic pieces of this idea, and then we’ll explore what that might mean as a more complex exploration of this kind of idea.

One of the primary ingredients to this approach is to understand the basics of writing your own functions in Python. If this is new to you, start by look over these other examples:

Before we get in too deep, let’s first write a simple function in a text DAT, and see how that works in TouchDesigner. Let’s open up a new network, and add a Text DAT to our empty project. Now we’ll write a simple function. Write this following, or copy and paste, into your text DAT:

def simple():
 
    print( 'This is Hard coded text' )
    
    return

Great, but what is all of this? First we defined our function with the name simple and then we indicated that there aren’t any parameters that we want to change in the function by leaving our parentheses empty. Inside of our function we’ve written a print statement that is hard coded into our function – that is to say that we can only change this text by re-writing our function. If you try to run this text DAT as is, nothing should appear in your text port. What gives? Well, we’ve defined our function, but we haven’t actually asked TouchDesigner to run it – to do that, we need to call the function. We can do that by adding one more line of code to our text DAT:

def simple():
 
    print( 'This is Hard coded text' )
    
    return

simple()

Our last line calls the function we just defined. Now, if you run this DAT, you should see our text print in the text port.

mod2Congratulations, you’ve written your first Python Function here in TouchDesigner. This is all well and good, but lets push a little harder. Let’s change this to be a simple multiplication operation. Let’s make a simple function that converts feet to centimeters. If we do a little googling, we can quickly find that 1 foot is equal to 30.48 centimeters. Imagine we want to feed this function feet as a unit, and get centimeters back.

This time we can write a function that looks like this:

def simple( feet ):
 
    centimeters = feet * 30.48
 
    print( centimeters )
 
    return

This prints out centimeters, when we input feet:mod3

This is pretty good, but let’s look at two other ways to get information out of this function. Rather than just printing out a float (floating point number, a value with numbers after the decimal), what if we wanted this to print out a full statement like, “X feet is Y centimeters.” Let’s change our function so we can make that happen:

def simple( feet ):
 
    centimeters = feet * 30.48
 
    print( '%s feet is %s centimeters' %(feet, centimeters) )
 
    return

mod4

Alright, this is all well and good, but let’s consider that we just want to return a value, that we can then plug into something else. To do this, we’ll change our function, and then write a print command that integrates our new function. Here’s our new function:

def simple( feet ):
 
    centimeters = feet * 30.480000
 
    return centimeters

We can read this as completing the operation, and then returning the variable centimeters. We can now add the following line to our text DAT to actually print out our conversion string:

print( '%s feet is %s centimeters' %( 2, simple(2) ) )

mod5

Alright this is an awful lot of simple function writing, but not much integration work, what gives? Now that we have some fundamental pieces of function writing under  our belt, let’s look at what this means for using functions in parameter references.

Let’s start with a simple case where we hard code a value into our function. Let’s start by adding a text DAT to our network, lets also make sure we give it a unique name – I’m going to call mine simpleMod1. Here’s our simple function:

def simple():
 
    a = 100
 
    return a

We should have something like looks like this in our network:

simpleMod1

Great, now we just need to reference this in a parameter. Let’s add a constant CHOP into our network, and write the following reference:

me.mod.simpleMod1.simple()

Here we can see the syntax is:

  • me.mod – we’re asking for a module
  • simleMod1 – the name of the text DAT we’re referencing
  • simple() – the name of the function in the text DAT

Looking at this we should be excited to realize that we could write multiple functions in a single text DAT, and call them by name – we wont’ look at that here today, but feel free to experiment on your own. As long as everything’s working, you should now see that you’ve successfully retrieved your value from your function in your constant CHOP. You should have something that looks like this:

simpleMod2

Whew! Alright, we’ve now successfully written a function, that we’re retrieving in a constant CHOP. This, however, is highly impractical. You would probably never retrieve a constant value from a module like this – that said, it’s a helpful for us to see some of the fundamental elements that go into making something like this work. Now that we have some general concepts down, lets look at a few more complex examples.

We might imagine a circumstance where we want to perform a set of computations on values in a table, and then return them in a parameter. Let’s start by laying out all of the operators we need, First let’s add a Table DAT, a text DAT, and a constant CHOP. I’m going to name my text DAT simpleMod10. Let’s keep our initial function, but make one small change. Let’s change it to accept a parameter called val. That means our function should now look like this:

def simple(val):
 
    a = 10 * val
 
    return a

Now we’re ready to start cooking. In our table, let’s add a second row, and place two values in the two rows. You should have something like this:

table1

Now, we’ll write an expression in a constant CHOP that passes in a reference to these two cells to our function. In our constant CHOP, let’s write the following expression:

me.mod.simpleMod10.simple( op('table2')[0,0] )

Looking at what this might mean in English, we see that we’re calling the module simpleMod10 and the function called simple() we can also see that we’re passing it a value from table2 and the cell in row 0, column 0. Let’s do the same thing for the next row in our table. This means we should have something that looks like this:

multiTableRef

We’ve got simple referencing and value passing down, now let’s consider the circumstance where we want to use a slider in a control interface, but instead of a constant float we instead want to only get 4 values. From 0 – 0.25 we want the value 10, from 0.25 – 0.5 we want the value 15, from 0.5 – 0.75 we want the value 20, and from 0.75 – 1.0 we want 25. First let’s start by adding a few things to our network. First we’ll need a text DAT, a slider COMP, and a constant CHOP. Let’s write our function in our Text DAT. I’m going to name my Text DAT simpleMod8. We can use some simple if and else if statements return different values based on our ranges. With that in mind our function should look something like this:

def simple(val):
 
    if val < 0.25:
        a = 10
    elif val < 0.5:
        a = 15
    elif val < 0.75:
        a = 20
    elif val <= 1.0:
        a = 25
    
    return a

Next, in our constant we’ll use the following expression to make sure that we pass in the value from our slider:

me.mod.simpleMod8.simple( op( 'slider1/out1' )[ 'v1' ] )

There we go, we should now end up with values that we’ve specified based on a logical test in our function. You should have something that looks like this:

sliderOutputVals

Returning values is excellent, but we can also return strings using this same method. Let’s set up another similar example. In this one we’ll need a slider COMP, a Text DAT, an Eval DAT, and a Text TOP. Let’s start with our function from the last example, but change our returned values to strings.

def simple(val):

    if val < 0.25:
        a = 'apple'
 
    elif val < 0.5:
        a = 'kiwi'
 
    elif val < 0.75:
        a = 'orange'
 
    elif val <= 1.0:
        a = 'grape'

    return a

Now in both the text field of our Text TOP, and in the expression field of our Eval DAT let’s use the following expression:

me.mod.simpleMod9.simple( op('slider2/out1')['v1'] )

We should have something that looks like this, and as the slider moves returns strings:

stringReturn

stringReturn

Alright, last but not least, let’s look at how we might use this same idea to test strings and return values. This time around we’ll want a Text DAT, a field COMP, and a constant CHOP. I’m going to name my Text DAT simpleMod7. Using the same ideas we’ve explored already, let’s write a function that tests our field COMP’s output and returns an integer.

def simple( val ):

    if val == 'kiwi':
        a = 15
 
    elif val == 'apple':
        a = 10
 
    else:
        a = 0

    return a

This should feel like a pretty routine drill now, let’s set our constant to use the module and pass in our field value with the following expression:

me.mod.simpleMod7.simple(val = op( 'field1/out1' )[ 0,0 ])

With everything up and working, you should see something like this:

stringReturnsInt

The way our function works, if we type ‘apple’ or ‘kiwi’ into our field, we’ll see 10 or 15 returned. In all other cases, we’ll see 0.

stringReturnsInt

If you want to see a set of examples head over to GitHub and look through the source examples.

TD-Examples on GitHub

TouchDesigner | Getting More out of the CHOP Execute DAT

One of the most challenging elements for me to wrap my heard around when using TouchDesigner was the Execute DATs. When scripting was still new to me, it was hard to understand where to start, or what to write. It was also hard to understand what kind of options I had when dealing with the execute family of DATs. Let’s take a closer look at some of the interesting elements that you can extract from a CHOP Execute DAT, and hopefully learn a few things along the way.
LFO Trail

CHOP Execute DATs allow us to run a script at particular events. The functions that you see in the CHOP Execute specify what kind of event, and what kind of information gets passed into those events that you might take advantage of. Looking at the CHOP Execute DAT we can see the following kinds of events that we might want to take advantage of:

  • Off to On
  • While On
  • On to Off
  • While Off
  • Value Change

Screenshot_061015_113742_PM

That’s all well and good, but what do those things actually mean? Let’s create a simple script to see what we end up with. We’ll work mostly with the Off to On Definition – with what we learn here you can start to explore what the other definitions do (when I say definitions, I’m mean the portions of the Execute DAT that begin with def).

Before we can get started we need a CHOP in our network to associate with our CHOP Execute DAT. Let’s add an LFO CHOP, and a CHOP Execute DAT to our network.

Screenshot_061015_114358_PM

Next we need to associate our CHOP with our DAT. We can do this by dragging the LFO CHOP onto the CHOP Execute DAT.

associate CHOP to DAT

Next we need to make sure that in the parameters of our Execute DAT that the flag which corresponds to our definition is turned on.

Screenshot_061015_114629_PMWith my default values, I can see that something isn’t quite right. Here my “Value Change” Flag is turned on, I want to turn off that flag and turn on the Off to On Flag. The parameters for your CHOP Execute should now look like this:

Screenshot_061015_114822_PM

Alright, now that we’re just about set up let’s see what happens when we write some scripts for our Execute DAT. The simplest place for us to start might be to just print a string. Let’s add this line to inside of our definition:

print('Hello World')

Because this is inside of a defined function, we’ll also need to make sure that it’s indented. The whole function should look like this in your DAT:

def offToOn(channel, sampleIndex, val, prev):
    
    print('Hello World')
    
    return

In TouchDesigner that looks like this:

Screenshot_061015_115603_PM

That’s all well and good, but what does that do? Well, if we open up our text port we should see that ever time we cross the 0 threshold in our LFO that we print “Hello World” to the text port.

Hello World

That in itself isn’t very excited, but we might imagine a situation where we want to run a script at a given regular interval, and this is exactly the kind of technique we might use to make that happen. You might also notice that there are several other parameters for this function that we might be interested in.

Before we get in too deep, lets remember to read what’s already in our DAT. We can see that the header comment reads:

# me is this DAT.
# 
# channel is the Channel object which has changed.
# sampleIndex is the index of the changed sample
# val is the numeric value of the changed sample.
# prev is the previous sample value.
# 
# Make sure the corresponding toggle is enabled in the CHOP Execute DAT.

In general I like to think about any scripting with the following kind of mantra – “When in Doubt, Print it Out.” With that in mind, let’s just print out these parameters to see what they are. To do that, we’ll change our function to read like the following:

def offToOn(channel, sampleIndex, val, prev):
 
    print('channel is ' , channel)
    print('sampleIndex is ', sampleIndex)
    print('val is ', val)
    print('prev is ', prev)
 
 return

The results in our text port should look something like this:

Screenshot_061115_121305_AM

This should give us a sense of the kinds of information that we can pull out of our Panel Execute DAT. Before we stop, let’s push a little harder on one of these ideas. First we need to make some changes to our LFO. Let’s first use some pattern matching to add some more channels. We can do this on the Channel Page of our CHOP by specifying that our Channel Names should be chan[1-5]:

Screenshot_061115_122708_AMNext, lets give these unique all a unique phase with the following python expression:

me.chanIndex/me.numChans

Screenshot_061115_122846_AM

Alright, now we should have an LFO that looks something like this:

LFOCHOP

Let’s change up our Execute DAT to see what kinds of interesting information we might be able to extract from this. First let’s just see if we can retrieve the channel index as our wave passes through zero. We can do this by changing our function to read:

def offToOn(channel, sampleIndex, val, prev):
 
    print(channel.index)
 
    return

In TouchDesigner that might look like this:

Screenshot_061115_123416_AMThat’s pretty interesting, but what if we only want to print out a line of text when a certain set of conditions are met? We might imagine a scenery where we want to first match a logical test, then run a script.

Let’s try writing something like this:

def offToOn(channel, sampleIndex, val, prev):
 
    if channel.index == 0:
        print('This will only appear when index 0 fires')
 
    if channel.index == 1:
        print('This will only appear when index 1 fires')
 
    if channel.index == 2:
        print('This will only appear when index 2 fires')
 
    if channel.index == 3:
        print('This will only appear when index 3 fires')
 
    if channel.index == 4:
        print('This will only appear when index 4 fires')
 
 return

We’ve now set up a series of tests where we’ll only see a particular line of text when the if conditions are met. As a result we should see this in the text port:

textPortText

This may initially seem unimpressive, but when you begin to imagine how you might combine the power of CHOPs with the extensible nature of scripting you soon end up with a world of options that wasn’t present before.

Looking for the Example Files? You can find them here on GitHub.

TouchDesigner | Make a Copy of that Table

When working with DATs there are often a whole host of operations that we want to be able to take advantage of that don’t make sense when we initially look at the documentation on the TouchDesigner Wiki. One simple example of this is how copy operations work. A clear example of this might be how copy operations work. For example, I have a table full of information but I’d like to make a copy of it for another part of my network. If we look at the Table DAT Class wiki page we see this:

copy(DAT)

Copy the text or table from the specified DAT operator.

  • OP – The DAT operator whose contents should be copied into the DAT.

How can we make sense of this?

Let’s start by first setting up a simple table – you can fill it with any information that you’d like. We also want an empty Table DAT, as well as a Text DAT.

Screenshot_060615_065054_PM

In our Text DAT we’re going to write the following script:

source = op('table1')
target = op('table2')

target.copy(source)

Looking at the syntax of the operation we can see what we first defined two variables – target and source, named as such to make it easier to see what’s happening. Next we start with the target and then specify what operator we’d like to copy. Now we can right click on the text DAT, run our script, and voila. A little copy magic.

anatomy of a copy script

Need to see it action? Look through the example files on Git Hub.

TouchDesigner | Drop Scripts

We all love some drag and drop action – we use it all the time, and in fact have often come to expect it to be a feature in nearly every application we interact with. With that in mind, how can we think about integrating some drop actions into our work in TouchDesigner? Well, today is your lucky day! Here we’re going to look at some of the fundamental ideas that will help you get started with much more complicated drag and drop functions.

Anatomy of a Drop Script

Let’s start by setting up a container to perform some actions based on when we drop a file onto it – this will help us get our bearings, and see how dropping works. First we need to set up our container to work with a drop script. First add a new container to a network, next let’s go to the Drag page of the container, and look at the sub page labeled “On Dropping into this Component.”

Screenshot_053015_095734_PM

We’ll start by first changing the drop down menu here to be “Allow Drop.” Next we’ll need to specify what script to run when something is dropped onto this Component. Let’s plan to call our script dropScript, and we’ll place it inside of our container. This means that we can specify it’s relative path and name as

./dropScript

Now we should have something that looks like this:

Screenshot_053015_100204_PM

Now we’re ready to start working. First let’s navigate inside of our container, then split our work-space left and right, and open up a text port on one side of our work-space. Inside of container1 let’s add a new Text DAT and call it dropScript – just like we had planed earlier.

Screenshot_053015_100509_PM

To get started, let’s first write a simple script so we can see this whole thing in action. In our Text DAT let’s use the simple script:

print('Hello World')

Screenshot_053015_100738_PM

To this in action, let’s open the viewer for our container and drop a file onto it – we’ll want to keep an eye on our text port as well, because this is where our print command will show up.

drop

Perfect. Here we can see that when we drop something onto our container, low and behold our script runs. This is great, but wouldn’t it be slick if we could instead get the pathway to our file? In fact, we can get this! Let’s change out drop script to this:

print( args )

Now when we drop something on to our container we get a list that should look something like this:

('E:/Dropbox/Camera Uploads/2015-05-26 10.55.18.jpg', '0', '0', '0', '1', 'jpg', '2015-05-26 10.55.18', '/container1')

The first item in this list is what we’re looking for – it’s the pathway to where our dropped file lives on our computer. Let’s change our drop script so we only get that first item in our list.

print( args[0] )

Now when we drop a file onto our container we only get the pathway to the file.

getAFile

That’s all well and good, but what can we do with that?

Changing a Movie File In TOP

Let’s set up a few other items in our container so we can really see this in action. Let’s add a Movie File In TOP, wire that to a Fit TOP, and finally to a Null TOP.

Screenshot_053015_102336_PM

Set the Fit TOP resolution to match that parent container with following expressions:

parent().par.w
parent().par.h

Screenshot_053015_102639_PM

Finally, let’s name the null bg, and set the parent to display this as the background TOP.

Screenshot_053015_102751_PM

Now we’re ready to return to our drop script. Let’s change our drop script to set the parameter “file” on the Movie File In TOP to be the path from args. In python that looks something like this:

op('moviefilein1').par.file = args[0]

If you’re following along at home, you might want to consider adding some comments and breaking this up slightly. It may make your code a little more verbose, but when you come back to it later on it will be easier to decode what’s actually going on here. That might end up reading like this:

# identify the movie file in 
# operator that I want to change
movTarget = op('moviefilein1')

# make a new variable called newFile
# assign the args[0] string to this variable
newFile = args[0]

# set the file paramater for the target op
# to be newFile - this means that when I drop
# an image onto this container, that file
# will become the background
movTarget.par.file = newFile

Commenting isn’t always the most fun part of programming, but when you come back to a project or script it can make it much easier to remember what you were thinking and how you were working.

Now, when we drop a file onto our container, we should see the background file change as well.

DropAction

Changing a Directory

That’s great, but how can we push this a little farther? Well, we might imagine that we find ourselves in a situation where we’d like to change the target directory for a Folder DAT. While the Choose a Directory example works well, it you might not want the hassle of navigating to your folder – instead you might just want to drop it onto your container. We can use the same principles from above to do just that.

Let’s add a Folder DAT to our network. Now we’ll change our dropScript to be:

folderDAT = op('folder1')
folderDAT.par.rootfolder = args[0]

Now we can drag an entire folder onto our container and see the Folder DAT update.

folderDop

Bada bing! With a few basics under your belt, now you can have fun and and start to play with doing something exciting with drop scripts.

Want to take a look at some examples? You can find them here on GitHub