21 texture switcher frame based material node

THis script is for a material node that lets you switch among any of up to 21 textures during the render and use it in a node material. The assignment of which texture to use within that frame is based on matching the current frame number of the render with data entered in an array within the script.

THis is not a totaly new script, rather it expands on the script by Holly "10Way Nodal Framebased Col/Value switcher " http://blenderartists.org/forum/showthread.php?t=155800 increasing the number of texture choices from 10 to 21 textures.

Here is a short demo video showing the scripted node in action:
http://www.youtube.com/watch?v=y0CUrMoBhZA

and here is the Blend file this vid was rendered from:
http://www.filefront.com/14678657/21WayTexSwitchNode.blend/

and here is the python script:
http://www.filefront.com/14678661/21WayTexSwitchNode.py/

and this shows a node setup using this material node:

http://img19.imageshack.us/img19/1595/21waytexframswitchnodes.png

There is a glitch in this script, and within the 10 way switch script it comes from, that I have not been able to fix yet: as you can see in the above video, the textures switch properly (at the frame in the input array) for the first texture change, but from then on, the textures change on the next frame after the value in the array data. Eg. the texture for ‘9’ is supposed to end at frame 150 and ‘10’ start at frame 151. Instead, ‘9’ ends at 150, ‘0’ aka base, shows at frame 151 and ‘10’ starts at frame 152.

To use this scripted node, open a text window and “open” the python script within that window. Edit the array within the script for the start and stop frames you want for each texture within that render. Start a material node system, add a ‘dynamic’ node and select the 21WayTexSwitchNode.py node then hit update. Next make 2 dummy materials and load your textures. THen make each texture a fake “F” user and add them to the material node window. Now hook them up (the above example applies them as a UV texture). Note that each time you ‘update’ the node by adding new frame data, all the connections to the dynamic node are severed and you will have to hook it all up again.

it looks like my post is too long to include the code here so I will put it in the next post:

Last, here is the script as a text file. I had terrible luck editing this externally and then
importing it into Blender mainly due to many many errors in indentation seemingly
generated during the conversion and import. Luckily, the Blender text editor seems to
work well and if you turn on the options makes editing within Blender pretty easy (other
than the fact that you can’t increase the font size to something my old eyes can read!)

#"""
#Name: '21Way Nodal Frame Switch'
#Blender: '248-249'
#Group: 'Animation'
#Tooltip: 'Edit the values in the script below in the section User Definable Variables to work'
#"""
#__author__ = "Lyle Walsh" extended the size of the array in the script made by Holly Tsukiko Grimes
#for the 10Way Nodal Frame Switch script
#__version__ = "2.09"
#__bpydoc__ = "http://blenderartists.org/forum/showthread.php?t=168665"
#Description:
#THis is a scripted material node that allows you to switch among 21 different textures depending on 
#the current frame number of the render.  Save the textures to dummy materials as fake before using.
#You must hit the update button in the node if you enter new frame information but when you do that
#all the connections to the node are severed and you have connect the node back up again.
#demo video: http://www.youtube.com/watch?v=y0CUrMoBhZA
# Why 21 textures?  Well 10 weren't enough and I can't count to 20 properly. This version is simply
#an extension of the 10Way nodal script written by Holly Tsukiko Grimes.  I didn't write it, she did. 
#All I did was enlarge it for more textures.
#
#THere is a Glitch in the timing of texture switching that I can't figure out how to fix.  
#The first time you switch textures by frame number, the switch occures at the correct frame, here
#at frame 20.   However, from then on the switch occures on the frame AFTER the one you have specified;
#in this example at frame 36 instead of frame 35 as specified for texture '2' in FrameInfoArray.  
#You can see this problem as a single frame of the base (the '0') inserted between the textures
#for the numbers 10-20 in the render - frames 150-260.
#
#THe following is the original description written by Holly:
##A nodal version of my script from http://blenderartists.org/forum/showthread.php?t=141472
##This is a simple node that allows one to select a colour/texture/value
##to use in a node map depending on the current frame of the animation i.e
##A 10-way switch based on the current frame.
##
##This version is simplier, easier to use and customise and can be used
##for any colour or float-value in a node map compared to the original
##script.
##
##You simply edit the array below to have a range of frames you want that
##'channel' of the node to be active. If the current frame isn't in the array
##the node automatically forwards the 'Base' Channel so something is always
##forwarded. The array works in pairs, so you have to set a start frame and an
##end frame (inclusive).
##
##If you have more than 1 channel indicated for a frame, the switcher only chooses
##the first instance of it eg if channels 3 and 5 are in the array as active for
##frame 32, channel 3 is used.
##
##Drawbacks: cannot be used in glsl textures or previewing on the fly due to the amount of
##computations needed per pixel. I'm hoping to speed this up in the future.
##
##There is a glitch in which the texture detail forwarded is corrupt due to poor multithread support
##from python, make sure you only use 1 thread if you wish to use this node.
##
##Revisions 0.9->0.95: corrected a glitch in which wrong channel is selected due to the indices being
##off in the searcher
import Blender
from Blender import *
from Blender import Node
######################################################
#User Definable Variables
#Edit these values for your own personalised node
######################################################
#Example array information
#(note: do not change name of array)
#Base= Base texture Colour (not shown but is active on frames 1-20, 31-34, etc
#1= texture 1 active on frames 20-30 
#2= texture 2 active on frames 35-45
#3= texture 3 active on frames [50,60],
#4= texture 4 active on frames [65,75],
#5= texture 5 active on frames [80,90]
#6= texture 6 active on frames [65,75],
#7= texture 7 active on frames [95,105],
#8= texture 8 active on frames [110,120],
#9= texture 9 active on frames [140,150],
#10= texture 10 active on frames [151,160],
#11= texture 11 active on frames [161,170],
#12= texture 12 active on frames [171,180],
#13= texture 13 active on frames [181,190],
#14= texture 14 active on frames [191,200],
#15= texture 15 active on frames [201,210],
#16= texture 16 active on frames [211,220],
#17= texture 17 active on frames [221,230],
#18= texture 18 active on frames [231,240],
#19= texture 19 active on frames [241,250],
#20= texture 20 active on frames [251,260]]
FrameInfoArray=[
[20,30],
[35,45],
[50,60],
[65,75],
[80,90],
[95,105],
[110,120],
[125,135],
[140,150],
[151,160],
[161,170],
[171,180],
[181,190],
[191,200],
[201,210],
[211,220],
[221,230],
[231,240],
[241,250],
[251,260]]
 
 
######################################################
#Main Node class
#
#Doesn't need to be changed. Its automatically set up
#for 21 fields, inc the base field, more can be achieved
#but one would need to extend the arrays
######################################################
class FTextureChanger(Node.Scripted):
    def __init__(self, sockets):
        colB = Node.Socket('TexBase', val = 4*[1.0])
 col1 = Node.Socket('Tex1', val = 4*[1.0])
 col2 = Node.Socket('Tex2', val = 4*[1.0])
 col3 = Node.Socket('Tex3', val = 4*[1.0])
 col4 = Node.Socket('Tex4', val = 4*[1.0])
 col5 = Node.Socket('Tex5', val = 4*[1.0])
 col6 = Node.Socket('Tex6', val = 4*[1.0])
 col7 = Node.Socket('Tex7', val = 4*[1.0])
 col8 = Node.Socket('Tex8', val = 4*[1.0])
 col9 = Node.Socket('Tex9', val = 4*[1.0]) 
 col10 = Node.Socket('Tex10', val = 4*[1.0])
 col11 = Node.Socket('Tex11', val = 4*[1.0])
 col12 = Node.Socket('Tex12', val = 4*[1.0])
 col13 = Node.Socket('Tex13', val = 4*[1.0])
 col14 = Node.Socket('Tex14', val = 4*[1.0])
 col15 = Node.Socket('Tex15', val = 4*[1.0])
 col16 = Node.Socket('Tex16', val = 4*[1.0])
 col17 = Node.Socket('Tex17', val = 4*[1.0])
 col18 = Node.Socket('Tex18', val = 4*[1.0])
 col19 = Node.Socket('Tex19', val = 4*[1.0])
 col20 = Node.Socket('Tex20', val = 4*[1.0])
 colO = Node.Socket('Colour', val = 4*[1.0])
 ValB= Node.Socket('ValueBase', val = 1.0, min = 0.0, max = 1.0)
 Val1= Node.Socket('Value1', val = 1.0, min = 0.0, max = 1.0)
 Val2= Node.Socket('Value2', val = 1.0, min = 0.0, max = 1.0)
 Val3= Node.Socket('Value3', val = 1.0, min = 0.0, max = 1.0)
 Val4= Node.Socket('Value4', val = 1.0, min = 0.0, max = 1.0)
 Val5= Node.Socket('Value5', val = 1.0, min = 0.0, max = 1.0)
 Val6= Node.Socket('Value6', val = 1.0, min = 0.0, max = 1.0)
 Val7= Node.Socket('Value7', val = 1.0, min = 0.0, max = 1.0)
 Val8= Node.Socket('Value8', val = 1.0, min = 0.0, max = 1.0)
 Val9= Node.Socket('Value9', val = 1.0, min = 0.0, max = 1.0)
 Val10= Node.Socket('Value10', val = 1.0, min = 0.0, max = 1.0)
 Val11= Node.Socket('Value11', val = 1.0, min = 0.0, max = 1.0)
 Val12= Node.Socket('Value12', val = 1.0, min = 0.0, max = 1.0)
 Val13= Node.Socket('Value13', val = 1.0, min = 0.0, max = 1.0)
 Val14= Node.Socket('Value14', val = 1.0, min = 0.0, max = 1.0)
 Val15= Node.Socket('Value15', val = 1.0, min = 0.0, max = 1.0)
 Val16= Node.Socket('Value16', val = 1.0, min = 0.0, max = 1.0)
 Val17= Node.Socket('Value17', val = 1.0, min = 0.0, max = 1.0)
 Val18= Node.Socket('Value18', val = 1.0, min = 0.0, max = 1.0)
 Val19= Node.Socket('Value19', val = 1.0, min = 0.0, max = 1.0)
 Val20= Node.Socket('Value20', val = 1.0, min = 0.0, max = 1.0)
 ValO= Node.Socket('ValueOut', val = 1.0, min = 0.0, max = 1.0)
        sockets.input = [ValB,colB,Val1,col1,Val2,col2,Val3,col3,Val4,col4,Val5,col5,Val6,col6,Val7,col7,Val8,col8,Val9,col9,Val10,col10,Val11,col11,Val12,col12,Val13,col13,Val14,col14,Val15,col15,Val16,col16,Val17,col17,Val18,col18,Val19,col19,Val20,col20]
        sockets.output = [ValO, colO]
    def __call__(self):
 def checkframes(frameno, FrameArray):
  Texno=-1
  found=0
  for Lnum in range(0,len(FrameArray)):
   for Fnum in range(0,len(FrameArray[Lnum])/2):
    if found==0:
     if int(frameno) >= FrameArray[Lnum][(Fnum)*2] and int(frameno) < FrameArray[Lnum][((Fnum)*2)+1]:
      Texno=Lnum
      found=1
  return Texno
 
 scn= Blender.Scene.GetCurrent()
 currframe=str(Blender.Get('curframe'))
 ColourinputArray=[self.input.Tex1, self.input.Tex2, self.input.Tex3, self.input.Tex4,self.input.Tex5,self.input.Tex6,self.input.Tex7,self.input.Tex8,self.input.Tex9,self.input.Tex10,self.input.Tex11,self.input.Tex12, self.input.Tex13, self.input.Tex14,self.input.Tex15,self.input.Tex16,self.input.Tex17,self.input.Tex18,self.input.Tex19,self.input.Tex20]
 ValueinputArray=[self.input.Value1, self.input.Value2, self.input.Value3, self.input.Value4,self.input.Value5,self.input.Value6,self.input.Value7,self.input.Value8,self.input.Value9,self.input.Value10,self.input.Value11, self.input.Value12, self.input.Value13, self.input.Value14,self.input.Value15,self.input.Value16,self.input.Value17,self.input.Value18,self.input.Value19,self.input.Value20]
 Texnumber= checkframes(currframe, FrameInfoArray)
        if Texnumber!=-1:
         self.output.Colour = ColourinputArray[Texnumber]
  self.output.ValueOut = ValueinputArray[Texnumber]
 else:
  self.output.Colour = self.input.TexBase
  self.output.ValueOut = self.input.ValueBase
__node__ = FTextureChanger

hehe im glad to see my script being extended^^ its pretty easy to extend it past 10frames, or even 21 if the user has knowledge of python and knows where to change the variables.

as for the glitch you mentioned about the extra base texture being shown inbetween changes. to solve it, you just need to alter the starting times of the textures.

for instance, in your current list of:


FrameInfoArray=[
[20,30],
[35,45],
[50,60],
[65,75],
[80,90],
[95,105],
[110,120],
[125,135],
[140,150],
[151,160],
[161,170],
[171,180],
[181,190],
[191,200],
[201,210],
[211,220],
[221,230],
[231,240],
[241,250],
[251,260]]

to fix it so the textures start on, say, 251/241/etc, you need to change the starting times to:



FrameInfoArray=[

[20,30],

[35,45],
[50,60],
[65,75],
[80,90],
[95,105],
[110,120],
[125,135],
[140,150],
[150,160],
[160,170],
[170,180],
[180,190],
[190,200],
[200,210],
[210,220],
[220,230],
[230,240],
[240,250],
[250,260]]

this way at frame 250, the script finds that its at the end of textureinput 20 and chooses it over texture21, and the next frame texture21 will start.

hope that helps! ill look into a way to get it to work normally without having to change the start frames.

i also have a new update for the 10way frame changer soon to make it faster. its a lil more complex but changes to the material can be seen in the matter of seconds instead of >30seconds.

Thanks for keeping at it!

Any chance you could figure out how to do this with an animated material or texture? Eg. you could have a sequence of mouth expressions where say frames 51-80 are a smile and you could then play the texture animation frames 51-80 then 80-51 to make you anime smile?

I’ll give it a shot, but i don’t expect it to be too hard. i mean, an animated texture is contained within a ‘texture’ object, and these scripts just decide which texture object to be shown at what frame, whether its static or animated. controlling the animated texture to such an extent might be harder, but i’ll look into it^^

you could always do what im doing at the minute for speaking with mouth expressions (see my vids), which is quite hands on. i guess using animated textures could make things easier if the same mouth expressions happen often (like in games with no vocals, it would still show the mouth moving to give an idea that the speechbox’s words were actually said).

give me a day or 2 to look at the animated texture bit, ill try build a test file to play with.

just did a quick check, it is possible codewise to use animated textures for the texture channels, but its v. difficult to use python to make blender reverse frame an animation (your 80-51 example) as python can only control what functionality blender already has, and it doesnt have a reverse function. So one would have to code in a reverse python function themselves that would reverse the playback (by manually adjusting the offset frame and framelength of the animated texture every framechange/update).

This seems pretty complex but doable, it would be much easier to create the reversed gif/avi outside blender and just use it so you’d have 2 textures of animations “51-80.avi” and “80-51.avi” you know?

ill still give the switcher node a go using animated textures in the meantime, ^^ ill start on that tomorrow.

Sweet!
For reversing the animated texture, there should be something used to flip vids in the sequence editor
I still can’t figure out how you are using bones to animate the mouth texture in yhour anime??

there we go, did a test. the current node works with animated textures. i inputted a 33frame avi into the texture and ran it at an interval using python in the node and it worked fine. the only quib i can find with using animated textures is the difficulty in running alpha encoded animations, but that can be solved with Gif animations i suppose.

ah yes, i seen the flip frames in the sequence section. I dont think this can be transferred over to textures easily. that would require some testing in blender, perhaps one could make a “texture node” where it takes in the animation texture and maybe use the function from the sequence alongside the ‘time’ node to reverse the texture animation. perhaps might be useful to look into to create a pynode that will let people use and control animated textures in the nodes. ill look into it now soon.

hehe, i didnt use bones to animate the mouth. i used bones to animate the eye textures and the hair highlight textures. its based on the same principle as using a bone to control a shapekey, except inside a material node. i basically accessed the rotation of bones in the pynode and changed the mapping vector of the texture or switch between different textures depending on angles. its still a little buggy at certain times tho >< once i clear up the bugs ill put it up on the forum so others can use, but its kind of unique so i dont know if many people will be interested in it.

Well I think this is brilliant, great work!

Would you please go into more detail, for us beginners, on how you got the current node to use an animated texture?

i can do better, i can provide the blend file and avi i was using to test to give a comparison to work with XD

http://www.megaupload.com/?d=EFIXRY6A
(link to zip file containing blend file, and an uncompressed short avi. BA forum wouldnt let me attach a file to my reply here)

Well, using animated textures instead of static textures doesnt require any extra options other than initially setting up your animated texture (start/end/framecount/etc). Just make up an animated texture and link it into the pynode just like any other texture you have been using. It still follows the same rules as a nomal texture in python.

for instance, in the file ive provided i use this frame setup:

FrameInfoArray=[
[1,38,120,180]]

So from frames 1-37 and 120-179, a static image thats in channel1 of the pynode is used (closed eyes in the example). from frames 38-119, the base channel is used, which is an animated texture (blinking). the animated texture is setup to repeat and is only 33 frames long, so its set to repeat 2-3 times (which i confirmed by rendering all 180 frames).

just to note from the file. the avi is black outside the eyes as i was testing alpha too (didnt work). Also, this is using the 10channel script, but its basically identical to the 21channel script so i dont see any problems with that.

(also, make sure the avi is in the same folder as the blend file. the script should be internal if i set things up right).

hope that helped guys^^