def main():
    global OutputObject
    global runcount
    global min_Frame
    global max_Frame

    op.SetName("--- OBJ Sequence Reader by George Adamon ---")

    SetupUserData()

    OutputObject = c4d.BaseObject(c4d.Onull)

    multifile = ud.GetUserDataValue(op, "Load All")
    if not multifile:
        currentPath = ResolveFilename(
            ud.GetUserDataValue(op, "First Object FileName"))
        ImportToCinema(currentPath)
    else:
        for i in range(min_Frame, max_Frame):
            currentPath = ResolveFilename(
                ud.GetUserDataValue(op, "First Object FileName"), i)
            ImportToCinema(currentPath)

    print str.format("The generator has run {0} times.", runcount)
    return OutputObject
def ReadUserData():
    global fileContainsHeaders
    global filePath
    global fileName
    global isQuad
    global size
    global referencedCloner

    fileContainsHeaders = ud.GetUserDataValue(op, "Data Has Headers")
    filePath = ud.GetUserDataValue(op, "Transformation Matrices File")
    fileName = os.path.basename(filePath).split(".")[0]
    isQuad = ud.GetUserDataValue(op, "Display Matrices As") == 0
    size = ud.GetUserDataValue(op, "Display Size")
    referencedCloner = ud.GetUserDataValue(op, "Attach to Cloner")
def ConstructFrame(Frame=-1):
    """
        Accepts a provided FileName pointing to the first object of the sequence to import, and figures out the apporppriate filename to load based on the current animation 
        frame.

        Args:
            filename: The filename of the first object in the OBJ sequence
        Returns:
            Path: the filename of the actual object to load.
    """
    global min_Frame
    global max_Frame
    global digitCount

    if Frame <= -1:
        first = max(ud.GetUserDataValue(op, "First Frame"), min_Frame)
        last = min(ud.GetUserDataValue(op, "Last Frame"), max_Frame)

        ud.SetUserDataValue(op, "First Frame", first)
        ud.SetUserDataValue(op, "Last Frame", last)

        step = ud.GetUserDataValue(op, "Frame Step")
        offset = ud.GetUserDataValue(op, "Frame Offset")
        after = ud.GetUserDataValue(op, "After Last Frame")

        frame = doc.GetTime().GetFrame(doc.GetFps()) + offset

        if frame % step != 0:
            frame = frame - (frame % step)  #Frame skipping

        if after == 0:
            if last <= first:
                frame = max(frame, first)  #open ended sequence, no max limit
            else:
                frame = max(min(frame, last),
                            first)  # Min and Max limit applied
        elif after == 1:
            frame = frame % last

        elif after == 2:
            if (frame // last) % 2 == 0:
                frame = frame % last
            else:
                frame = last - (frame % last)
        elif after == 3:
            if frame > last or frame < first:
                return -1
    else:
        frame = Frame

    formatted_frame = str(frame).zfill(
        digitCount)  # Fill number with zeros if necessary

    return formatted_frame
def SetupUserData():
    #1.1: UI Header
    if not ud.UserDataExists(op, "File Handling"):
        ud.CreateUserData(op, "File Handling", c4d.DTYPE_SEPARATOR)

    #1.2: "Filename" text box
    if not ud.UserDataExists(op, "Transformation Matrices File"):
        ud.CreateUserData(op, "Transformation Matrices File",
                          c4d.DTYPE_FILENAME)

    #1.3
    if not ud.UserDataExists(op, "Data Has Headers"):
        ud.CreateUserData(op, "Data Has Headers", c4d.DTYPE_BOOL)

    #2.1: UI Header
    if not ud.UserDataExists(op, "Display"):
        ud.CreateUserData(op, "Display", c4d.DTYPE_SEPARATOR)

    #2.2: "Object Type" text box
    if not ud.UserDataExists(op, "Display Matrices As"):
        ud.CreateDropDown(op, "Display Matrices As", c4d.CUSTOMGUI_CYCLE,
                          ["Quad", "Triangle"])

    #2.3: "Display Size" slider
    if not ud.UserDataExists(op, "Display Size"):
        ud.CreateFloatData(op, "Display Size", "Float Slider", 0, 100, 0.01,
                           "Meters")

    #3.1: UI Header
    if not ud.UserDataExists(op, "Pipeline"):
        ud.CreateUserData(op, "Pipeline", c4d.DTYPE_SEPARATOR)

    #3.2: "Filename" text box
    if not ud.UserDataExists(op, "Attach to Cloner"):
        ud.CreateUserData(op, "Attach to Cloner", c4d.DTYPE_BASELISTLINK)
def ImportToCinema(PATH):
    #global Polygon
    global OutputObject
    global runcount
    global prev_filename

    #SetupUserData()

    #PATH =  ResolveFilename(ud.GetUserDataValue(op, "First Object FileName"))

    if PATH != None and PATH != -1:

        N = os.path.basename(PATH)

        FaceUV = []
        # LOAD OBJ FILE
        Vertices,       \
        Faces,          \
        Colors,         \
        VertexNormals,  \
        VertexUV,       \
        FaceNormals,    \
        FaceUV = OBJ.ParseObj(PATH,
                              ud.GetUserDataValue(op, "Swap Y/Z"),
                              ud.GetUserDataValue(op, "Flip Z"),
                              ud.GetUserDataValue(op, "Scale"))

        ud.SetUserDataValue(op, "Vertices", str(len(Vertices)))
        ud.SetUserDataValue(op, "Colors", str(len(Colors)))
        ud.SetUserDataValue(op, "Faces", str(len(Faces)))

        Polygon = c4d.BaseObject(c4d.Opolygon)
        Polygon.ResizeObject(
            len(Vertices),
            len(Faces))  #New number of points, New number of polygons
        Polygon.SetName(N)

        UpdateVertexCoordinates(Polygon, Vertices)
        UpdateFaces(Polygon, Faces)

        if len(FaceUV) > 0:
            UpdateUV(Polygon, FaceUV, VertexUV)

        if ud.GetUserDataValue(op, "Import Vertex Colors"):
            UpdateVertexColors(Polygon, Colors)
            if ud.GetUserDataValue(op, "Create Test Material"):
                UpdateMaterial(Polygon)

        if ud.GetUserDataValue(op, "Phong Smoothing"):
            Polygon.CreatePhongNormals()
            Polygon.SetPhong(True, True, 90)
        else:
            Polygon.SetPhong(False, True, 90)

        if len(Vertices) == 0 and len(Faces) == 0:
            msg = ">>> Mesh Succesfully Loaded, but has no Vertices, neither Faces. It's OK, though."
            ud.SetUserDataValue(op, "Mesh Handling Message", msg)
        elif len(Vertices) == 0:
            msg = ">>> Mesh Succesfully Loaded, but has no Vertices.  It's OK, though."
            ud.SetUserDataValue(op, "Mesh Handling Message", msg)
        elif len(Faces) == 0:
            msg = ">>> Mesh Succesfully Loaded, but has no Faces.  It's OK, though."
            ud.SetUserDataValue(op, "Mesh Handling Message", msg)
        else:
            msg = ">>> Mesh Succesfully Loaded! It should appear in the Viewport."
            ud.SetUserDataValue(op, "Mesh Handling Message", msg)

        runcount += 1
        prev_filename = ud.GetUserDataValue(op, "First Object FileName")

        Polygon.InsertUnder(OutputObject)

        return
    elif PATH == -1:
        msg = "Outside of Frame Range."
        ud.SetUserDataValue(op, "Mesh Handling Message", msg)
        return
    else:
        msg = "No Mesh Loaded, because of faulty Filename. Check the File Handling Message above."
        ud.SetUserDataValue(op, "Mesh Handling Message", msg)
        return
def SetupUserData():
    """
    Setup the UI of the Python Generator object
    """
    # ------------------------------------------------------------------------------------------------------------------
    # 1 - FILE HANDLING UI ---------------------------------------------------------------------------------------------
    # ------------------------------------------------------------------------------------------------------------------

    # 1.1: UI Header
    if not ud.UserDataExists(op, "File Handling"):
        ud.CreateUserData(op, "File Handling", c4d.DTYPE_SEPARATOR)
    # 1.2: "Filename" text box
    if not ud.UserDataExists(op, "First Object FileName"):
        ud.CreateUserData(op, "First Object FileName", c4d.DTYPE_FILENAME,
                          True)
    # 1.3: "Load All" Boolean, determines whether to load all files in the folder at once
    if not ud.UserDataExists(op, "Load All"):
        ud.CreateUserData(op, "Load All", c4d.DTYPE_BOOL)
    # 1.4
    if not ud.UserDataExists(op, "First Frame"):
        ud.CreateIntegerData(op, "First Frame", "Integer")
    # 1.5
    if not ud.UserDataExists(op, "Last Frame"):
        ud.CreateIntegerData(op, "Last Frame", "Integer")
    # 1.6
    if not ud.UserDataExists(op, "Frame Step"):
        ud.CreateIntegerData(op, "Frame Step", "Integer Slider")
        ud.SetUserDataValue(op, "Frame Step", 1)
    # 1.7
    if not ud.UserDataExists(op, "Frame Offset"):
        ud.CreateIntegerData(op, "Frame Offset", "Integer Slider")
        ud.SetUserDataValue(op, "Frame Offset", 0)
    # 1.8
    if not ud.UserDataExists(op, "After Last Frame"):
        ud.CreateDropDown(op, "After Last Frame", "Cycle",
                          ["Freeze", "Loop", "Ping Pong", "Disappear"])
        ud.SetUserDataValue(op, "After Last Frame", 0)
    # 1.9
    if not ud.UserDataExists(op, "File Handling Message"):
        ud.CreateUserData(op, "File Handling Message", c4d.DTYPE_STATICTEXT,
                          True)

    # ------------------------------------------------------------------------------------------------------------------
    # 2 - MESH HANDLING UI ---------------------------------------------------------------------------------------------
    # ------------------------------------------------------------------------------------------------------------------

    # 2.1
    if not ud.UserDataExists(op, "Mesh Handling"):
        ud.CreateUserData(op, "Mesh Handling", c4d.DTYPE_SEPARATOR)
    # 2.2
    if not ud.UserDataExists(op, "Phong Smoothing"):
        ud.CreateUserData(op, "Phong Smoothing", c4d.DTYPE_BOOL)
    # 2.3
    if not ud.UserDataExists(op, "Import Vertex Colors"):
        ud.CreateUserData(op, "Import Vertex Colors", c4d.DTYPE_BOOL)
    # 2.4
    if not ud.UserDataExists(op, "Create Test Material"):
        ud.CreateUserData(op, "Create Test Material", c4d.DTYPE_BOOL)
    # 2.5
    if not ud.UserDataExists(op, "Mesh Handling Message"):
        ud.CreateUserData(op, "Mesh Handling Message", c4d.DTYPE_STATICTEXT,
                          True)
    # 2.6
    if not ud.UserDataExists(op, "Vertices"):
        ud.CreateUserData(op, "Vertices", c4d.DTYPE_STATICTEXT, True)
    # 2.7
    if not ud.UserDataExists(op, "Colors"):
        ud.CreateUserData(op, "Colors", c4d.DTYPE_STATICTEXT, True)
    # 2.8
    if not ud.UserDataExists(op, "Faces"):
        ud.CreateUserData(op, "Faces", c4d.DTYPE_STATICTEXT, True)

    # ------------------------------------------------------------------------------------------------------------------
    # 3 - COORDINATE HANDLING UI ---------------------------------------------------------------------------------------
    # ------------------------------------------------------------------------------------------------------------------
    # 3.1
    if not ud.UserDataExists(op, "Coordinate Handling"):
        ud.CreateUserData(op, "Coordinate Handling", c4d.DTYPE_SEPARATOR)
    # 3.2
    if not ud.UserDataExists(op, "Swap Y/Z"):
        ud.CreateUserData(op, "Swap Y/Z", c4d.DTYPE_BOOL)
    # 3.3
    if not ud.UserDataExists(op, "Flip Z"):
        ud.CreateUserData(op, "Flip Z", c4d.DTYPE_BOOL)
    # 3.4
    if not ud.UserDataExists(op, "Scale"):
        ud.CreateFloatData(op, "Scale", "Float Slider", 0.0, 1000.0, 0.1)
        ud.SetUserDataValue(op, "Scale", 1)
def ResolveFilename(filename, Frame=-1):
    """
    Accepts a provided FileName pointng to the first object of the sequence to import, and figures out the apporppriate filename to load based on the current animation 
    frame.

    Args:
        filename: The filename of the first object in the OBJ sequence
    Returns:
            Path: the filename of the actual object to load.
    """
    global runcount
    global prev_filename
    global min_Frame
    global max_Frame
    global digitCount

    # ------------------------------------------------------------------------------------------------------------------
    # FILE ERROR HANDLING ----------------------------------------------------------------------------------------------
    # ------------------------------------------------------------------------------------------------------------------

    if not filename:
        msg = ">>> Empty Filename. Please provide a link to the FIRST item of the sequence you want to import"
        ud.SetUserDataValue(op, "File Handling Message", msg)
        return

    if not exists(filename):
        msg = ">>> Invalid FileName. Please provide a link to an existing file or folder."
        ud.SetUserDataValue(op, "File Handling Message", msg)
        return

    if not isfile(filename):
        msg = ">>> Invalid FileType. Please provide a link to an .obj file, not to a folder."
        ud.SetUserDataValue(op, "File Handling Message", msg)
        return

    if not splitext(filename)[1] == ".obj":
        msg = ">>> Invalid FileType. Please provide an .obj file."
        ud.SetUserDataValue(op, "File Handling Message", msg)
        return

    directory, fullname, name, extension = SplitPath(filename)
    parts = filter(None, re.split(
        r'(\d+)', name))  # Split the digit and non-digit parts of the name

    if parts[-1].isdigit():  #if the last part is digit, use it
        digit = parts[-1]
    else:
        msg = ">>> Invalid Naming Convention. Please make sure that no other characters exist after your file numbering."
        ud.SetUserDataValue(op, "File Handling Message", msg)
        return

    # ------------------------------------------------------------------------------------------------------------------
    # RESET RUN COUNT ON FILE CHANGE -----------------------------------------------------------------------------------
    # ------------------------------------------------------------------------------------------------------------------
    if not runcount == 0:
        if not prev_filename == filename:
            runcount = 0

    if len(parts[:-1]) > 1:
        nonDigitName = " ".join(parts[:-1]).replace(" ", "")
    else:
        nonDigitName = parts[0]

    if runcount == 0:
        files = GetFiles(directory, ".obj")

        _digit = 0

        for f in files:

            _d, _f, _n, _e = SplitPath(f)

            _parts = filter(None, re.split(
                r'(\d+)',
                _n))  # Split the digit and non-digit parts of the name

            if len(_parts[:-1]) > 1:
                _nonDigitName = " ".join(_parts[:-1]).replace(" ", "")
            else:
                _nonDigitName = _parts[0]

            if _parts[-1].isdigit():  #if the last part is digit, use it
                _digit = _parts[-1]

            if _nonDigitName == nonDigitName:
                if int(_digit) < min_Frame:
                    min_Frame = int(digit)
                    digitCount = len(_digit)

                if int(_digit) >= max_Frame:
                    max_Frame = int(_digit)

        ud.SetUserDataValue(op, "First Frame", int(min_Frame))
        ud.SetUserDataValue(op, "Last Frame", int(max_Frame))

    if Frame <= -1:
        formatted_frame = ConstructFrame()
    else:
        formatted_frame = ConstructFrame(Frame)

    if formatted_frame != -1:
        Path = os.path.join(directory,
                            nonDigitName + formatted_frame + extension)
        msg = ">>> File: ' " + nonDigitName + formatted_frame + extension + " ' succesfully located."
        ud.SetUserDataValue(op, "File Handling Message", msg)
        return Path
    else:
        return -1
def ResolveFilename(filename):
    """
    Accepts a provided FileName pointng to the first object of the sequence to import, and figures out the apporppriate filename to load based on the current animation 
    frame.

    Args:
        filename: The filename of the first object in the OBJ sequence
    Returns:
            Path: the filename of the actual object to load.
    """

    first = ud.GetUserDataValue(op.GetObject(), "First Frame")
    last = ud.GetUserDataValue(op.GetObject(), "Last Frame")
    step = ud.GetUserDataValue(op.GetObject(), "Frame Step")

    frame = doc.GetTime().GetFrame(doc.GetFps())

    if last <= first:
        frame = max(frame,
                    first)  #op.GetObject()en ended sequence, no max limit
    else:
        frame = max(min(frame, last), first)  # Min and Max limit applied

    if frame % step != 0:
        frame = frame - (frame % step)  #Frame skipping

    if filename == None or filename == "":
        msg = ">>> Empty Filename. Please provide a link to the FIRST item of the sequence you want to import"
        ud.SetUserData(op.GetObject(), "File Handling Message", msg)
        return

    if os.path.exists(filename) == False:
        msg = ">>> Invalid FileName. Please provide a link to an existing file or folder."
        ud.SetUserData(op.GetObject(), "File Handling Message", msg)
        return

    if os.path.isfile(filename):
        directory = os.path.dirname(filename)  #Directory of the file
        fullname = os.path.basename(
            filename)  #Fullname of the file, including extension
        name, extension = os.path.splitext(
            fullname)  #Split the actual name from the extension

        if extension != ".obj":
            msg = ">>> Invalid FileType. Please provide an .obj file."
            ud.SetUserData(op.GetObject(), "File Handling Message", msg)
            return

        parts = filter(None, re.split(
            r'(\d+)', name))  # Split the digit and non-digit parts of the name

        if parts[-1].isdigit():  #if the last part is digit, use it
            digit = parts[-1]
        else:
            msg = ">>> Invalid Naming Convention. Please make sure that no other characters exist after your file numbering."
            ud.SetUserData(op.GetObject(), "File Handling Message", msg)
            return

        formatted_frame = str(frame).zfill(
            len(digit))  # Fill number with zeros if necessary

        if len(parts[:-1]) > 1:
            nonDigitName = " ".join(parts[:-1]).replace(" ", "")
        else:
            nonDigitName = parts[0]

        Path = os.path.join(directory,
                            nonDigitName + formatted_frame + extension)

        msg = ">>> File: ' " + nonDigitName + formatted_frame + extension + " ' succesfully located."
        ud.SetUserData(op.GetObject(), "File Handling Message", msg)
        return Path

    else:
        msg = ">>> Invalid FileType. Please provide a link to an .obj file, not to a folder."
        ud.SetUserData(op.GetObject(), "File Handling Message", msg)
        return
def ImportToCinema():
    global Polygon
    SetupUserData()

    PATH = ResolveFilename(
        ud.GetUserDataValue(op.GetObject(), "First Object FileName"))

    N = os.path.basename(PATH)

    if PATH != None:
        Vertices, Faces, Colors = ParseObj(PATH,
                                           ud.GetUserDataValue(
                                               op.GetObject(),
                                               "Swap Y/Z"))  #LOAD OBJ FILE

        children = op.GetObject().GetChildren()

        if len(children) == 0:
            Polygon = c4d.BaseObject(
                c4d.Opolygon)  #Create an empty polygon object
            doc.InsertObject(Polygon, op.GetObject(), None)
        else:
            Polygon = children[0]
            Polygon.ResizeObject(0, 0)

        Polygon.ResizeObject(
            len(Vertices),
            len(Faces))  #New number of points, New number of polygons
        Polygon.SetName(N)

        UpdateVertexCoordinates(Vertices)
        UpdateFaces(Faces)

        if ud.GetUserDataValue(op.GetObject(), "Import Vertex Colors"):
            UpdateVertexColors(Colors)
            if ud.GetUserDataValue(op.GetObject(), "Create Test Material"):
                UpdateMaterial()

        if ud.GetUserDataValue(op.GetObject(), "Phong Smoothing"):
            Polygon.CreatePhongNormals()
            Polygon.SetPhong(True, True, 90)
        else:
            Polygon.SetPhong(False, True, 90)

        if len(Vertices) == 0 and len(Faces) == 0:
            msg = ">>> Mesh Succesfully Loaded, but has no Vertices, neither Faces. It's OK, though."
            ud.SetUserData(op.GetObject(), "Mesh Handling Message", msg)
        elif len(Vertices) == 0:
            msg = ">>> Mesh Succesfully Loaded, but has no Vertices.  It's OK, though."
            ud.SetUserData(op.GetObject(), "Mesh Handling Message", msg)
        elif len(Faces) == 0:
            msg = ">>> Mesh Succesfully Loaded, but has no Faces.  It's OK, though."
            ud.SetUserData(op.GetObject(), "Mesh Handling Message", msg)
        else:
            msg = ">>> Mesh Succesfully Loaded! It should appear in the Viewport."
            ud.SetUserData(op.GetObject(), "Mesh Handling Message", msg)

        return Polygon
    else:
        msg = "No Mesh Loaded, because of faulty Filename. Check the File Handling Message above."
        ud.SetUserData(op.GetObject(), "Mesh Handling Message", msg)
        return
def SetupUserData():
    #1
    if not ud.UserDataExists(op.GetObject(), "File Handling"):
        ud.CreateUserData(op.GetObject(), "File Handling", c4d.DTYPE_SEPARATOR,
                          True)
    #2
    if not ud.UserDataExists(op.GetObject(), "First Object FileName"):
        ud.CreateUserData(op.GetObject(), "First Object FileName",
                          c4d.DTYPE_FILENAME, True)
    #3
    if not ud.UserDataExists(op.GetObject(), "First Frame"):
        ud.CreateIntegerData(op.GetObject(), "First Frame", "Integer", 0, 100,
                             1)
    #4
    if not ud.UserDataExists(op.GetObject(), "Last Frame"):
        ud.CreateIntegerData(op.GetObject(), "Last Frame", "Integer", 0, 100,
                             1)
    #5
    if not ud.UserDataExists(op.GetObject(), "Frame Step"):
        ud.CreateIntegerData(op.GetObject(), "Frame Step", "Integer Slider", 1,
                             100, 1)
        ud.SetUserData(op.GetObject(), "Frame Step", 1)
    #6
    if not ud.UserDataExists(op.GetObject(), "File Handling Message"):
        ud.CreateUserData(op.GetObject(), "File Handling Message",
                          c4d.DTYPE_STATICTEXT, True)
    #7
    if not ud.UserDataExists(op.GetObject(), "Mesh Handling"):
        ud.CreateUserData(op.GetObject(), "Mesh Handling", c4d.DTYPE_SEPARATOR,
                          True)
    #8
    if not ud.UserDataExists(op.GetObject(), "Swap Y/Z"):
        ud.CreateUserData(op.GetObject(), "Swap Y/Z", c4d.DTYPE_BOOL, True)
    #9
    if not ud.UserDataExists(op.GetObject(), "Phong Smoothing"):
        ud.CreateUserData(op.GetObject(), "Phong Smoothing", c4d.DTYPE_BOOL,
                          True)
    #10
    if not ud.UserDataExists(op.GetObject(), "Import Vertex Colors"):
        ud.CreateUserData(op.GetObject(), "Import Vertex Colors",
                          c4d.DTYPE_BOOL, True)
    #11
    if not ud.UserDataExists(op.GetObject(), "Create Test Material"):
        ud.CreateUserData(op.GetObject(), "Create Test Material",
                          c4d.DTYPE_BOOL, True)
    #12
    if not ud.UserDataExists(op.GetObject(), "Mesh Handling Message"):
        ud.CreateUserData(op.GetObject(), "Mesh Handling Message",
                          c4d.DTYPE_STATICTEXT, True)