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