SOLVED Custom exporting animation data into aec file crashes Cinema

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

hi,

we are missing one information, from where are you executing this code ?
a script ?

Cheers,
Manuel

Hi @m_magalhaes,

Apologies! Pyblish is called from a script inside Cinema and the script above runs after Pyblish opening and going through each stage.

Hope this helps!

Cheers!

Andre

hi,

What come to mind is maybe the crash have nothing to do with that critical stop.
Just in case be sure that you don't have the take dialog tab in a custom layout when you are opening Cinema 4D with a script.

Also your code seem to be only a part of the script you are using (because in that state, it simply does nothing).

We don't have any experience with Pyblish.
Is it possible to recreate that crash without using that library ? (that would simplify a lot our job here)

I would also suggest to ask them. (we can't debug third party libraries)

But try to reproduce the crash without that library just to be sure if the problem is in our side or their side.

Cheers,
Manuel

hi,

I will mark this thread as resolved if you don't have anything to add.

Cheers,
Manuel

Hi Manuel,

Apologies for this, as due to current circumstances I'm inundated in solving other issues.
Can we revisit this please?

Thank you in advance and apologies!

Andre

Sure.

I will wait for another 14 days 🙂

Cheers,
Manuel

Thank you Manuel! 🙂

I appreciate it!

Andre

@m_magalhaes
Hi Manuel,

Thank you for giving me some time to check this out.
So after a bit of testing and changing the code to work with Cinema I cannot replicate the issue. As you mention it seems to be related to Pyblish.

I did find interesting that when running the code in Cinema without Pyblish you can't see the active frame running through the timeline, while you do see it when running through Pyblish. Perhaps that's the correlation with the crash.

I will close this as solve, for the fact that it does not crash in the Cinema context.

Thanks again!

Andre