Hi all,
Our studio added an awesome publishing plugin to our pipeline called Pyblish and I'm currently developing sense checks to work with it.
One of the requests is to extract a .aec file, ready to be imported to After Effects.
Unfortunately, when extracting the objects transformations values Cinema crashes and looking at the bug report it shows the following. Let me know if you need full report, please.
CRITICAL: Stop: Current take is nullptr document data could be corrupted [takemanager.cpp(505)]
To understand what I've done here's the latest full code:
This is working in the Pyblish context and I understand if there are a few parts that may not easy to understand without knowing the tool.
I commented on the lines which I think are causing the crashing.
"""
Extract .aec file into a server path
"""
import os
import pyblish.api as api
import c4d
from c4d import utils
DOC = c4d.documents.GetActiveDocument()
DOC_NAME = DOC.GetDocumentName().split('.')[0]
DOC_PATH = DOC.GetDocumentPath()
def aec_file(data):
full_doc_name = DOC.GetDocumentName()
doc_name = os.path.splitext(full_doc_name)
abs_path = DOC.GetDocumentPath()
file_path = '{0}/05_Animations/04_AE'.format(os.path.dirname(os.path.dirname(abs_path)))
if os.path.exists(file_path):
txt_file_path = os.path.join(file_path, 'AEC', '{}.aec'.format(doc_name[0]))
with open(txt_file_path, 'w') as txt_file:
for line in data:
txt_file.write(''.join(line))
return file_path
class ExtractAEC(api.InstancePlugin):
"""
Extract .aec file into server path
"""
order = api.ExtractorOrder
families = ["animation.assets"]
label = "AEC"
def process(self, instance):
if instance.data["name"] == "Cameras":
# Start data file
global extract_data
extract_data = ['CINEMA 4D COMPOSITION\n\n']
bp_render = DOC.GetFirstRenderData()
root_layer = DOC.GetLayerObjectRoot()
global layer
layer = c4d.documents.LayerObject()
layer.SetName('Temp_Layer')
layer.InsertUnder(root_layer)
cam_lst = [cam[0] for cam in instance.data["cameras"]]
# We create a layer and solo the object under it to improve performance.
layer_data = layer.GetLayerData(DOC)
layer_data['solo'] = True
layer.SetLayerData(DOC, layer_data)
DOC.ChangeNBit(c4d.NBIT_SOLO_LAYER, c4d.NBITCONTROL_SET)
res_x = bp_render[c4d.RDATA_XRES_VIRTUAL]
res_y = bp_render[c4d.RDATA_YRES_VIRTUAL]
extract_data.extend(['RESOLUTION {0} {1}\n'.format(int(res_x), int(res_y))])
# Time frames
global full_start_frame
full_start_frame = cam_lst[0][c4d.ID_USERDATA, 3]
global full_end_frame
full_end_frame = cam_lst[-1][c4d.ID_USERDATA, 4]
time_range = full_end_frame + 1
global fps
fps = bp_render[c4d.RDATA_FRAMERATE]
if bp_render[c4d.RDATA_FIELD] == 0:
field = 'NONE'
elif bp_render[c4d.RDATA_FIELD] == 1:
field = 'EVEN'
else:
field = 'ODD'
aspect = bp_render[c4d.RDATA_PIXELASPECT]
extract_data.extend(['FROM {0}\n'
'TO {1}\n'
'RANGE {2}\n'
'FPS {3}\n'
'FIELDS {4}\n'
'ASPECT {5}\n'
'VERSION 7\n\n'.format(full_start_frame,
full_end_frame,
time_range,
fps,
field,
aspect)])
# Composition
extract_data.extend(['COMPOSITION NRM ON "{0}"\n'
'{1}\n'
'\tSTEREONUM -1\n'
'{2}\n\n'.format(DOC_NAME, '{', '}')])
# Cameras
for cam in cam_lst:
cam_layer = cam.GetLayerObject(DOC)
cam.SetLayerObject(layer)
start_frame = cam[c4d.ID_USERDATA, 3]
end_frame = cam[c4d.ID_USERDATA, 4]
focal = utils.RadToDeg(cam[c4d.CAMERAOBJECT_FOV])
extract_data.extend(['CAMERA "{0}"\n'
'{1}\n'
'\tSHOWFROM {2}\n'
'\tSHOWTO {3}\n'.format(cam.GetName().split(' ')[0],
'{',
start_frame,
end_frame)])
for key in range(start_frame, end_frame + 1):
# Is this correct?
DOC.SetTime(c4d.BaseTime(key, float(fps)))
DOC.ExecutePasses(None, True, False, False, 0)
c4d.EventAdd(c4d.EVENT_ANIMATE)
pos = cam.GetAbsPos()
rot = cam.GetAbsRot()
# This is where it usually crashes. Not sure if anything to do with converting Radians to Degrees.
extract_data.extend(['\tKEY {0} '
'{1:.3f} {2:.3f} {3:.3f} '
'{4:.3f} {5:.3f} {6:.3f} '
'{7} 100 0\n'.format(key,
pos.x, pos.y, pos.z,
utils.RadToDeg(rot.x),
utils.RadToDeg(rot.y),
utils.RadToDeg(rot.z),
focal)])
cam.SetLayerObject(cam_layer)
extract_data.extend('}\n\n')
# Trackers
if instance.data["name"] == "External Comp Tags":
trackers = instance.data["compTags"]
for tracker in trackers:
tracker_layer = tracker.GetLayerObject(DOC)
tracker.SetLayerObject(layer)
extract_data.extend(['NULL2 "{0}"\n'
'{1}\n'
'\tNULLTYPE 1\n'
'\tSIZEX 100\n'
'\tSIZEY 100\n'
'\tANCHOR 3\n'
'\tORIENTATION 1\n'
'\tCOLOR 1 0 0\n'
'\tSHOWFROM {2}\n'
'\tSHOWTO {3}\n'.format(tracker.GetName(),
'{',
full_start_frame,
full_end_frame)])
for key in range(full_start_frame, full_end_frame + 1):
# Is this correct?
DOC.SetTime(c4d.BaseTime(key, float(fps)))
DOC.ExecutePasses(None, True, True, True, 0)
c4d.EventAdd(c4d.EVENT_ANIMATE)
pos = tracker.GetMg().off
rot_xyz = c4d.utils.MatrixToHPB(tracker.GetMg(), c4d.ROTATIONORDER_XYZGLOBAL)
rot_hpb = c4d.utils.MatrixToHPB(tracker.GetMg(), c4d.ROTATIONORDER_HPB)
# # This is where it usually crashes. Not sure if anything to do with converting Radians to Degrees.
extract_data.extend(['\tKEY {0} '
'{1:.3f} {2:.3f} {3:.3f} '
'{4:.3f} {5:.3f} {6:.3f} '
'{7:.3f} {8:.3f} {9:.3f}\n'.format(key,
pos.x, pos.y, pos.z,
utils.RadToDeg(rot_xyz.x),
utils.RadToDeg(rot_xyz.y),
utils.RadToDeg(rot_xyz.z),
utils.RadToDeg(rot_hpb.x),
utils.RadToDeg(rot_hpb.y),
utils.RadToDeg(rot_hpb.z))])
tracker.SetLayerObject(tracker_layer)
extract_data.extend('}\n\n')
# Markers
first_marker = c4d.documents.GetFirstMarker(DOC)
markers = []
# If a marker exists add it to the markers list
if first_marker is not None:
while first_marker is not None:
markers.append(first_marker)
first_marker = first_marker.GetNext()
for marker in markers:
extract_data.extend(['MARKER "{0}" {1}\n'.format(marker.GetName(),
marker[c4d.TLMARKER_TIME].GetFrame(24))])
DOC.ChangeNBit(c4d.NBIT_SOLO_LAYER, c4d.NBITCONTROL_CLEAR)
layer.Remove()
path_file = aec_file(extract_data)
self.log.info("AEC extracted to {}".format(path_file))
If there is other solutions for exporting a .aec file with python I would like to know as well, please!
Let me know if you need more information!
Thank you all in advance!
Andre