Community
Maya Programming
Welcome to Autodesk’s Maya Forums. Share your knowledge, ask questions, and explore popular Maya SDK topics.
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Coding challenge! Please help me achieve this effect

2 REPLIES 2
Reply
Message 1 of 3
jmollot
287 Views, 2 Replies

Coding challenge! Please help me achieve this effect

If anyone is up to it I have an effect I’m trying to create in Maya. I’m new to using MEL and/or python and/or expressions in Maya. That being said I do have significant experience coding in Java.  I would appreciate if anyone could give me some tips on how to achieve the effect I’m going after as I learn to program in Maya, or alternatively I would of course be grateful if anyone simply wants to write up the code…here’s the plan:


I want to do something to a specified object. Here is psuedocode for what I want to do - first create an empty dynamic/array list (let’s call it “vertexList”) that will eventually contain contain tuples that contain two things - the tuples will be 1) a vertex, and 2) a list containing three float numbers….then every six frames do this: for each vertex of the object {

if the vertex DOES exist in vertexList{ find the tuple that contains the vertex and then get the other value in the tuple (the list of 3 numbers) and save it as a temporary variable called “tempTransform.” Subtract the first value in tempTransform from the x translate value of the current vertex, subtract the second value in tempTransform from the y value of the vertex, and subtract the third value in tempTransform from the z value of the vertex, then remove the tuple containing this vertex from vertexList} Else If the current vertex is NOT in vertexList{ generate a random integer between 1 and 4 (inclusive), then if the number is 2{ create a list with 3 values, where each value is a random float number between 0 and 1, and save this list of three numbers as a temporary variable called “tempTransform.” Add the first number in tempTransform to the current vertex’s x translation value, the second number to the vertex’s y translation value, and the third number to the vertex’s x translation value, and then add to vertexList a tuple containing the current vertex and tempTransform} else if the number is not 2{ do nothing and continue the for loop going to the next vertex.} } } So in summary, my code should cause the object to have its vertices looped through every 6 frames and for each vertex there should be a 25% chance it is moved some random distance between 0 and 1 away from its current position, and if it is moved it should be added to the tuple list “vertexList”…unless it was already moved in the previous cycle, in which case it should be transformed back to its initial relative position and its associated tuple should be removed from vertexList. Ultimately, this should create an interesting glitching effect on the object.

Labels (5)
2 REPLIES 2
Message 2 of 3
jmreinhart
in reply to: jmollot

Before you dive into developing something for this specific purpose, there are some vanilla Maya nodes that can get you pretty close.

 

  1. Select your mesh
  2. Go to deform>texture
  3. Select the texture deformer
  4. Open the attribute editor
  5. Click on the white checker box next to the "texture" attribute. 
  6. jmreinhart_2-1721310399004.png

     

  7. Select the noise texture under 2D textures.
  8. Your mesh should now be spikey.
  9. Adjust the settings to get a slightly different effect.
  10. To make it animated, just set keys on the "time" attribute of the noise texture.

This will only make the points offset along 1 axis, but you could apply 3 texture deformers (one for each axis) or you could change the direction setting on the texture deformer to normal which will make the points only offset along the normals.

 

If that doesn't get you the desired result let me know and we can go from there 🙂

 

Also, just so you know, you can use the "</>" button on the forum to insert a code block, which will help prevent the formatting of code or pseudo code from getting messed up.

Message 3 of 3
Kahylan
in reply to: jmollot

Hi!

 

So if you want to do this with code there are two ways of achieving this. One is to use an Expression, but for that you have to use MEL, you also have to use the approach that you outlined where you have to use multiple arrays to keep track of which vertex is currently in its deformed state and which isn't.

I wouldn't do this way, because arrays in MEL are absolute hell to work with, there are only like 3 builtin funtions that can manipulate an array, if you want to delete out elements from an array you have to recreate it without the element in question. Unless you convert all your inner arrays into strings, because string arrays have some special functions you can work with. But all the converting to and from strings, tokenizing etc is just not something I would recommend. Also since this funtion would have to run through every vertex on every sixth frame, with an expression this effect would would massively slow down playback, increasing with every vertex added. 

The other way is to do it with keyframes in python. This will bake the effect on your vertices using point-level-animation.
If i'd do it like you suggested in your pseudocode, it would look something like this:

import maya.cmds as mc
import random as rand


sel = mc.ls(sl = True) or []
startTime = mc.playbackOptions(minTime = True, q = True)
endTime = mc.playbackOptions(maxTime = True, q = True)

vertexDict = {}
startPostDict = {}

for s in sel:
    vertexCount = mc.polyEvaluate(v= True)
    shapes = mc.listRelatives(s, s= True) or []
    for sh in shapes:
        startTimeNew = startTime +6
        while startTimeNew < endTime:
    
            for i in range(0, vertexCount):
                startPos = mc.getAttr("{0}.vtx[{1}]".format(sh, i))[0]
                mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTime), v = startPos[0])
                mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTime), v = startPos[1])
                mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTime), v = startPos[2])
                    
                if "{0}.vtx[{1}]".format(sh, i) in vertexDict:
                    changedPosition = vertexDict.get("{0}.vtx[{1}]".format(sh, i))
                    
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew -1), v = changedPosition[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew -1), v = changedPosition[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew -1), v = changedPosition[2])
                                        
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew), v = startPos[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew), v = startPos[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew), v = startPos[2])
                    
                    
                    del vertexDict["{0}.vtx[{1}]".format(sh, i)]
    
                elif rand.randint(1,5) == 2:
                    
                    changeVector = (rand.uniform(0,1),rand.uniform(0,1), rand.uniform(0,1))
                    position = [startPos[0]+changeVector[0],startPos[1]+changeVector[1],startPos[2]+changeVector[2]]
                   
                    
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew -1), v = startPos[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew -1), v = startPos[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew -1), v = startPos[2])
                                        
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew), v = position[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew), v = position[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew), v = position[2])
                    vertexDict["{0}.vtx[{1}]".format(sh, i)] = position
    
            startTimeNew = startTimeNew + 6    

 

But since I work with keyframes already and I know that the effect will be reversed after 6 frames anyways, I can just skip the whole adding it to a dictionary and removing from the dictionary by simply adding the keyframes needed after 6 frames and skipping that frames evaluation. Which makes the code faster and less cluttered

 

import maya.cmds as mc
import random as rand


sel = mc.ls(sl = True) or []
startTime = mc.playbackOptions(minTime = True, q = True)
endTime = mc.playbackOptions(maxTime = True, q = True)

for s in sel:
    vertexCount = mc.polyEvaluate(v= True)
    shapes = mc.listRelatives(s, s= True) or []
    for sh in shapes:
        for i in range(0, vertexCount):
            startTimeNew = startTime +6
            while startTimeNew < endTime:
                startPos = mc.getAttr("{0}.vtx[{1}]".format(sh, i))[0]
                mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTime), v = startPos[0])
                mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTime), v = startPos[1])
                mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTime), v = startPos[2])
                    
    
                if rand.randint(1,5) == 2:
                    
                    changeVector = (rand.uniform(0,1),rand.uniform(0,1), rand.uniform(0,1))
                    position = [startPos[0]+changeVector[0],startPos[1]+changeVector[1],startPos[2]+changeVector[2]]
                   
                    
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew -1), v = startPos[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew -1), v = startPos[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew -1), v = startPos[2])
                                        
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew), v = position[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew), v = position[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew), v = position[2])
                    
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew+5), v = position[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew+5), v = position[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew)+5, v = position[2])
                    
                    
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew +6), v = startPos[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew +6), v = startPos[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew +6), v = startPos[2])
                                        
                    startTimeNew = startTimeNew +6

    
                startTimeNew = startTimeNew + 6    

 

I'm not a huge fan of effects that are this static, where you have it after 6 frames, and the distance is 0-1. So I would probably add some parameters that easily allow me to change the maximum offset and the time between changes when I call the function.

 

import maya.cmds as mc
import random as rand


def glitchDeform(maxDistance= 1, step = 6):
    sel = mc.ls(sl = True) or []
    startTime = mc.playbackOptions(minTime = True, q = True)
    endTime = mc.playbackOptions(maxTime = True, q = True)
    
    for s in sel:
        vertexCount = mc.polyEvaluate(v= True)
        shapes = mc.listRelatives(s, s= True) or []
        for sh in shapes:
            for i in range(0, vertexCount):
                startTimeNew = startTime +step
                while startTimeNew < endTime:
                    startPos = mc.getAttr("{0}.vtx[{1}]".format(sh, i))[0]
                    mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTime), v = startPos[0])
                    mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTime), v = startPos[1])
                    mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTime), v = startPos[2])
                        
        
                    if rand.randint(1,5) == 2:
                        
                        changeVector = (rand.uniform(0,maxDistance),rand.uniform(0,maxDistance), rand.uniform(0,maxDistance))
                        position = [startPos[0]+changeVector[0],startPos[1]+changeVector[1],startPos[2]+changeVector[2]]
                       
                        
                        mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew -1), v = startPos[0])
                        mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew -1), v = startPos[1])
                        mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew -1), v = startPos[2])
                                            
                        mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew), v = position[0])
                        mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew), v = position[1])
                        mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew), v = position[2])
                        
                        mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew+(step-1)), v = position[0])
                        mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew+(step-1)), v = position[1])
                        mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew)+(step-1), v = position[2])
                        
                        
                        mc.setKeyframe("{0}.vtx[{1}].px".format(sh, i), t = (startTimeNew +step), v = startPos[0])
                        mc.setKeyframe("{0}.vtx[{1}].py".format(sh, i), t = (startTimeNew +step), v = startPos[1])
                        mc.setKeyframe("{0}.vtx[{1}].pz".format(sh, i), t = (startTimeNew +step), v = startPos[2])
                                            
                        startTimeNew = startTimeNew +step
    
        
                    startTimeNew = startTimeNew + step

glitchDeform(maxDistance= 1, step = 6) 

 

Short warning: this code still is faaaaaaar from perfect. It also runs pretty slow, so adding a processbar would probably be a good idea.
But it does what you wanted.

 

If you want to have this effect work in runtime, I would suggest that you take the rigging approach that @jmreinhart outlined in his response.

 

I hope it helps!

 

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk Design & Make Report