I'm really sorry for the delay, we are still working on it (hopefully we will get it resolved sooner or later)
But in any case, don't worry we didn't forget you!
Cheers,
Maxime.
I'm really sorry for the delay, we are still working on it (hopefully we will get it resolved sooner or later)
But in any case, don't worry we didn't forget you!
Cheers,
Maxime.
MAXON SDK Specialist
Hi, @Leo_Saramago!
First of all, I would like to present my apologies, for the time we ask to solve your issue. Moreover, I would like to point you, to our Q&A functionality in order to use as best as we can the new features offered by the forum.
I have setup your first post as a question and put tags to the topic.
With that's said, Motion Layer and Motion Clip are stored in some particular branch of the Motion Tag and there is currently no way to directly access Motion Clip or Motion Layer so you have to do it manually.
To know exactly how a scene is structured you can use the C++ example activeobject.cpp to help you.
Here it's an example of how to access to motion clip named "a,b,c or d" and move them to the 20th frame.
import c4d
def getListHeadFromBranches(op, branchName):
branches = op.GetBranchInfo()
for branch in branches:
if branch["name"] == branchName:
return branch["head"]
return
# Main function
def main():
obj = op
if not obj: return
# Get the motion Tag (all the data are stored in it)
tag = obj.GetTag(465003000)
if not tag: return
# Get the motion system list head from the motion tag
motionSystemListHead = getListHeadFromBranches(tag, "Motion System")
if not motionSystemListHead: return
# Get Motion layers
motionLayers = list()
motionLayer = motionSystemListHead.GetDown()
while motionLayer:
motionLayers.append(motionLayer)
motionLayer = motionLayer.GetNext()
if not motionLayers:
return
# Get Motion clip from the Motion layer
motionClips = list()
motionClipsNameAllowed = ["a", "b", "c", "d"]
for motionLayer in motionLayers:
# Get the motion Layer list head from the Layer object
motionLayerListHead = getListHeadFromBranches(motionLayer, "Motion Layer")
if not motionLayerListHead: return
motionClip = motionLayerListHead.GetDown()
if not motionClip: continue
name = motionClip.GetName()
displayedName = motionClip[c4d.ID_MT_CLIP_SOURCE].GetName() # Name displayed in the timeline in the rectangle of the motion clip
if name in motionClipsNameAllowed:
motionClips.append(motionClip)
# Move all our motions clips
for motionClip in motionClips:
duration = motionClip[c4d.ID_MT_CLIP_VIEWEND] - motionClip[c4d.ID_MT_CLIP_VIEWSTART]
startFrame = c4d.BaseTime(20, doc.GetFps())
motionClip[c4d.ID_MT_CLIP_VIEWSTART] = startFrame
motionClip[c4d.ID_MT_CLIP_VIEWEND] = startFrame + duration
c4d.EventAdd()
# Execute main()
if __name__=='__main__':
main()
But take care when modifying motionClip, and the value you enter. Since you have to modify the basecontainer directly, there is no check done and you can screw up c4d, so be sure values you enter are correct!
Moreover about marker you can find example about how to use them in this example.
Hope it makes sense if you need help, or you have any questions please let me know!
Again, all my apologies for the delay.
Cheers,
Maxime!
MAXON SDK Specialist
Hey, thanks for your reply! There's no need for apologies, Maxime, you've kept in touch and I understand it takes time to figure things out, especially in C4D with its broad range of resources. Your software is a solid robust beast.
Enough of that... lol!
I have one question right away:
When you say "you have to do it manually", you mean I have to drag Motion Sources to Motion Layers so that they become Motion Clips before I run the script?
If so, this could be a problem because I may have dozens of repetitions for each "a", "b", "c" or "d" depending on the project I'm working on... unless I could create Motion Clip copies dynamically inside a loop. Is that possible?
"The idea dictates everything."
by David Lynch
Hi @leo_saramago, first of all, you can edit your post. To do so click on the 3 little boxes in the bottom left of your post.
If I understand correctly.
And to be sure we get the same terminology and we understand the same thing.
def main():
root = doc.GetNLARoot()
obj = root.GetDown()
while obj:
print obj
obj = obj.GetNext()
With my previous script, you have to select the object, which holds the motion tag.
Then I assumed Motion Source where already set, but with the previous code snippet, you are able to iterate Motion Source and then add them to your previously created Motion Clip. And then move the Motion Clip to the correct frame.
If I misunderstand please let me know, and maybe try to summarize what the initial state and the desired final state.
Cheers,
Maxime!
MAXON SDK Specialist
Hi! Yes, I knew editing was possible, but I also thought purging posts was possible, and I messed up. Lesson learned!
The only terminology mistake from me was "Motion Clips being containers for Motion Sources". I think we'll get to the same page this time, now that I understand things a little better. Here's what I have in mind:
it's no big deal if I have to manually select each Motion Layer and run the script again. I'm trying to create this tool because having to drag dozens of Motion Sources to the Timeline Markers in every project sounds like a waste of time.
I've read somewhere I'm supposed to avoid using CallCommand, it's just that I don't know if there's a method to create a Motion Clip on the fly.
I've just thought of something else: after creating a Motion Clip with CallCommand, I'd still need to find it - and it has to be the right one, before any attributes get modified. How would I make sure it's the one the script had just created, and not another one from a previous iteration? Would it always be the last on a stack? Would I have to store them in some sort of list?
Thanks again!
"The idea dictates everything."
by David Lynch
Hi @Leo_Saramago, thanks a lot for your patience.
As you can see the Motion System is not very well exposed in the API.
But normally with the following script, it should do what you want to. (MotionLayer are named as same as the MotionSource and as the marker in the document). If you want to see the setup and the script in action
import c4d
MT_TAG = 465003000 # ID for a Motion Tag
MT_LAYER = 465003001 # ID for a MotionLayer object
MT_CLIP = 465003002 # ID for a MotionClip object
MT_SOURCESTART = 465003056 # BaseContainer ID for Start time of a Motion Source
MT_SOURCEEND = 465003057 # BaseContainer ID for End time of a Motion Source
# Get the List head of the given branch name
def getListHeadFromBranches(op, branchName):
branches = op.GetBranchInfo()
for branch in branches:
if branch["name"] == branchName:
return branch["head"]
return
# Get a list of all motion Layer stored in a motion Tag
def GetMotionLayersFromMotionTag(tag):
if not tag or not tag.CheckType(MT_TAG):
return
# Get the motion system list head from the motion tag
motionSystemListHead = getListHeadFromBranches(tag, "Motion System")
if not motionSystemListHead: return
# Get Motion layers
motionLayers = list()
motionLayer = motionSystemListHead.GetDown()
while motionLayer:
motionLayers.append(motionLayer)
motionLayer = motionLayer.GetNext()
return motionLayers
# Function to create a Motion Clip and return it
def CreateMotionClip(motionLayer):
if not motionLayer or not motionLayer.CheckType(MT_LAYER):
return
# Get the motion Layer list head from the Layer object
motionLayerListHead = getListHeadFromBranches(motionLayer, "Motion Layer")
if not motionLayerListHead: return
motionClip = c4d.BaseObject(MT_CLIP)
if not motionClip: return
motionLayer.GetDocument().AddUndo(c4d.UNDOTYPE_NEW, motionClip)
motionClip.InsertUnderLast(motionLayerListHead)
return motionClip
# Get all MotionSources in a list
def GetAllMotionSources(doc):
if not doc: return
root = doc.GetNLARoot()
obj = root.GetDown()
motionSources = list()
while obj:
motionSources.append(obj)
obj = obj.GetNext()
return motionSources
# Get all markers
def GetAllMarkers(doc):
markers = list()
marker = c4d.documents.GetFirstMarker(doc)
while marker:
markers.append(marker)
marker = marker.GetNext() #Since a marker is a BaseList2D we can use GetNext for iterate
return markers
# Get a baselist 2d by his name from a given list of BaseList2D (marker, obj(MotionSource, MotionLayer etc...), tag)
def GetBaseList2DByName(listOfBaseList2D, name):
if not listOfBaseList2D or not name: return
for bl in listOfBaseList2D:
if not isinstance(bl, c4d.BaseList2D): continue
if bl.GetName() == name:
return bl
return
# Main function
def main():
# Get selected object
obj = op
if not obj: return
# Get the motion Tag (all the data are stored in it)
tag = obj.GetTag(MT_TAG)
if not tag: return
# Get all Motion Sources from the document
motionSources = GetAllMotionSources(tag.GetDocument())
if not motionSources: return
# Get All Markers from the document
markers = GetAllMarkers(tag.GetDocument())
if not markers: return
# Get Motion layers from the tag
motionLayers = GetMotionLayersFromMotionTag(tag)
if not motionLayers:
return
doc.StartUndo()
# Iterate over all motion Layers
for layer in motionLayers:
# Get the motionSource which match the motionLayer name
motionSource = GetBaseList2DByName(motionSources, layer.GetName())
if not motionSource: continue
# Get the marker which match the motionLayer name
marker = GetBaseList2DByName(markers, layer.GetName())
if not marker: continue
# Create a new motionClip
motionClip = CreateMotionClip(layer)
if not motionClip: continue
# Define start Frame and End Frame (if the lenght is define in the marker we use this lenght, otherwise we use the lenght of the motion source)
start = marker[c4d.TLMARKER_TIME]
end = marker[c4d.TLMARKER_TIME] + marker[c4d.TLMARKER_LENGTH] if marker[c4d.TLMARKER_LENGTH] != c4d.BaseTime(0) else marker[c4d.TLMARKER_TIME] + (motionSource[MT_SOURCEEND] - motionSource[MT_SOURCESTART])
# Define our parameter
doc.AddUndo(c4d.UNDOTYPE_CHANGE, motionClip)
motionClip[c4d.ID_MT_CLIP_SOURCE] = motionSource
motionClip[c4d.ID_MT_CLIP_START] = start
motionClip[c4d.ID_MT_CLIP_END] = end
doc.EndUndo()
c4d.EventAdd()
# Execute main()
if __name__=='__main__':
main()
If you don't understand something in the code, please feel free to ask me any information.
Again I'm sorry for the huge delay we asked for answers to your first questions in the community, I hope the next one will go faster.
Cheers,
Maxime.
MAXON SDK Specialist
Hi! Almost there, almost there...
I replicated your setup and simply pasted the code above. I haven't analysed it, yet.
It works, but only the first marker for each MotionLayer gets a MotionClip. Please, try adding more markers, something that repeats like "A" "A" "B" "A" "C" "D" "A". There's no need to worry about MotionClips superimposing in the same MotionLayer because the semantics behind those markers guarantee there will never be a case where the MotionClips repeat in such a short span of time.
I'm sorry I can't reveal more about the nature of those semantics, it's not supposed to go public, but I promise I'll send you an .mp4 showcasing the amazing results this script helps come true.
"The idea dictates everything."
by David Lynch
Hi @Leo_Saramago don't worry, but please keep in mind we can help you only for problems about our API, and the SDK (like how to create motion clip? How to iterate over all the markers? How to iterate over all the motion layer).
Normally with the code, I posted previously you get everything you need in order to make the desired change.
With that's said, since the topic gets a lot of delays here is one of the possible solutions to do what you want.
import c4d
MT_TAG = 465003000 # ID for a Motion Tag
MT_LAYER = 465003001 # ID for a MotionLayer object
MT_CLIP = 465003002 # ID for a MotionClip object
MT_SOURCESTART = 465003056 # BaseContainer ID for Start time of a Motion Source
MT_SOURCEEND = 465003057 # BaseContainer ID for End time of a Motion Source
# Get the List head of the given branch name
def getListHeadFromBranches(op, branchName):
branches = op.GetBranchInfo()
for branch in branches:
if branch["name"] == branchName:
return branch["head"]
return
# Get a list of all motion Layer stored in a motion Tag
def GetMotionLayersFromMotionTag(tag):
if not tag or not tag.CheckType(MT_TAG):
return
# Get the motion system list head from the motion tag
motionSystemListHead = getListHeadFromBranches(tag, "Motion System")
if not motionSystemListHead: return
# Get Motion layers
motionLayers = list()
motionLayer = motionSystemListHead.GetDown()
while motionLayer:
motionLayers.append(motionLayer)
motionLayer = motionLayer.GetNext()
return motionLayers
# Function to create a Motion Clip and return it
def CreateMotionClip(motionLayer):
if not motionLayer or not motionLayer.CheckType(MT_LAYER):
return
# Get the motion Layer list head from the Layer object
motionLayerListHead = getListHeadFromBranches(motionLayer, "Motion Layer")
if not motionLayerListHead: return
motionClip = c4d.BaseObject(MT_CLIP)
if not motionClip: return
motionLayer.GetDocument().AddUndo(c4d.UNDOTYPE_NEW, motionClip)
motionClip.InsertUnderLast(motionLayerListHead)
return motionClip
# Get all MotionSources in a list
def GetAllMotionSources(doc):
if not doc: return
root = doc.GetNLARoot()
obj = root.GetDown()
motionSources = list()
while obj:
motionSources.append(obj)
obj = obj.GetNext()
return motionSources
# Get all markers
def GetAllMarkers(doc):
markers = list()
marker = c4d.documents.GetFirstMarker(doc)
while marker:
markers.append(marker)
marker = marker.GetNext() #Since a marker is a BaseList2D we can use GetNext for iterate
return markers
# Get a baselist 2d by his name from a given list of BaseList2D (marker, obj(MotionSource, MotionLayer etc...), tag)
def GetBaseList2DByName(listOfBaseList2D, name, remove=False):
if not listOfBaseList2D or not name: return
for bl in listOfBaseList2D:
if not isinstance(bl, c4d.BaseList2D): continue
if bl.GetName() == name:
if remove: listOfBaseList2D.remove(bl)
return bl
return
# Main function
def main():
# Get selected object
obj = op
if not obj: return
# Get the motion Tag (all the data are stored in it)
tag = obj.GetTag(MT_TAG)
if not tag: return
# Get all Motion Sources from the document
motionSources = GetAllMotionSources(tag.GetDocument())
if not motionSources: return
# Get All Markers from the document
markers = GetAllMarkers(tag.GetDocument())
if not markers: return
# Get Motion layers from the tag
motionLayers = GetMotionLayersFromMotionTag(tag)
if not motionLayers:
return
doc.StartUndo()
# Iterate over all motion Layers
for layer in motionLayers:
# Get the marker which match the motionLayer name
marker = GetBaseList2DByName(markers, layer.GetName(), remove=True)
if not marker: continue
while marker:
# Get the motionSource which match the motionLayer name
motionSource = GetBaseList2DByName(motionSources, layer.GetName())
if not motionSource: continue
# Create a new motionClip
motionClip = CreateMotionClip(layer)
if not motionClip: continue
# Define start Frame and End Frame (if the lenght is define in the marker we use this lenght, otherwise we use the lenght of the motion source)
start = marker[c4d.TLMARKER_TIME]
end = marker[c4d.TLMARKER_TIME] + marker[c4d.TLMARKER_LENGTH] if marker[c4d.TLMARKER_LENGTH] != c4d.BaseTime(0) else marker[c4d.TLMARKER_TIME] + (motionSource[MT_SOURCEEND] - motionSource[MT_SOURCESTART])
# Define our parameter
doc.AddUndo(c4d.UNDOTYPE_CHANGE, motionClip)
motionClip[c4d.ID_MT_CLIP_SOURCE] = motionSource
motionClip[c4d.ID_MT_CLIP_START] = start
motionClip[c4d.ID_MT_CLIP_END] = end
marker = GetBaseList2DByName(markers, layer.GetName(), remove=True)
doc.EndUndo()
c4d.EventAdd()
# Execute main()
if __name__=='__main__':
main()
Cheers,
Maxime.
MAXON SDK Specialist