Thinking Particles, Mograph Cache



  • On 08/10/2013 at 15:34, xxxxxxxx wrote:

    I am having a challenge with Thinking Particles, Mograph Cache, and Python: All the code runs without errors and sets the correct keys, but I must be missing a key idea somewhere about thinking particles—probably in the sequence of what has to happen first, second, third, and so on. Any comments would be greatly appreciated.
     
    To picture the scene, I have many towers in whose tops lights fly around like fireflies. These lights turn on and off at different frames for different durations, in either the same or in different towers.
     
    I am using the Volume Emitter from the Content Browser—two for each tower, an upper and lower. I assign a unique particle group for the two emitters for each tower. I use one cloner for each tower, set to object mode to generate the lights, using the particle group assigned to the individual tower. From an xml database, I read in a series of values that tell when to turn on the particles from a given frame to another frame and for which tower. I set the emitters' type to Shot, keyframing the On parameter to True on one frame and then False on the next, and keyframe the Life parameter for the duration between frames. Then I bake the particles using the Mograph Cache Tag. Lastly, I delete the On parameter tracks of the emitters and set the On value to False, expecting that the cloner will use the cache and the emitter is no longer necessary.
     
    The code works for a single tower, but not for two. With different settings, I can get one or the other tower working. For the tower that doesn't work, it seems that the particles were never turned on for that tower (the cache is empty, even though, as the script runs, I can see the cache window appear, showing the system is baking the clones). Or, with other settings, I've gotten odd results such as having too many particles appearing in the towers, or having some cached To and From frame areas with no data.
     
    One key seems that I need to reset the timeline to 0, which I do in the Python code, but maybe someone can explain why this would be so and, if doing so resets the master particle system, does it do so asynchronously (which may account for the odd results) or synchronously, pausing the rest of the script until it recalculates the particles. And when is it "safe" to do this?

    Another key seems to be the state of the objects when the script begins. Was the particle system turned off or on when it started? Was there already a track? Were the cache tags empty? So I wrote a set up script to control the state.
     
    On one occasion, it worked perfectly, but when I reset the starting values to re-run the program, it stopped working and I am guessing in the dark about what is required.

    Here is the relevant code for the setup:

    def SetStartValues(tower) :  
      topEmitter=GetChildByName(tower, "Glow_Dot_Emitter_Top")  
      
      #set a unique random seed  
      CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(30)), random.randint(1,1000),0)  
        
      bottomEmitter=GetChildByName(tower, "Glow_Dot_Emitter_Bottom")  
      
      #set a unique random seed  
      CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(30)), random.randint(1,1000),0)  
      
      #find the "on" track and delete it  
      onTrack = topEmitter.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)))  
      if onTrack:  
          onTrack.Remove()  
                        
      onTrack = bottomEmitter.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)))  
      if onTrack:  
          onTrack.Remove()  
                
      #turn the emitter on without a keyframe  
      topEmitter[c4d.ID_USERDATA,1] = True  
      bottomEmitter[c4d.ID_USERDATA,1] = True  
      
      #Clear the cache  
      glowDotCloner = GetChildByName(tower, "Glow Dots Cloner")  
      cacheTag = glowDotCloner.GetFirstTag()  
      cacheTag[c4d.MGCACHETAG_ACTIVE] = True  
      cacheTag[c4d.MGCACHETAG_BAKESEQUENCE_ACTIVE] = True  
      cacheTag[c4d.MGCACHETAG_LOOP] = False  
      c4d.CallButton(cacheTag,c4d.MGCACHETAG_CLEARCACHE)
    

    Here is the relevant code for setting the values:

    def main() :  
      #set up the databases of settings  
        
      SetUpScene() #set up the default values at frame zero  
        
      #import the xml databases and convert them into list objects  
      masterFramesList = GetMasterXMLList()  
      setsList = GetSetsXMLList()  
      colorsList = GetColorsXMLList()  
      dynamicsList = GetDynamicsXMLList()  
        
      for frame in masterFramesList:  
          towerSet = []  
      
          #get the set of towers  
          if (frame.attrib["namedSet"] is not "") :  
              found = False  
              for name in setsList:  
                    
                  if (name.attrib['SetName'] == frame.attrib["namedSet"]) :  
                    
                      towerSet = CreateTowerSet(name.attrib["towers"])  
                      print "Tower set " + frame.attrib["namedSet"] + " has been chosen for frame " + frame.attrib["beginFrame"]   
                      found = True  
                      break  
                        
              if (found == False) :  
                  print ("*******Set name "+frame.attrib["namedSet"]+" could not be found in setsList.")  
                  return  
                
          else:  
            
              towerSet = CreateTowerSet(frame.attrib["set"])  
            
            
          for tower in towerSet:  
                
              #get the actual objects on the tower and set up the variables  
              towerObj = doc.SearchObject(tower) #each tower has its own particle group  
              lightName = GetLightName(frame.attrib["musicalLayer"])  
              lightObjName = lightName + " Obj"  
              spotlightName = GetSpotlightName(frame.attrib["musicalLayer"])  
              spotlightShaderName = GetSpotlightShaderName(frame.attrib["musicalLayer"])  
              rampUpFrame = int(frame.attrib["beginFrame"])-9  
              startFrame = int(frame.attrib["beginFrame"])  
              endFrame = int(frame.attrib["endFrame"])  
              rampDownFrame = int(frame.attrib["endFrame"])+9  
      
              topEmitter = None  
              bottomEmitter = None  
              glowDotCloner = None  
              cacheTag = None  
      
              if (towerObj) :  
                    
                  #get the different objects that need settings  
                    
      
                  fireflyLight = GetChildByName(towerObj, "Glow_Dot_Light") #the light object for the cloner  
                  topEmitter = GetChildByName(towerObj, "Glow_Dot_Emitter_Top") #each tower has two emitters: both are assigned to a single particle group representing that tower  
                  bottomEmitter = GetChildByName(towerObj, "Glow_Dot_Emitter_Bottom")  
      
                  #different options to trigger the particles. Do they need to be turned on?  
                  #CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), True,0)  
                  #CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), True,0)  
      
                
                  #now set the fireflies, only if the layer is full (or maybe top, as well)  
                    
                  if (frame.attrib["musicalLayer"] == "full") :  
        
                      particleLife = rampDownFrame-(rampUpFrame-1) #calculate the particle life  
                        
                      CreateKey(fireflyLight,c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), 0.00,rampUpFrame)  
                      CreateKey(fireflyLight,c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), 1.84,startFrame)  
                      CreateKey(fireflyLight,c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), 1.84,endFrame)  
                      CreateKey(fireflyLight,c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), 0.00,rampDownFrame)  
      
                      #CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), True,0) #to set up particles at the beginning?  
                      #CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,1) #to set up particles at the beginning?  
      
                      #set a random seed  
                      CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(30)), random.randint(1,1000),rampUpFrame)   
                      #turn on and off the emitter, which is set to Shot  
                      CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), True, rampUpFrame)  
                      CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,rampUpFrame + 1)  
                      CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(6)), particleLife, rampUpFrame) #life of the particles  
      
                      #CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), True,0) #to set up particles                      
                      #CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,1) #to set up particles  
      
      
                      #set a random seed  
                      CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(30)), random.randint(1,1000),rampUpFrame)  
                      #turn on and off the emitter, which is set to Shot   
                      CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), True, rampUpFrame)  
                      CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,rampUpFrame + 1)  
                      CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(6)), particleLife, rampUpFrame) #life of the particles  
                        
                      print "Set the top and bottom emitters for frame " + str(startFrame)  
      
                      GoToFrame(0) #possibly to reset the particles?  
                      GoToFrame(1)  
                      GoToFrame(0)  
                        
                        
                       
                      #Bake particles  
                      glowDotCloner = GetChildByName(towerObj, "Glow Dots Cloner") #each tower has one cloner  
                        
                      cacheTag = glowDotCloner.GetFirstTag()  
                      cacheTag[c4d.MGCACHETAG_ACTIVE] = True  
                      cacheTag[c4d.MGCACHETAG_BAKESEQUENCE_ACTIVE] = True  
                      cacheTag[c4d.MGCACHETAG_LOOP] = False  
                      cacheTag[c4d.MGCACHETAG_BAKEFROM] = c4d.BaseTime(rampUpFrame,doc.GetFps())  
                      cacheTag[c4d.MGCACHETAG_BAKETO] = c4d.BaseTime(rampDownFrame,doc.GetFps())  
                      #CreateKey(cacheTag, c4d.DescID(c4d.DescLevel(c4d.MGCACHETAG_BAKEFROM)), c4d.BaseTime(rampUpFrame,doc.GetFps()), rampUpFrame) #other options to see what is needed  
                      #CreateKey(cacheTag, c4d.DescID(c4d.DescLevel(c4d.MGCACHETAG_BAKETO)), c4d.BaseTime(rampDownFrame,doc.GetFps()), rampUpFrame)  
                        
                      c4d.CallButton(cacheTag,c4d.MGCACHETAG_BAKESEQUENCE)  
                        
                      print "Baked sequence on " + towerObj.GetName() + " for frame "+ str(startFrame)  
                        
                      #clear emitter tracks and turn off at this time as an option?  
                      #CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,rampUpFrame)  
                      #CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,rampUpFrame+1)  
                      #CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,rampUpFrame)  
                      #CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,rampUpFrame+1)  
      
                       
                      #c4d.EventAdd(c4d.EVENT_FORCEREDRAW)  
      
      
                  #Now that the cloner cache tag is filled, remove the tracks. Flush keys as an alternative to make a difference?  
                  onTrack = topEmitter.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)))  
                  if onTrack:  
                     onTrack.Remove()  
                        
                     onTrack = bottomEmitter.FindCTrack(c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)))  
                     if onTrack:  
                     onTrack.Remove()  
                
                 #CreateKey(topEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,0) #set key while keeping track as an alternative?  
                 #CreateKey(bottomEmitter, c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA), c4d.DescLevel(1)), False,0)  
                 topEmitter[c4d.ID_USERDATA,1] = False  #set emitter to off  
                 bottomEmitter[c4d.ID_USERDATA,1] = False  
      
                
       
      c4d.EventAdd(c4d.EVENT_FORCEREDRAW)  
        
    if __name__=='__main__':  
      main()  
    

    Thanks for your comments.



  • On 08/10/2013 at 17:56, xxxxxxxx wrote:

    My first guess would be a threading problem. Have you tried to add something like that ?

    for tower in towerSet:
      # ... wall of code ...
    c4d.CallButton(cacheTag,c4d.MGCACHETAG_BAKESEQUENCE)
      time.sleep(10)
    

    The example does assume that the baking process does take about 10 seconds per tower. And yes that approach on handling threading problems is illegal in more than 26 countries ;)



  • On 16/10/2013 at 12:53, xxxxxxxx wrote:

    Thanks for your response. I waded into Threads and couldn't quite understand how to use the functions for what I needed to accomplish, but I solved the problem simply by removing the cache part of the script into a separate script and then running it separately, after all the other settings in the original script were made.

    But for my own edification: Is the goal of the thread functions to work like a traffic cop, holding up parts of the script while another one runs, so that one can wait on another?


Log in to reply