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.