This is a Python Tag script that uses a basic Null object to make a nice object manager separator. Change separator’s name in Null object’s User Data and select style and width as you wish.
The script is created to work with Cinema 4D’s default UI font: Segoe UI, Regular 11. Since the font is not a monospace typeface, I had to go through each letter, number and symbol and gave them a custom weight. Weighting is not perfect but it is better than nothing.
Here is two Python Tags that toggles object’s visibility by given frames.
Toggle Visibility
This Python Tag toggles it’s object’s visibility. You can input individual frame numbers or frame ranges. Use a comma to separate different selections. Frame numbers that you put in to the input field are “selected frames”. You can change operation what happens to selected frames or non-selected frames.
This one works like the previous one but instead controlling only one object, you can control multiple objects’ visibility. Add more items with “Add” button. You can add as many item as you want. “Remove” button removes always the last “Toggle Visibility” group.
This is an old script that I made, but I wanted to wrote something about it before I forget everything. The script exports selected layers masks from Adobe After Effects to text file that you can copy paste straight to Black Magic Fusion composition. The script exports only the paths, it does not support feather, opacity, expansion or other fancy things.
I created this script because sometimes I have to rotoscope something and when that happens I only use free version of Mocha that comes bundled with After Effects. But since I have started to use Fusion more and more I had to figure a way to get my rotoscoped Mocha shapes to Fusion. Fusion’s own rotoscope tools are fine, but I’m so much faster with Mocha.
After Effects and Fusion have a different coordinate systems (Fusion has many different coordinate systems, this is for polygon masks).
Following pseudocode shows how mask vertices are remapped from another coordinate system to another:
// lerp(value, old min, old max, new min, new max)
x = lerp(vertices[n].x, 0, compositionWidth, -0.5, 0.5)
y = lerp(vertices[n].y, 0, compositionHeight, 0.5, -0.5)
Tangents are remapped similarly, but by trial and error I found out that I need to additionally add 0.5 to X-tangents and subtract 0.5 from Y-tangents to get right result.
This was a really quick JavaScript project that I made when I was a bit bored. I wrote it originally with Python, but I wanted to port it to JavaScript, so it is easy to use in browser or something.
It is render time estimator. Just type frames or frame range and average time how much it takes to render a single frame and then render time estimator calculates how long it takes to render whole sequence.
Time should be given in HH:mm:ss format.
Updated 17/11/2021
> Added support for multiple render times, separate values with comma and estimated render time is average of those values.
> Added history.
This is my first Python Field Object. With Selector Field, you can easily select items by typing their ID’s (e.g. 0, 10, 11, 25, 30 and so on). Selector Field supports also range selections which works by typing start ID and end ID e.g. 5-20, 49-99, 0-3 and so on.
Originally Selector Field is intended to use with MoGraph Effectors but it works also with polygon, edge and point selections and vertex maps.
There is three different ‘Input type’ systems: Single-Line, Multi-Line and Range Sliders. First two allows you to type ID’s and the last one allows you to select items with two sliders.
If you want to invert the selection, go to ‘Remapping’ tab and tick ‘Invert’ checkbox there.
Selector Field won’t update user interface if it does not have anything to process! You should first attach it to somewhere and then modify settings.
Updated 24/06/2020
> Updated – now it supports more than 400 items. N.B. large selections gets really slow! (Bad optimization)
Updated 17/09/2022
> Updated support for Cinema 4D R25 and newer (Python 3)
Lately I have worked a lot with alembic files that have point level animated geometry and once I had to attach objects to that PLA geometry. I figured out a couple different ways to do this (e.g. MoGraph Cloner or Contraint Tag) but my favorite solution is to use Xpresso and Python.
In this setup I’m taking three points from selected polygon. Then I’m creating vectors v1 and v2 from those points’ positions. Then vectors are used to generate orthogonal axes by using cross product.
import c4d
import math
def main():
global Matrix
v1 = Point1 - Point0 # Generate first vector from two point positions
v2 = Point2 - Point0 # Generate second vector from two point positions
m = c4d.Matrix() # Initialize a matrix
m.v1 = v1.GetNormalized()
m.v3 = v1.Cross(v2).GetNormalized()
m.v2 = m.v3.Cross(v1).GetNormalized()
m.off = Position # Set position
Matrix = m # Output
This was a funny experiment. Sending MoGraph data from Cinema 4D to Novation Launchpad (the first version). I used Python Effector to send UDP packets to Processing and in Processing I used UDP library to read those packets and The MidiBus library to send MIDI messages to Launchpad.
Pads’ indexes starts from 0 and ends to 199. Every circle pad takes 8 indexes so in every row we add that amount to index value. Velocity value changes color and brightness of the pad but I did not have interest to find out what every single values from 0 to 127 did. In my setup I just mapped clone’s red color value (float 0.0-1.0) to MIDI velocity (integer 0-127). Mapping is done in Processing sketch.
Python Effector from this experiment can now be used as a template for different setups to send MoGraph data to other softwares. Data is transfered with UDP packets in strings. So Cinema 4D is sending constantly long strings and Processing sketch is listening those and unpacking those strings to something useful.
Python Effector code
import c4d
import socket
from c4d.modules import mograph as mo
def initSocket():
global s
global server
host = '127.0.0.1' # Host address
port = 5001 # Port
server = ('127.0.0.1', 5006) # Server address and server port
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # Initialize socket
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # Re-use socket
s.bind((host, port)) # Bind
return s, server
initSocket() # Create a socket
def main():
md = mo.GeGetMoData(op) # Get MoData
if md is None: return False # If no MoData return false
cnt = md.GetCount() # Get clone count
carr = md.GetArray(c4d.MODATA_COLOR) # Get color array
message = "" # Initialize message string
row = 0 # Initialize row integer
for i in xrange(0, cnt): # Loop through clones
if i != 0: # If not the first index
if i % 8 == 0:
row = row + 8 # Launchpad pad numbering
color = carr[i] # Get clone's color
index = (i+row) # Calculate pitch
message += str(index) + "-" + str(color.x) + "," # Write message
s.sendto(message, server) # Send message
return True # Everything is fine
Processing sketch
import hypermedia.net.*;
import themidibus.*;
MidiBus myBus;
UDP udp;
String HOST = "127.0.0.1"; //ip address
int PORT = 5006; //port
String receivedFromUDP = "0-0.0,1-0.0,2-0.0,3-0.0,4-0.0,5-0.0,6-0.0,7-0.0,16-0.0,17-0.0,18-0.0,19-0.0,20-0.0,21-0.0,22-0.0,23-0.0,32-0.0,33-0.0,34-0.0,35-0.0,36-0.0,37-0.0,38-0.0,39-0.0,48-0.0,49-0.0,50-0.0,51-0.0,52-0.0,53-0.0,54-0.0,55-0.0,64-0.0,65-0.0,66-0.0,67-0.0,68-0.0,69-0.0,70-0.0,71-0.0,80-0.0,81-0.0,82-0.0,83-0.0,84-0.0,85-0.0,86-0.0,87-0.0,96-0.0,97-0.0,98-0.0,99-0.0,100-0.0,101-0.0,102-0.0,103-0.0,112-0.0,113-0.0,114-0.0,115-0.0,116-0.0,117-0.0,118-0.0,119-0.0,"; // Example message
void setup() {
size(400, 400); // Document size
background(0); // Background color
udp = new UDP(this, PORT, HOST); // Create a new UDP
udp.listen(true); // Listen UDP
//MidiBus.list(); // List all available Midi devices
myBus = new MidiBus(this, 1, "Launchpad"); // Create a new MidiBus
}
void draw() {
String[] list = split(receivedFromUDP, ','); // Create a list from a string
int channel = 0; // MIDI channel
int number = 0;
int value = 90;
for (int i = 0; i < (list.length-1); i+=1) {
String[] values = split(list[i], '-');
int pitch = int(values[0]); // Pitch (pad)
float m = map(float(values[1]), 0, 1, 0, 127); // Map values from 0-1 to 0-127
int velocity = int(m); // Velocity (color)
myBus.sendNoteOn(channel, pitch, velocity); // Set note on
}
myBus.sendControllerChange(channel, number, value); // Send a controller change
}
void receive(byte[] data, String HOST, int PORT) {
String value = new String(data); // Recieved UDP message
receivedFromUDP = value; // Assign message to global string
}
This Python Tag automatically adds effectors to MoGraph generator. Add this Python Tag to MoGraph generator. Put effectors under Null object and link that hierarchy to Python Tag. Now you can quickly add and change order of effectors without opening generator’s “Effectors” tab.
This Python Tag detects if object in the hierarchy is an effector.
When you render with Cinema 4D’s render queue, you get a XML-file where C4D puts useful data, this file is called a render log. It’s not very pleasing to read the raw XML-file, so I made a simple log viewer with HTML and JavaScript that views render logs more readable way.
There is an input field where you can just drag and drop your log-file and when you press “View Log”-button, the site converts XML-file to nice looking HTML code.
The log viewer does not upload or collect any kind of data to my server. It opens file straight in the browser using HTML5’s FileReader, so your log files stays private.
This was a fun little friday experiment. Transfering MoGraph data to Spline Data with Python Tag. I don’t know how useful it is, but it is fun to play with.
import c4d
from c4d import utils as u
from c4d.modules import mograph as mo
def main():
clamp = op[c4d.ID_USERDATA,2] # User Data: 'Clamp'
intrp = op[c4d.ID_USERDATA,3] # User Data: 'Interpolation'
obj = op.GetObject() # Get object
md = mo.GeGetMoData(obj) # Get MoData
if md is None: return False # If there is no MoData, stop processing
cnt = md.GetCount() # Get clone count
marr = md.GetArray(c4d.MODATA_MATRIX) # Get MoData matrix
spline = c4d.SplineData() # Initialize Spline Data
for i in range(0, cnt):
x = u.RangeMap(marr[i].off.x, 0, 100, 0, 1, clamp) # Calculate X position
y = u.RangeMap(marr[i].off.y, 0, 100, 0, 1, clamp) # Calculate Y position
spline.InsertKnot(x, y) # Insert new knot
leftTangent = c4d.Vector(0, 0, 0) # Left tangent values
rightTangent = c4d.Vector(0, 0, 0) # Right tangent values
spline.SetKnot(i, c4d.Vector(x,y,0), 0, False, leftTangent, rightTangent, intrp) # Edit knot
spline.DeleteKnot(spline.GetKnotCount()-1) # Remove some leftovers
spline.DeleteKnot(spline.GetKnotCount()-1) # Yes, do it twice
op[c4d.ID_USERDATA,1] = spline # Set spline data to user data