I made this gradient rig for a work project that I’m currently working with. With this Xpresso rig you can blend between two different gradients. You have to use same count of knots with both gradients, otherwise rig won’t work. There is options to restrict mixing to work only with knot position or color. I also added spline user data to remap gradient.

import c4d
from c4d import utils as u

def main():
    global GradOut # Output gradient
    GradCnt = Grad1.GetKnotCount() # Get start grad knot count
    knotListA = [] # List for storing knots
    knotListB = [] # List for storing knots
    resultList = [] # List for final grad
    for i in range(GradCnt): # Iterate through knots
        knotListA.append(Grad1.GetKnot(i)) # Store knot data
        knotListB.append(Grad2.GetKnot(i)) # Store knot data
    for i in range(GradCnt): # Loop
        if MPos: # If 'Mix Position' is enabled
            mixPos = u.MixNum(knotListA[i]['pos'],knotListB[i]['pos'], Mix) # Mix positions
        else: # Otherwise
            mixPos = knotListA[i]['pos'] # Take position from start gradient
        if MColor: # If 'Mix Color' is enabled
            mixCol = u.MixVec(knotListA[i]['col'],knotListB[i]['col'], Mix) # Mix colors
        else: # Otherwise
            mixCol = knotListA[i]['col'] # Take color from start gradient
        mixBias = u.MixNum(knotListA[i]['bias'],knotListB[i]['bias'], Mix) # Mix knots bias
        mixBrightness = u.MixNum(knotListA[i]['brightness'],knotListB[i]['brightness'], Mix)
        mixPos = u.RangeMap(mixPos, 0, 1, 0, 1, True, Spline) # Remap position with spline
        resultList.append({ # Collect mixed gradient data
        'brightness': mixBrightness,
        'index': i+1,
        'bias': mixBias,
        'pos': mixPos,
        'col': mixCol
        })
    MixGrad = c4d.Gradient() # Initialize gradient
    for i in range(GradCnt): # Loop
        MixGrad.InsertKnot(resultList[i]['col'],
        resultList[i]['brightness'],
        resultList[i]['pos'],
        resultList[i]['bias'],
        resultList[i]['index'])
    GradOut = MixGrad # Output mixed gradient

mix_gradients.c4d

Asset, Cinema 4D, Python, Xpresso

Transfer animated splines from Cinema 4D to After Effects masks. There are two scripts: the first one is for exporting AI file sequence from Cinema 4D. The second script is for importing AI file sequence and converting it to single solid layer with mask paths in After Effects. Export script uses Cinema 4D’s ‘Sketch and Toon’ module so you need it to run export script. Cinema 4D script is tested with R19 and R20. After Effects script is tested with CC 2018.

Warning 1. The import script is super slow if you have many points. Try to use as minimum point count as you can! Tweak ‘Intermediate Points’ settings in Cinema 4D.

Warning 2. It is recommend that you export only one spline at a time since masks can easily get mixed up. But if that does not matter, then don’t care.

Exporting (C4D)

  1. Select spline objects you want to export
  2. Set the preview range to match what frames you want to export
  3. Run the script (c4d-splines-to-ae-masks-part1.py)
  4. Select folder where you want to save AI file sequence

Importing (After Effects)

  1. Run the script (c4d-splines-to-ae-masks-part2.jsx)
  2. Select folder to export AI file sequence
  3. Insert frame rate that you used in Cinema 4D
  4. Wait until the script is done (it may take a long time)

Special thanks to @lasse_lauch for testing.

Updated 04/10/2018
> Added script for After Effects CS6

Updated 06/04/2019
> Added progress status for import script (CC version)

Updated 24/08/2019
> Added export script that creates separated folders for each spline object
> Added import script that imports to shape layers instead of masks

Export
AR_C4DSplinesToAeMasksPart1.py (All splines to one AI-sequence)
AR_C4DSplinesToAeMasksPart1SeparatedFolders.py (Splines to separated AI-sequences)

Import
AR_C4DSplinesToAEMasksPart2(CC).jsx (Solid layer with masks)
AR_C4DSplinesToAEShapesPart2(CC).jsx (Shape layer with shape paths)

After Effects, Cinema 4D, JavaScript, Python

Connect two X-Particles xpEmitters with splines with this Python Generator. You can also tweak amount of connections and spline interpolations.

You can also connect two xpEmitters using xpElektrix by setting Mode to ‘Particle to Particle’.

import c4d, xparticles
from c4d import utils as u

def main():
    connect = c4d.BaseObject(1011010) # Initializ connect object
    connect[c4d.CONNECTOBJECT_WELD] = False # Untick weld checkbox
    emittera = xparticles.ToEmitter(op[c4d.ID_USERDATA,1]) # Emitter A link
    emitterb = xparticles.ToEmitter(op[c4d.ID_USERDATA,4]) # Emitter B link
    connections = op[c4d.ID_USERDATA,2] # Amount of connections
    interp = op[c4d.ID_USERDATA,3] # Iterpolation for spline
    offset = op[c4d.ID_USERDATA,8] # Offset connections
    splines = [] # Initialize list for splines
    pca = emittera.GetParticleCount() # Get Emitter A's particle count
    pcb = emitterb.GetParticleCount() # Get Emitter B's particle count
    if pca > pcb: # If Emitter A's particle count is larger than Emitter B's
        op[c4d.ID_USERDATA,6] = str(pcb) # Maximum amount of connections
        tpc = pcb # Particle count to use
    else: # Otherwise
        op[c4d.ID_USERDATA,6] = str(pca) # Maximum amount of connections
        tpc = pca # Particle count to use
    try: # Try to execute following script
        for i in xrange(connections): # Loop through connections count
            points = [] # Initialize list for points
            if op[c4d.ID_USERDATA,10] == True: # If modulo checkbox is ticked
                offset = offset%tpc # Offset with modulo
            pa = emittera.GetParticle(i+offset) # Get Emitter A's particle index
            pap = pa.GetPosition() # Get Emitter A's particle's position
            pb = emitterb.GetParticle(i+offset) # Get Emitter B's particle index
            pbp = pb.GetPosition() # Get Emitter B's particle's position
            for k in xrange(interp): # Loop through interpolation count
                r = u.RangeMap(k, 0, interp, 0, 1, True) # Range map
                pp = u.MixVec(pap, pbp, r) # Get point's final position
                points.append(pp) # Add point to points list
            points.append(pbp) # Add last point
            spline = c4d.SplineObject(interp+1, c4d.SPLINETYPE_LINEAR) # Initialize spline object
            splines.append(spline) # Add spline to splines list
            splines[i].SetName("Spline"+str(i)) # Set name for spline object
            splines[i].SetAllPoints(points) # Set points to spline
            splines[i].InsertUnder(connect) # Insert spline under connect object
    except: # If something went wrong
        pass # Do nothing
    return connect # Return connect object that contains splines

Updated 16/10/2018
> Added offset option

connect_xpemitters.c4d

Asset, Cinema 4D, Python, X-Particles

Controlling TurbulenceFD’s wind and buoyancy direction parameters with default vector input is not very intuitive. I made more artist friendly tool with Xpresso and Python so you can rotate spline object to aim direction for wind and buoyancy.

import c4d
from c4d.utils import SplineHelp

def main():
    global Vector # Output variable
    sh = SplineHelp() # Get a new spline help object
    sh.InitSpline(Spline) # Initialize spline help with object
    Vector = sh.GetNormal(0) # Get a normal vector

tfd_direction_tool.c4d

Asset, Cinema 4D, Python, TurbulenceFD, Xpresso

Here is three simple deletion effectors I made for fun. Perhaps they might be sometimes pretty useful. These effectors hides clones completely, they does not scale them to zero.

Delete by scale effector

Hides clones (visibility) based on clone’s scale parameter (not size). This helps to get rid off too tiny or too big clones when you are randomizing clones’ scale with e.g. random effector.

delete_by_scale_effector.c4d

Delete random effector

Hides clones randomly. You can change seed to randomize which clones are deleted, change amount of deleted clones and invert the final output.

delete_random_effector.c4d

Delete by index effector

Hides clones by given index number or range. Indexes are separated with commas (,) and ranges are pointed with dashes (-). You can also invert the final output.

delete_by_index_effector.c4d

Updated 24/04/2019:
> Effectors now supports already hidden clones
> Fixed ‘Delete by scale Effector’ does not calculate only x scale but scale vector average

Cinema 4D, Effector, MoGraph, Python

This script is for Blackmagic Fusion. With this script you can easily view different versions. You can load newer version, older version, latest version or custom given version. You need to install Python 2.7 64-bit version to use this script with Fusion. The script scans folder so it can jump over versions that are missing (e.g. from v02 to v05).

Place the script to comp folder:
“C:\Users\[USER]\AppData\Roaming\Blackmagic Design\Fusion\Scripts\Comp”

Script uses “_v” as a version delimiter.

Your filepath should be something like this:
“..\Versions\MyProject_v001\Passes\MyProject_v001_diffuselighting_####.exr”
So next version path looks like this:
“..\Versions\MyProject_v002\Passes\MyProject_v002_diffuselighting_####.exr”

Path structure is very important. It should not change.

AR_VersionUp.py

Fusion, Python

This is my first Cinema 4D plug-in. Originally I made it for Xpresso heavy users. This plug-in makes changing node’s color easy. Just select nodes and click what color you want to use. You can also load and save custom color palettes. You can also colorise objects and add objects to layers. Plug-in works also with Redshift shader graph editor’s nodes.

When you are chaning Xpresso node colors, Xpresso tag has to be selected, otherwise plug-in wont colorise nodes. And when you are changing Redshift nodes, the Redshift material has to be selected. Also make sure that you have enabled these in the options (File > Options)!

Installation:
Place Colorise plug-in to your appdata plug-in folder to make it work properly (avoid permission errors), since plug-in has to write and read presets and options files.

Win: “C:\Users\[USER]\AppData\Roaming\MAXON\Cinema 4D R[VERSION]\plugins\”
Mac: “/Users/[USER]/Library/Preferences/MAXON/CINEMA 4D R[VERSION]/plugins”

Commands: (Keymofidier, Button)
Alt + [Color]: Select items with this color
Alt + Shift + [Color]: Add to selection items with this color
Alt + Ctrl + [Color]: Remove from selection items with this color
Ctrl + [Color]: Group selected objects under null and assign layer for selected objects
Shift + [Color]: Set objects layer to this colored layer
Shift + [X]: Remove selected objects’ layer
Alt + [X]: Sequence colors to selected items
Ctrl + [X]: Random colors to selected items

Thanks to @BachtellDesign and @_james_owen_ for new feature ideas.

N.B. In R20 Maxon added color picker next to the color field. It is not possible to get rid of. That’s why horizontal mode is disabled.

Updated 08/09/2019 (v0.5)
> R21 version – completely rewritten plug-in with new features and bug fixes

Updated 11/09/2019 (v0.5.1)
> Added option to colorise lights
> Improved Redshift node handling

Updated 12/10/2019 (v0.5.2)
> Added support to make polygon selections
> Fixed Redshift node-editor bug

colorise_v0.4.6_r20.zip (Depricated)
colorise_v0.5.2_r21.zip

Cinema 4D, Plug-in, Python, Redshift, Xpresso

This is simple Python Effector that piles clones top of each other. It supports random effectors scale y parameter. It uses clone’s original object’s bounding box to get correct height. It needs primitive or polygon object to work. You should use it only with cloner. Pile Up Effector does not support rotation.

Updated 19/04/2018
>
Now it works if object is scaled in coordinates tab.

Updated 02/05/2019
> Now you can change axis.

pile_up_effector.c4d

Cinema 4D, Effector, MoGraph, Python

Take Control Effector takes control over selected clone. Select clone ID and then clone is linked to the Effector. When you move the Effector, clone moves with it in Effector’s coordinate space.

Besides changing PSR, you can also modify clone (works only with Cloner object), set custom clone weight, or change clone color. Take Control Effector works properly with Cloner object when ‘Fix Clone’ checkbox is ticked. There is also checkboxes to activate/deactivate position, scale and rotation linking in ‘Matrix’ group.

There is handy ‘Go To Origin’ button that moves effector to clone’s original position (before this effector is calculated).

Warning! There is a bug in C4D (at least in R20.059) with Fracture Object. You can fix this putting Plain Effector before Take Control Effector to Fracture Object and then adjusting ‘Weight Transform’ parameter (just drag it around). After that you can delete Plain Effector.

Updated 26/04/2019
> Complete rebuild

Updated 30/05/2019
> Changed ‘Visible’ option

take_control_effector.c4d

Cinema 4D, Effector, MoGraph, Python

I’m currently working with one project where I mess with OBJ sequences (recorded with Brekel Pro PointCloud), so I created a script that loads OBJ files from a folder and a Python Tag that plays through objects making them visible and invisible depending the current frame.

Import obj folder

import c4d, os
from c4d import storage as s
def main():
    extensions = ["obj"] # File extensions that will be imported
    folder = s.LoadDialog(c4d.FILESELECTTYPE_ANYTHING,'Select folder to import',c4d.FILESELECT_DIRECTORY,'') # Load folder
    if not folder: return # If there is no folder, stop the script
    files = os.listdir(folder) # Get files
    for f in files: # Loop through files
        ext = f.rsplit(".",1) # Get file extension
        if ext[1] in extensions: # If extension matches
            c4d.documents.MergeDocument(doc, folder+'\\'+f, 1) # Merge file to current project
    c4d.EventAdd() # Update Cinema 4D
if __name__=='__main__':
    main()

OBJ Sequence Player

This is Python Tag with some custom constrols on it. You can change starting frame, reverse sequence and animate manually with custom mode. Controls are in Python Tag.

import c4d

def main():
    frame = doc[c4d.DOCUMENT_TIME].GetFrame(doc[c4d.DOCUMENT_FPS]) # Get current frame
    obj = op.GetObject() # Get object
    children = obj.GetChildren() # Get children
    index = 0 # Initialize index variable
    run = 0 # Initialize run variable

    rev = op[c4d.ID_USERDATA,2] # User data: Reverse sequence
    start = op[c4d.ID_USERDATA,3] # User data: Starting frame
    holdf = op[c4d.ID_USERDATA,4] # User data: Hold first frame
    holdl = op[c4d.ID_USERDATA,5] # User data: Hold last frame
    manon = op[c4d.ID_USERDATA,7] # User data: Activate manual mode
    manfr = op[c4d.ID_USERDATA,8] # User data: Set manual frame

    if manon == False: # If manual mode is off
        if rev == False: # If reverse mode is off
            for child in children: # Loop through children
                if frame == index+start: # If index maches with current frame
                    child[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 2 # Default
                    child[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 2 # Default
                    run = 1 # Sequence is running
                else: # Otherwise
                    child[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 1 # Off
                    child[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 1 # Off
                index = index + 1 # Increase index
        else: # Otherwise
            for child in reversed(children): # Loop through reversed children
                if frame == index+start:
                    child[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 2
                    child[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 2
                    run = 1
                else: # Otherwise
                    child[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 1
                    child[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 1
                index = index + 1

        if holdf == True and run == 0: # If hold start frame is on
            if frame < len(children)+start: if rev == True: children[-1][c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 2 children[-1][c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 2 else: children[0][c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 2 children[0][c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 2 if holdl == True and run == 0: # If hold last frame is on if frame >= len(children)+start:
                if rev == True:
                    children[0][c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 2
                    children[0][c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 2
                else:
                    children[-1][c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 2
                    children[-1][c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 2
    else: # Manual mode is on
        for i, child in enumerate(children): # Loop through children
            if i == manfr: # If index is same as custom frame
                children[manfr][c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 2
                children[manfr][c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 2
            else: # Otherwise
                child[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 1
                child[c4d.ID_BASEOBJECT_VISIBILITY_EDITOR] = 1

Updated 31/03/2019

obj_sequence_player.c4d

Asset, Cinema 4D, Python