Hi, @happygrass_cn first of all welcome in the plugincafe community.
While you have deleted your topic (probably because you solved it). I had a solution that explores a few areas in Cinema 4D that are not so well documented so I restored it.
So my solution uses a cloner with 2 clones (one animated and the other one similar but without animation).
I also define a thinking particle channel data( a boolean "isMoving").
A python effector will read this attribute and according to this attribute will affect the displayed cloned object to be either the animated one or not.
Of course, this does not provide interpolation between animation so it can look a bit odd.
Finally in Python SetPData does not support boolean, so "IsMoving" is not a boolean (DTYPE_BOOL) but an integer(DTYPE_LONG) that I thread as a boolean.
The Python generator does the next things:
Thinking Particles Creation.
Custom Data Channel Creation/Assignation.
Movement of particles.
import c4d
def GetParticleSystemAndRootGroup():
# Retrieves the Particles System of the current document
pSys = doc.GetParticleSystem()
if pSys is None:
raise RuntimeError("op is none, please select one object.")
# Retrieves the Root group (where all particles belongs as default)
rootGrp = pSys.GetRootGroup()
if rootGrp is None:
raise RuntimeError("Failed to retrieve root group of tp master system.")
return pSys, rootGrp
def GetIsMovingChannelId():
# Retrieves the Particles System and the root group of the current document
pSys, rootGrp = GetParticleSystemAndRootGroup()
for x in xrange(pSys.NumDataChannels()):
if pSys.DataChannelName(x) == "isMoving(Integer)":
return x
return False
def CreateParticle(count):
# Retrieves the Particles System and the root group of the current document
pSys, rootGrp = GetParticleSystemAndRootGroup()
if pSys.NumParticles() >= count:
return
# Allows each particles to get a custom colors
rootGrp[c4d.PGROUP_USE_COLOR] = False
# Creates 90 Particles
particlesIds = pSys.AllocParticles(count)
if not particlesIds:
raise RuntimeError("Failed to create X TP particles.")
# Check if the "isMoving"" Channel data already exist and if not create it
# This Boolean "isMoving" will store the moving state of a particle
if GetIsMovingChannelId() is False:
pSys.AddDataChannel(c4d.DTYPE_LONG, "isMoving")
isMovingId = GetIsMovingChannelId()
if isMovingId is False:
raise RuntimeError("Failed to retrieve IsMoving particle Data")
# Assigns position and colors for each particles
for particleId in particlesIds:
# Checks if particles ID is ok
if particleId == c4d.NOTOK:
continue
# Defines a lifetime of 1000 frame for each particles
pSys.SetLife(particleId, c4d.BaseTime(1000))
# Calculates a position
sin, cos = c4d.utils.SinCos(particleId)
pos = c4d.Vector(sin * 300.0, cos * 300.0, particleId * 30.0)
# Assigns position
pSys.SetPosition(particleId, pos)
# Calculates a color
hsv = c4d.Vector(float(particleId) * 1.0 / count, 1.0, 1.0)
rgb = c4d.utils.HSVToRGB(hsv)
# Assigns color
pSys.SetColor(particleId, rgb)
# Assigns a "freeze" data to indicate either the particle is stoped or not
pSys.SetPData(particleId, isMovingId, False)
return particlesIds
def MoveParticle(frame):
# Retrieves the Particles System and the root group of the current document
pSys, rootGrp = GetParticleSystemAndRootGroup()
isMovingId = GetIsMovingChannelId()
if isMovingId is False:
raise RuntimeError("Failed to retrieve IsMoving particle Data")
for particleId in xrange(pSys.NumParticles()):
# If particle don't belong to the root group
if pSys.Group(particleId) != rootGrp:
continue
sin, cos = c4d.utils.SinCos(particleId + (frame/40.00))
pos = c4d.Vector(sin * 300.0, cos * 300.0, particleId * 30.0)
# Assigns position
pSys.SetPosition(particleId, pos)
# Assigns a "freeze" data to indicate either the particle is stoped or not
pSys.SetPData(particleId, isMovingId, True)
def FreezeParticle():
# Retrieves the Particles System and the root group of the current document
pSys, rootGrp = GetParticleSystemAndRootGroup()
isMovingId = GetIsMovingChannelId()
if isMovingId is False:
raise RuntimeError("Failed to retrieve IsMoving particle Data")
for particleId in xrange(pSys.NumParticles()):
# If particle don't belong to the root group
if pSys.Group(particleId) != rootGrp:
continue
# Assigns a "freeze" data to indicate either the particle is stoped or not
pSys.SetPData(particleId, isMovingId, 0)
def main():
currentFrame = doc.GetTime().GetFrame(doc.GetFps())
if currentFrame == 0:
CreateParticle(20)
# Don't move particles from frame 150 to 250
elif 150 <= currentFrame <= 250:
if currentFrame == 150:
FreezeParticle()
else:
MoveParticle(currentFrame)
The Python Effector set in Full Control does the next things:
Read the data for each particle of the Custom Data Channel
Defines the cloned used according to this value
import c4d
def GetParticleSystemAndRootGroup():
# Retrieves the Particles System of the current document
pSys = doc.GetParticleSystem()
if pSys is None:
raise RuntimeError("op is none, please select one object.")
# Retrieves the Root group (where all particles belongs as default)
rootGrp = pSys.GetRootGroup()
if rootGrp is None:
raise RuntimeError("Failed to retrieve root group of tp master system.")
return pSys, rootGrp
def GetIsMovingChannelId():
# Retrieves the Particles System and the root group of the current document
pSys, rootGrp = GetParticleSystemAndRootGroup()
for x in xrange(pSys.NumDataChannels()):
if pSys.DataChannelName(x) == "isMoving(Integer)":
return x
return False
def main() :
moData = c4d.modules.mograph.GeGetMoData(op)
if moData is None:
return False
cnt = moData.GetCount()
mdClone = moData.GetArray(c4d.MODATA_CLONE)
hasField = op[c4d.FIELDS].HasContent()
# Retrieves the Particles System and the root group of the current document
pSys, rootGrp = GetParticleSystemAndRootGroup()
# Retrieve the isMovingData channel ID
isMovingId = GetIsMovingChannelId()
if isMovingId is False:
raise RuntimeError("Failed to retrieve IsMoving particle Data")
# Maybe there is more particles allocated than what's is used in the cloner (aka mutlipe particle group)
# So we loop over them
cloneId = 0
for particleId in xrange(pSys.NumParticles()):
# If particle don't belong to the root group
if pSys.Group(particleId) != rootGrp:
continue
if cloneId > cnt:
raise RuntimeError("There is more cloneID than allowed")
# Retrieve the internal IsMoving Data and check if the particle is moving,
isMoving = pSys.GetPData(particleId, isMovingId)
if isMoving == 1:
# Defines the float value representing the cloned 0 = First child, 1 = last child
mdClone[cloneId] = 0.0
else:
mdClone[cloneId] = 1.0
cloneId +=1
moData.SetArray(c4d.MODATA_CLONE, mdClone, hasField)
return True
And the attached scene with everything together.
generatorTp.c4d
Cheers,
Maxime.