djFileSwitchRamp.mel & djPopulateSingleSwitch.mel

david | mel script,rendering | Sunday, June 7th, 2009

I wrote these two scripts for a job I did recently where I needed an efficient way of dynamically assigning many different fileTextures to large numbers of objects.

By "dynamically assigning" I mean that the texture assignment needed to be animateable. Initially I thought of assembling my images into a numbered file sequence and manually keyframing the image number in the fileTexture attributes so each object would get the desired image at the desired time. But each object would need to be independent in terms of  image timing and order and I could see the task of managing these image sequences would be difficult. To complicate matters further, some images were to be static while others were to be 25 fps sequences.

The picture above shows a simple test render where 100 single poly objects all share the same material, but each is assigned one of seven possible fileTextures.

An obvious way to do this is using a tripleShadingSwitch node, where the objects are added to the "In Shapes" list and the fileTextures to the "In Triple" list. Try doing it with 100 objects and you'll see how tedious it is to set up. And even more tedious to change. Animating the image assigned to each object is possible, but requires one fileTexture for each object - so in this example although I only have seven images, I would still need 100 fileTextures to be able to animate each objects image independantly of the others.

My solution was to create an extra keyable attribute on each object which I called "id".

To be able to choose a fileTexture I used a singleShadingSwitch to link each object and its id to the shader network.

At render time, the output of this switch is the id value of the object being rendered.

So now I just needed a way to choose a fileTexture based on this id value and a bit of lateral thinking lead me to the ramp texture. A v-ramp takes a v-coordinate value and chooses a color from the ramp. All I had to do was hook up each fileTexture to a different ramp.colorEntryList postion and connect the output of my singleShadingSwitch to the ramp.vCoord input.

The method is simple, but hooking everything up manually is quite a task. The ramp texture UI is fiddly when you have many fileTextures to hook up, offers little or no visual feedback and requires a calculator to figure out the actual positions. Obviously this is a job for mel scripting.

djPopulateSingleSwitch.mel

Select the objects and run the script. The objects will get a new id attribute and new singleShadingSwitch will be created with all the connections. This is a simple script, so if you need to add more objects later, you will have to delete the existing singleShadingSwitch, select all the objects again, including the new ones and rerun the script. Existing id attributes and their values will be preserved.

djFileSwitchRamp.mel

First you need to create some fileTextures. Select them in the order you want them to be numbered and run the script. A ramp texture will be created with the fileTextures connected and evenly spaced. In addition a multiplyDivide node will be created. The purpose of the multiplyDivide is to make the input numbers more user-friendly.

The idea is that you will connect the output of the singleShadingSwitch to the input of the multiplyDivide. This connection must be done manually. (And of course you would need to connect the ramp to something - mia_material_x.diffuse for example.)

Now if you want to assign fileTexture 3 to a particular object you would set that object's id value to 3.

Here is a hypershade snapshot of part of the shading network used in my example

It ends up being a complex network due to the number of objects involved, but it is also efficient. By keying the id values I can easily switch the fileTexture assigned to each object.

This is another simple script and so if you need to add fileTextures, you'll need to delete the existing ramp and start again. However if you select the fileTextures in the same order as the first time, they will get the same index number, so existing object id's will still match up.

The script creates the ramp with no interpolation, but by setting it to one of the other interpolation modes and by animating the id value using non-stepped keys you can create some interesting cross-fade transition effects.

You can download both of these scripts at my Downloads Page

Update 8 September 2009: In response to a request by agenesis I have added the ability to handle instances. Its a separate script called djPopulateSingleSwitch_instancedObjects.mel. Please read the script header for some explanation of how it works.

10 Comments »

  1. Hey David,

    This post saved me a bunch of work on a recent project...

    Thanks a lot!

    Oryan

    Comment by medooch — August 3, 2009 @ 10:35 pm

  2. Nice work david,

    We've used this method to assign random leaf textures to a forest we've made. Hundreds of leaves - one shader. Awesome.

    Comment by rusty — August 7, 2009 @ 5:18 am

  3. Hi David,

    Awesome script! I'm new to MEL, do you know how to randomly assign a selection of objects different ID nodes, so that the assignment of all the file textures through the switch node and onto the geo is chosen randomly?

    Comment by agenesis — August 12, 2009 @ 9:35 am

  4. is there a way to get this to work with instances?

    Comment by agenesis — August 13, 2009 @ 7:28 am

  5. To assign random id numbers to s selection of objects, you could select your objects and execute the following mel in the script editor, after changing the rand() statement to an appropriate range (in this example it will set a random integer between 0 and 100):

    {
    string $sel[] = `ls -sl`;
    for ($s in $sel) {
    int $rnd = rand(0,100);
    setAttr ($s +".id") $rnd;
    }
    }

    It gets a bit more complicated if you need to deal out the numbers randomly, but like a deck of cards, where each number will only be used once. Let me know if that's what you needed.

    As for instances... I'm not sure. If I get some time I'll give it a try and let you know.

    Comment by david — August 14, 2009 @ 1:26 am

  6. agenesis: It took me a while to be able to look into your question. In the populateSingleSwitch script objects are assumed to be transformNodes above a single shapeNode, and this assumption breaks when instances are involved. The technique can certainly work with instances, but the script would need some modifications. I will try to find time to do this in the next week or so.

    Comment by david — August 16, 2009 @ 11:42 pm

  7. agenesis: I finally got some time to look into the instance question. I have added an extra script to the rar download that seems to work. I have not had time to test it fully. Eventually I may combine the two scripts into one and add proper error checking, but this will have to do for now.

    Comment by david — September 8, 2009 @ 1:01 am

  8. Hi David,

    Sorry I did not get back to you with my thanks earlier ... I just checked back in now.

    Your original script came in handy big time over the summer, thanks a million for that, and also for looking into the instancing scenario. I'm grabbing a copy of it now and will test it out as soon as I can.

    As usual your blog is awesome. I look forward to seeing more soon.

    Best Regards,

    Matt

    Comment by agenesis — December 4, 2009 @ 4:18 pm

  9. Hi David,

    Sorry I did not get back to you with my thanks earlier ... I just checked back in now.

    Your original script came in handy big time over the summer, thanks a million for that, and also for looking into the instancing scenario. I'm grabbing a copy of it now and will test it out as soon as I can.

    As usual your blog is awesome. I look forward to seeing more soon.

    Best Regards,

    Matt

    Comment by agenesis — December 4, 2009 @ 4:18 pm

  10. Thanks for your brilliant idea!

    I am trying to get the instanced version working, though I do think I am missing a step somewhere... (warning: newbie)
    Any idea where I am going wrong?

    'djPopulateSingleSwitch_instancedObjects.mel' header says 'if you instanced pCube1 to get pCube2, pCube3 and so on, then select pCube1, pCube2, pCube3 and so on[...]'
    Well, my cube is instanced meaning there is only _one_ cube, pCube1, in my scene file. So where are those others supposed to be?

    The script did create an 'id' attribute, but then how (via MEL) do I modify this one attribute to individual values on the instanced cubes? Is this even possible? Or wouldn't all share the same 'id' value?
    I tried to play with "setAttr pCube1.instObjGroups[0].objectGroups[0].id" but not getting far here.

    Any help appreciated. Thanks!

    Comment by earthwavesurfer — April 4, 2011 @ 1:06 pm

RSS feed for comments on this post.

Leave a comment

You must be logged in to post a comment.

Powered by WordPress | Based on a theme by Roy Tanck