Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush Python API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Recent
    • Tags
    • Users
    • Login

    Thinking Particles, Mograph Cache

    Scheduled Pinned Locked Moved PYTHON Development
    3 Posts 0 Posters 376 Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • H Offline
      Helper
      last edited by

      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.

      1 Reply Last reply Reply Quote 0
      • H Offline
        Helper
        last edited by

        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 😉

        1 Reply Last reply Reply Quote 0
        • H Offline
          Helper
          last edited by

          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?

          1 Reply Last reply Reply Quote 0
          • First post
            Last post