Data from Processing to Cinema 4D with OSC

I made some experiments with Processing to send data to Cinema 4D with OSC (Open Sound Control) protocol.

Tools you need

  1. Maxon Cinema 4D
  2. fOSC v1.1.1 plug-in for Cinema 4D (https://github.com/fillmember/fOSC)
  3. Processing v3.5.3 (https://processing.org/download/)
  4. oscP5 v0.9.9 library for Processing (http://www.sojamo.de/libraries/oscP5/)
  5. Different Processing libraries depending what you want to do (e.g. Kinect, Leap Motion, The MidiBus)

Processing OSC template
Here is basic Processing example that sends OSC data. In this example sketch generates a window where mouse position is captured and then remapped to more nice values for Cinema 4D (y-axis is flipped and sketch middle point is the origin) and then data is send with OSC.

import oscP5.*; // Import oscP5 library
import netP5.*; // Import oscP5's net library

OscP5 osc; // Declare oscP5 object
NetAddress net; // Declare net address object

void setup() { // Sketch basic settings
  size(640, 640); // Set sketch window size (width, height)
  osc = new OscP5(this, 6449); // Set new OscP5 object
  net = new NetAddress("127.0.0.1", 32000); // Set new net address
}

void draw() { // Processing will run this function constantly
  clear(); // Clear window every frame
  float posX = mouseX; // Get mouse x-position
  float posY = mouseY; // Get mouse y-position
  circle(posX, posY, 25); // Draw circle to mouse position
  float x = map(posX, 0, width, (width/2.0)*-1, width/2.0); // Remap x value
  float y = map(posY, 0, height, height/2.0, (height/2.0)*-1); // Remap y value
  send(x, y); // Run send function
}

void send(float value_a, float value_b) {
    OscMessage message_1 = new OscMessage("Mouse Position"); // Initialize new OSC message
    message_1.add(value_a); // 1st x-position
    message_1.add(value_b); // 2nd y-position
    osc.send(message_1, net); // Send OSC message
}

With fOSC plug-in you want to listen to port 32000.

fOSC makes null object for every individual OSC message and you can assign position and rotation parameters to message using add() method. Then you can use Xpresso to remap those values to whatever you want to.

Cheat sheet

  1. value controls X-position.
  2. value controls Y-position.
  3. value controls Z-position.
  4. value controls H-rotation.
  5. value controls P-rotation.
  6. value controls B-rotation.

Example sketch:
Track brightest pixel from webcam and send that via OSC

Updated 14/04/2019

Cinema 4D, Experimental, Processing

BPM in Xpresso

Super simple setup to count beats per minute in Xpresso with Python node. Counting starts immediately when time is running. You can easily modify code  for your needs e.g. starting time.

import c4d

def main():
    global out # Output port: 'Out'

    bpm = 128 # Beats per minute
    spb = 60.0 / int(bpm) # Seconds per beat

    if time != 0: # If current frame is not first frame
        out = int(time/spb) # Hits count
    else: # Otherwise
        out = 0 # Hits count is 0

bpm_in_xpresso.c4d

Cinema 4D, Python, Xpresso

Vertex Maps and Python Tag

I had some fun trying to recreate @Sholmedal‘s Python Tag Vertex Map tools which he showed at his presentation at IBC 2014. I ended up with some different Python Tags.

N.B. This post is kind of obsolete now since you can do all kind of crazy stuff with fields in R20. I’ll keep this here anyway for learning purposes.

Falloff setup
This setup creates weight values to the Vertex Map. Control position with a Null.

import c4d
from c4d import utils as u

def main():
    vertexMap = op[c4d.ID_USERDATA,1] # Get Vertex Map tag
    mind = op[c4d.ID_USERDATA,2] # User Data: Minumum distance
    maxd = op[c4d.ID_USERDATA,3] # User Data: Maximum distance
    spline = op[c4d.ID_USERDATA,4] # User Data: Remap spline
    
    obj = vertexMap.GetObject() # Get object that Vertex Map uses
    pts = obj.GetAllPoints() # Get object's all points
    null = op.GetObject() # Get null object that Python Tag uses
    null[c4d.NULLOBJECT_RADIUS] = maxd # Set null's radius
    nullPosition = null.GetMg().off # Get null's position vector
    array = [0.0] # Initialize list
    if len(array) != len(pts): # If array is not same size as object's point count
        diff = len(pts) - len(array) # Get difference
        array.extend([0.0]*diff) # Extend array
    for i in xrange(len(pts)): # Iterate through points
        point = pts[i] # Get point
        distance = (nullPosition - point).GetLength() # Calculate distance
        value = u.RangeMap(distance,mind,maxd,1,0,False,spline) # Remap value
        array[i] = u.Boxstep(0,1,value) # Clamp value between zero and one
    vertexMap.SetAllHighlevelData(array) # Set the data for the Vertex Map

vertex_map_python_tag_falloff.c4d

Math
Combine two Vertex Maps together with different math operations, like add, substract and intersect.

import c4d
import operator as o

def main():
    # User data
    tagA = op[c4d.ID_USERDATA,1] # Get Vertex Map tag A
    tagB = op[c4d.ID_USERDATA,2] # Get Vertex Map tag B
    tagC = op[c4d.ID_USERDATA,3] # Get Output Vertex Map tag
    func = op[c4d.ID_USERDATA,4] # Math operation selection
    invert = op[c4d.ID_USERDATA,5] # Invert checkbox
    
    dataA = tagA.GetAllHighlevelData() # Get Vertex Map A's data
    dataB = tagB.GetAllHighlevelData() # Get Vertex Map B's data
    if func == 0: # If add
        dataC = map(o.add, dataA, dataB) # Add A and B
    elif func == 1: # If substract B from A
        dataC = map(o.sub, dataA, dataB) # Subtract B from A
    elif func == 2: # If substract A from B
        dataC = map(o.sub, dataB, dataA) # Subtract A from B
    elif func == 3: # If intersection
        dataC = map(o.mul, dataB, dataA) # Intersection
    if invert == True: # If invert checkbox is ticked
        for i in range(0, len(dataA)): # Loop through array
            dataC[i] = 1-dataC[i] # Invert value
    tagC.SetAllHighlevelData(dataC) # Set the data for the Vertex Map

vertex_map_python_tag_math.c4d

Remap
With this you can remap source data with a spline to something different.

import c4d
from c4d import utils as u

def main():
    source = op[c4d.ID_USERDATA,1] # User Data: Source Vertex Map tag
    target = op[c4d.ID_USERDATA,3] # User Data: Output Vertex Map tag
    spline = op[c4d.ID_USERDATA,4] # User Data: Remap spline
    sourceData = source.GetAllHighlevelData() # Get Vertex Map data
    array = [0.0] # Initialize list
    if len(array) != len(sourceData): # If array is not same size as sourceData list
        diff = len(sourceData) - len(array) # Get difference
        array.extend([0.0]*diff) # Extend array
    for i in xrange(0, len(sourceData)): # Iterate through data
        value = u.RangeMap(sourceData[i],0,1,0,1,False,spline) # Remap data
        array[i] = u.Boxstep(0,1,value) # Clamp value between zero and one
    target.SetAllHighlevelData(array) # Set the data for the output Vertex Map

vertex_map_python_tag_remap.c4d

Decay
With this you can decay source Vertex Map.

import c4d

array = [0.0] # Initialize global list
def main():
    global array # Get access to global list
    source = op[c4d.ID_USERDATA,1] # User Data: Source Vertex Map tag
    target = op[c4d.ID_USERDATA,3] # Data Data: Output Vertex Map tag
    decay = op[c4d.ID_USERDATA,2] # User Data: Amount of decay in percentage
    sourceData = source.GetAllHighlevelData() # Get source Vertex Map data

    if len(array) != len(sourceData): # If array is not same size as sourceData list
        diff = len(sourceData) - len(array) # Get difference
        array.extend([0.0]*diff) # Extend array
    for i in xrange(0, len(sourceData)): # Iterate through data
        array[i] = array[i]+sourceData[i] # Value is prev value + source value
        if array[i] > 1: # If value is over '1'
            array[i] = 1.0 # Clamp value to '1'
        if array[i] > 0: # If value is over '0'
            array[i] = array[i]-decay # Decay value
        if array[i] < 0: # If value is below '0'
            array[i] = 0.0 # Clamp value to '0'
    target.SetAllHighlevelData(array) # Set the data for the output Vertex Map

vertex_map_python_tag_decay.c4d

Cinema 4D, Python, Tag

Data from MoGraph to X-Particles

Here is easy Xpresso setup that drives X-Particles with MoGraph data. In this setup the cloner sets the amount of the XpEmitter’s particles. Emitter’s emission mode is set to “shot”. Then Xpresso iterates through the clones and sets position, rotation and color data from every single clone to X-Particle particles.

Now you can use for example XpTrail with your MoGraph setup instead using just old MoGraph Tracer object.

data_from_mograph_to_xpraticles.c4d

Cinema 4D, MoGraph, X-Particles

Connect Spheres

This is Python Generator that connects spheres with lines without line intersecting with spheres. There is many experimental commands in this tool. Python Generator virtually generates Sweep Objects and trims start and end parameters depending sphere’s radius so line does not go through spheres. Line is generated by using MoGraph Tracer object.

You can connect spheres by typing sphere IDs to data input. Syntax is “[first id, second id]”. You can also use experimental commands and generate automatically connections. Be careful, creating huge amount of connections will slow down your project. When you use “Add Connection” or “Remove Connection”, you must have only two spheres selected before you click those buttons.

This tool is made especially for parametric sphere objects. It does not work with anything else. This is updated post, before it was a bit clumsy Xpresso rig but now I have converted it completely to Python Generator.

connect_spheres.c4d

Asset, Cinema 4D, Experimental, MoGraph, Python

SoundFlop – A missing piece of the sound effector

This Xpresso setup is called SoundFlop. It is hooked to MoGraph’s Sound Effector. SoundFlop counts every hit that goes over certain threshold. I’m a bit confused how Sound Effector doesn’t have this feature out of the box. Fortunately we can fix this unfairness with simple Xpresso rig.

Threshold can be controlled with Sound Effector’s parameters, like “lower cutoff”, “compression” and “filter shape”.

Notice that the sound effector requires a wav file!

soundflop.c4d

Asset, Cinema 4D, MoGraph, Xpresso

Clone offset with a python effector

I wrote two Python Effectors that changes clone offset. Clone offset picks which child of the cloner gets cloned or the blending between those children. Keep in mind that offsetting clones does not change clone’s id number.

Clone offset

With the first Python Effector you can give clone id and child id to the input field and therefore you can change which child of the Cloner is chosen to that clone.

Syntax is [clone id, child id]. Clone and child indexes starts from 0 (zero). So, if you want that third clone is second child of the Cloner you would put [2,1]. You can modify as many clones as you please.

import c4d
from c4d.modules import mograph as mo

def main():
    try: # Try to execute following
        md = mo.GeGetMoData(op) # Get MoGraph data
        if md is None: return False # If there is no data, quit

        data = op[c4d.ID_USERDATA,1] # Get user data input
        c = data.replace('\n','') # Remove line breaks
        c = c.replace('\r','') # Remove carriage returns
        c = c.replace('[','') # Remove '[' characters
        c = c.split(']') # Split string to list using ']' character as a delimiter
        c.remove('') # Remove empty items from list
        clen = len(c) # Get length of the list

        gen = md.GetGenerator() # Get generator
        cnt = md.GetCount() # Get number of clones
        cln = md.GetArray(c4d.MODATA_CLONE) # Get clone array
        nchd = float(len(gen.GetChildren())) # Get number of generator's children
        fall = md.GetFalloffs() # Get falloffs

        for i in xrange(0, cnt): # Loop
            if i < clen: # If 'i' is not greater than length of the 'clen' list
                clone_id = int(c[i].split(',')[0]) # Get clone id
                child_id = int(c[i].split(',')[1]) # Get child id
                cln[clone_id] = (1/nchd) * child_id # Offset clone
    except: # If something goes wrong
        pass # Do nothing

    md.SetArray(c4d.MODATA_CLONE, cln, True) # Set clone array data
    return True

clone_offset_effector.c4d

Clone offset with weight threshold

With the second Python Effector you can change clones depending on weight transformation. You can choose number of the child and adjust the threshold. I don’t know how useful this tool is but it was fun to make.

import c4d
from c4d.modules import mograph as mo

def main():
    md = mo.GeGetMoData(op) # Get MoGraph data
    if md is None: return False # If there is no data, quit

    thd = op[c4d.ID_USERDATA,2] # Threshold
    cid = op[c4d.ID_USERDATA,3] # Chosen child id

    gen = md.GetGenerator() # Get generator
    cnt = md.GetCount() # Get number of clones
    cln = md.GetArray(c4d.MODATA_CLONE) # Get clone array
    warr = md.GetArray(c4d.MODATA_WEIGHT) # Get weight array
    nchd = float(len(gen.GetChildren())) # Get number of generator's children
    fall = md.GetFalloffs() # Get falloffs

    for i in reversed(xrange(0, cnt)): # Loop through clone count
        if warr[i] >= thd: # If clone weight is equal or greater than threshold
            cln[i] = (1/nchd)*(cid) # Offset clone

    md.SetArray(c4d.MODATA_CLONE, cln, True) # Set clone array data
    return True

clone_offset_with_weight.c4d

Cinema 4D, Effector, MoGraph, Python

X-Particles colors to Octane Render

N.B. This post is now obsolete because new version of Octane Render now supports X-Particles colors.

I have been wondering that is there a way to transfer color information from X-Particles to Octane? Well, today I started to messing around and I found some kind of solution or some kind of hack.

First thing to do is generate geometry to the particles. To do that I used polygon object that contains only single vertex point. Polygon object is put under the xpGenerator that generates vertex points to particles. To hook up xpGenerator into the MoGraph cloner xpGenerator need to be put under the connect object. Remember to turn welding off. Connect object is put to cloners object slot. Now we can clones to particles.

Next thing to do is the actual color data transformation.  I used python effector (MoGraph) to transfer color data from particles to clones. I added support for the particle rotation. With this python effector this setup works already perfectly in C4D’s own render engine but for some weird reason it does not work Octane Render.

import c4d, xparticles
from c4d.modules import mograph as mo
from c4d import utils as u

def main():
    md = mo.GeGetMoData(op) # Get MoData
    if md is None: return False # If there is no MoData, stop processing
    emitter = xparticles.ToEmitter(op[c4d.ID_USERDATA,1]) # Xpemitter link (user data)
    cnt = md.GetCount() # Get clone count
    carr = md.GetArray(c4d.MODATA_COLOR) # Get MoData color
    marr = md.GetArray(c4d.MODATA_MATRIX) # Get MoData matrix
    fall = md.GetFalloffs() # Get falloffs
    for i in reversed(xrange(0, cnt)): # Iterate through clones
        m = marr[i] # Get clone's matrix
        particle = emitter.GetParticle(i) # Get particle
        carr[i] = particle.GetColor() # Get particle color
        if particle.GetRotation() != None: # If particle is rotating
            hpb = particle.GetRotation() # Get particle rotation
            marr[i] = marr[i] * u.HPBToMatrix(hpb) # Set clone rotation to match particle rotation
    md.SetArray(c4d.MODATA_COLOR, carr, True) # Set MoData color
    md.SetArray(c4d.MODATA_MATRIX, marr, True) # Set MoData matrix
    return True # Everyting is fine

To get it work in Octane I had to add empty shader effector with blending mode set to multiply or divide. Empty means that there is no any shader in shader effector, you only need to chance blending mode. Last thing to do to get this setup to work is to add material with MoGraph color shader to the cloner. Now everything should be working fine.

There’s one drawback with this hack and it’s that the colors are a little bit off from the original in the render but I can live with that. Note that X-Particles own material doesn’t work since it’s calculated separately in rendering process. Another drawback is that this setup gets really slow when you have a thousands of particles. But cool thing is that you can use MoGraph effectors with particles.

xparticles_colors_to_octane.c4d

Cinema 4D, Experimental, MoGraph, Octane, Python, X-Particles

TurbulenceFD to MoGraph

I saw a tweet from @der_flow_ where he has hooked TurbulenceFD into MoGraph weight tag. Of course, I wanted to test that too, so I built my own version and here is the result.

Xpresso setup iterates through clones and checks what is TFD container’s temperature in that specific position where the clone is. Temperature data is then range mapped to two different MoGraph weightmaps: mw-size and mw-color. Then those two weightmaps are used in effectors selection slots to scale and colorize clones.

Setup can get really slow but setup works also with render instances and multi-instances, even though only normal instance mode shows colors in view port.

 

Updated 25/03/2019

tfd_to_mograph.c4d

Cinema 4D, MoGraph, TurbulenceFD, Xpresso

My current setup

I build my current desktop pc in 2012. It’s pretty old but still efficient. Only things that I have updated are graphic cards and SSDs. I mainly use my desktop computer but when I’m traveling I use 13″ MacBook Pro (mid 2014).

My computer’s specifications:
Operating System: Windows 7 Ultimate 64-bit
CPU: Intel i7 3930K @ 3.20 GHz Sandy Bridge-E 32nm
RAM: 32,0 DDR3 @ 1333 MHz
MOBO: Asus P9X79 Deluxe LGA2011
GPU: 2x Nvidia GeForce GTX 980
Storage: 2x Samsung 850 EVO 518GB SSD, 14TB Sata III HDD
PSU: Corsair AX850 – 80 Plus Gold
Case: Cooler Master Cosmos 1000
Fan: Noctua NH-D14

Other stuff on my table:
Sound Card: RME Babyface Pro
DAC: Cambridge Audio DacMagic Plus
Microphone: Shure SM7B
Headphones: Sony MDR-7506
Speakers: Genelec G Ones
Display: 2x Dell UltraSharp U2711
Pen Tablet: Wacom Intuos 3 Medium
Mouse: Logitech G900
Keyboard: Logitech K800

General Talk