Custom Slim Node



Slim has the ability- like an RSL shader- to incorporate custom programmed nodes. Using cutter, I chose to create a very versatile node to use as a scale pattern, which I applied to a snake model.





Reference



Writing .slim Files

A .slim file is divided into two parts. The first creates a custom interface, including color, float, and switch parameteres. The second, an RSL Function, uses the user's variables to calculate the node's output. The output is the 'result'.

Cutter makes scripting for .slim really easy - with a few clicks new parameters can be made, and those parameters can be grouped into 'collections' just as easily. Using a 'socket' to port cutter to Maya, executing the program from within Cutter updates changes into Slim.


Creating the Custom UI:


# menuInfo fundza, sineScales#0 color sineScales /Color
  
slim 1 extensions emily {
extensions fundza emil {
    template color sineScales {
        description {
            Description of the node goes here.
            }
        previewinfo {
            shadingrate 1
            objectsize 1
            objectshape Plane
            frame 1
            }
        #__custom parameters Begin____________
    
            collection manifold Manifold {
            description {The 'st' texture space.}
            detail mustvary "pixar,ST"
            state locked
            parameter point Q {
                detail mustvary
                default point(s,t,0)
                }
            parameter vector dQu {
                detail mustvary
                }
            parameter vector dQv {
                detail mustvary
                }
            }
  
    
        parameter color color1_input {
            label "Color 1"
            description "No description."
            default {0 0.119 0.297}
            detail varying
            }
        
        parameter color color2_input {
            label "Color 2"
            description "No description."
            default {0.125 0.275 0.5}
            detail varying
            }        
        
        parameter float blur {
            label "Blur"
            description "blur"
            subtype slider
            range {0 1}
            detail varying
            default 0.05
            }
            
        parameter float freq {
            label "Frequency"
            description "frequency"
            subtype slider
            range {0 50}
            detail varying
            default 10.0
            }
            
        parameter float amp {
            label "Amplitude"
            description "Amplitude"
            subtype slider
            range {0 10}
            detail varying
            default 2.0
            }
            
        collection void label {
            state closed
            drawmode all
            label {Pattern Repeat Adjustments}
            description {Description}
            
            parameter float offset {
                label "Offset Pattern"
                description "No description."
                subtype slider
                range {0 10}
                detail varying
                default 0.0
                }
                
            parameter float curve_offset {
                label "Offset Curve"
                description "No description."
                subtype slider
                range {0 3}
                detail varying
                default 1.0
                }
            
            parameter float repeatOffset {
                label "Offset Repeated"
                description "No description."
                subtype slider
                range {0 10}
                detail varying
                default 0.5
                }
                
                
            parameter float maniMirror {
                label "Mirror Manifold"
                description "No description."
                subtype switch
                range {0.0 1.0}
                provider variable
                default 0
                }
        
            parameter float switchDirection {
                label "Scale Direction"
                description "No description."
                subtype switch
                range {0.0 1.0}
                provider variable
                default 0
                }
            }
  
        parameter float switchColor {
            label "Switch colors"
            description "No description."
            subtype switch
            range {0.0 1.0}
            provider variable
            default 0
            }
  
  
        #__custom parameters End______________
  
        parameter color result {
            access output
            display hidden
            }

UI:




The first part of the RSL function connects the user inputs to variables to use in the function. Then, variables unaccessable to the user are created. The scale pattern is created with a repeated sine curve. To control the shape of the curve, the user can change the frequency and amplitude of the curve. The node's ability to repeat comes from the 'manifold' parameter. This was an easy function to add to the node as cutter supplied most of the code itself.

A large part of my code is dedicated to making it possible for the user to adjust the position of the sine curve both in the S and T direction. Also, the node has the ability to reverse the direction of the pattern based on the manifold input. That way, the scales won't have such an obvious cut-off when the ST coordinates repeat across the model. This function allows a huge range of shapes from the sineScale node. This is the giant 'if/else' part at the end where the result is given a value.


RSL Function:


        RSLFunction {
        void emilsineScales (
            point  Q;
            vector dQu;
            vector dQv;
            // parameter vars goes here...
  
            color color1_input;
            color color2_input;
            float blur;
            float freq;
            float amp;
            float offset;
            float curve_offset;
            float repeatOffset;
            float maniMirror;
            float switchDirection;
            float switchColor;
            
            output color result;
  
            )
        {
        float    s_man = mod(xcomp(Q),1);
        float    t_man = mod(ycomp(Q),1);
        
        float column = floor(xcomp(Q));
        float row = floor( ycomp(Q));
                
        float direction;
        float placeHolder;
  
        if(switchDirection == 0) {
            direction = t_man;
            placeHolder = s_man;
            } 
        else 
            {
            direction = s_man;
            placeHolder = t_man;
            }
    
        float phase = 0;
        if(mod(column, 2) != 0)
            phase = repeatOffset;
        
        //The manifold will reverse the colors if switch is on
        if(mod(column,2)!=0) {
            
            
        }
                
        float blend;    
        
        float minimum = ((sin( (direction) * freq + offset + phase)) - blur);
        float maximum = (sin( (direction) * freq + offset + phase)) + blur;
        
    
        blend = smoothstep(minimum + curve_offset, maximum + curve_offset , placeHolder *(amp));
    
        if(maniMirror == 1) {
            if(switchDirection == 0) {
                if(switchColor == 0) {
                    if(mod(column,2)!=0) 
                        result = mix( color1_input, color2_input, blend);
                    else
                        result = mix( color2_input, color1_input, blend);
                    }
                else 
                    {
                    if(mod(column,2)!=0) 
                        result = mix( color2_input, color1_input, blend);
                    else
                        result = mix( color1_input, color2_input, blend);
    
                    }
                }
            else
            {
                if(switchColor == 0) {
                    if(mod(row,2)!=0) 
                        result = mix( color1_input, color2_input, blend);
                    else
                        result = mix( color2_input, color1_input, blend);
                    }
                else 
                    {
                    if(mod(row,2)!=0) 
                        result = mix( color2_input, color1_input, blend);
                    else
                        result = mix( color1_input, color2_input, blend);    
                    }        
            }
        }
        else
        {
            if(switchColor == 0) 
                result = mix( color1_input, color2_input, blend);
            else
                result = mix( color2_input, color1_input, blend);
    
  
        }
  
        }
    }
}
}}



Parameter Outputs





Blur



Frequency



Amplitude




Offset Pattern



Offset Curve



Switch Color




Manifold Repeating



Mirrors the Manifold



Process




I incorporated my 'sineScale' node by layering them to get a varied color and texture across the snake. Into the bottom most sineScale's, I used ramps as color inputs. The ramps were similar - yet slightly different - yellow and green S ramps so the snake would become darker closer to the head. The eye's and tongue were also created using Slim- primarily with ramps for color and subsurface shading.


Patterns with Manifold Mirroring


Having the ability to make the adjacent S or T repititions mirror each other provided a lot of control to create more interesting patterns. If I were to create a different type of snake with a more patterned back - like a rattlesnake or coral snake - this ability could help make that happen.









Conclusion

The functionality of Slim definitely comes from the different combinations of inputs and outputs the user creates within a shader's node network. Creating a custom node provides more specific control - and is relatively easy to do for a simple pattern such as the sineScale node. Adding attributes such as manifold and offset controls could take the simple sine shape and manipulate it extensively.

However, programming nodes doesn't provide limitless possibilities. My original intent was to create an l-system pattern so that the user could creat veins in leaves and skin. However, slim's tcl didn't allow for the 'turtle' function. It was also - for practical purposes - imposible to create and call upon procedures within the node, which would have been needed for recursion.