avd_Logo

avd_Logo
Welcome to the world of mystery.

Python Commands/Tutorials

disclaimer-I am not a professional programmer. If you follow my advice and something goes wrong, dont be surprised, it happens, but please let me know where i led you astray and we can work together to fix the incorrect information. other than that, hope this is helpful !

-IMPORTING MODULES-

When i first load up python in C4D I always use these commands to make my coding a helluva lot shorter and easier to make. plus the math, random, and utils module are super useful. 


import c4d # must use this first to connect the main c4d module with your code. 

import math # import the built in python math functions

import random # import the random functions

from c4d import documents as docs # import the documents module and assign the name "docs"

from c4d import BaseObject as bo # import the BaseObject class and assign the name "bo"
from c4d import BaseTag as bt # same thing for tags
from c4d import utils as u #utilities module
from c4d import Vector as v #vector module


-DEFINING MODULES or HOW I LEARNED TO STOP WORRYING AND LOVE THE API DOCUMENTATION-
Python is not a hard language to learn when compared with other languages like c++. But once python begins to be implemented into a program like c4d, the complexity ballons very quickly because despite python being a great simple language, all the commands from c4d had to be ported over to python and the coders that did this did not make it very self explanatory. Plus the documentation expects you to understand how to find commands that are not listed anywhere in the documentation. This section is about some useful tools to figure out what is wrong with your code. 

Firstly, when working in c4d , you need understand the different areas (or "hooks") that c4d uses to implement your code and the different ways that you can create code. 

1. Python Script

    -Uses:
        Usually used to create a button that can be called from the plugins menu.

    -Pros:
        Quick, easy to create, generally easy to use for the user. Works great with interactive
        commands, such as CallCommand(), or SendModelingCommand.

    -Cons:
        Limited in Its ability to retain information over long periods of time. Generally, when you call
        a script from the menu, it is only called once and evaluated once. there are exceptions to this, 
        but generally, your script will run and then all information/data that was used is discarded.

2.Python Generator

    -Uses:
        Generates objects that can hold User Data as well as python code. 

    -Pros:
        Quick to set up. works great for Generating new types of spline or geometry shapes or it can 
         be used to prototype out ideas. 

    -Cons
        Does not work as well with SendModelingCommand()(requires more work to use) or   
        CallCommand(). this is due to the fact that some functions use the base Document and the 
        python object is it's own BaseDocument... sort of... this also limits your ability to send update
        messages.

3.Python Tag


    -Uses
         A tag that can hold user data and python code.

    -Advantages
         Great for creating code that you want to be able to copy across multiple objects.

    -Disadvantages
          much like the python generator, some commands do not work as easily in a python tag as they
          would within the python script.

4.Python Node


    -Uses
        Creates a node in the xpresso graphview that can have unlimited(?) inputs and outputs, all
        manipulated by the python code it contains. 

    -Advantages
        Allows you to create your own xpresso nodes! now instead of using 20 nodes in xpresso you can  
        just use one python node!

    -Disadvantages
     Generally, limited to only being used in the xpresso graphview. User cannot really access hits type 
       of node as easily. 
5.Python Plugin


    -Uses:
        Create your own Python plugin! Is callable from the plugins menu.

    -Advantages
        Nearly limitless control due to the fact that your plugin will be run in the main thread of c4d.

    -Disadvantages
        Hard to figure out and complex to set up.


My favorite one of these methods to use when just playing around with ideas is usually the python generator because of it's ability to hold user data and the fact that it can be used in conjunction with all the mograph tools! fun times ahead...

-First example-
Lets use the Python Generator to create a simple beveled cube. This should be a good first attempt at understanding python in c4d, plus the python generator comes into the scene automatically generating a cube! kickass. so...

1) Lets create a python Generator click on the "Open Python Editor" button.
2) Inside the editor is a bit of code:

import c4d
#Welcome to the world of Python


def main():
    return c4d.BaseObject(c4d.Ocube)

Lets take a look at the code.

-Using ' def '-
First of all the 'def main()' function is a way for python to understand a function. 'def' is the way to being a function declaration followed by the name, in this case, 'main'  followed by ':'. Then comes our function's code, indented by 4 spaces. Because we are using the "main" function name, this is the code that will be called every time the python Generator updates. If we define our own function we can run that whenever we want, but the main function is required to get the python object to work inside c4d. All your main code will go here.



-Using ' return '
with ' c4d.BaseObject'-
The next line of code defines the cube and returns it to the scene. 'return' should only be used with a python object if you want to have the python generator create things and contain them(like a cube). also keep in mind that 'return' can only be used on one object, so if you are creating a complex object just use the command 'c4d.BaseObject..InsertUnder()' to place your object into a null(more on creating objects up next...)  and then use return on the null. it might look something like this:


def main():
    null = c4d.BaseObject(c4d.Onull)
    cube =  c4d.BaseObject(c4d.Ocube)

    cube.InsertUnder(null)
    return null


So you can begin to see how to use a python object to return a simple cube or a nul full of objects, but what about defining a new object? mabye creating a custom spline or custom polygon, or in our case, beveling an existing object? well we need to create a def function that will bevel whatever we place in it.

Here is a bevel function with special thanks to Niklas(Nux) on plugin cafe for his help on getting this to work inside a python Generator object.


def Bevel(op,offset):
    bc = c4d.BaseContainer()
    bc[c4d.MDATA_BEVEL_OFFSET2] = offset
    bc[c4d.MDATA_BEVEL_MODE] = c4d.MDATA_BEVEL_MODE_LINEAR
    if (not op) | (not op.CheckType(c4d.Opolygon)): return op
    doc = c4d.documents.BaseDocument()
    doc.InsertObject(op, None, None)
    print c4d.utils.SendModelingCommand(
                              command = 450000005,#bevel
                              list = [op],
                              bc = bc,
                              doc = doc,
                              mode = c4d.MODIFY_EDGESELECTION )
    return op.GetClone()


so lets go over this line by line.

1) def Bevel(op,offset):
   - first we declare the function's name and the data that will be plugged into the function. in this case, we need to things for our simple bevel function. irst we need our object, and then we need the offset, or how big the bevel operation is going to be. finally, we close the line off with the ":" in order to begin the function's main execution code.

2)    bc = c4d.BaseContainer()
       bc[c4d.MDATA_BEVEL_OFFSET2] = offset
       bc[c4d.MDATA_BEVEL_MODE] = c4d.MDATA_BEVEL_MODE_LINEAR

   - these next three lines are to declare a BaseContainer object which holds all the settings for our bevel until  we are ready to use the c4d.utils.SendModelingCommand() function(which requires a base container for execution).  The c4d.MDATA_xxxx option names can be found by dragging the parameters inside c4d into the python console(or just the console if your in r13). the c4d.MDATA_BEVEL_MODE_xxxx are also found by searching for the .h files in your c4d/resource folder that correspond to the tool you are using. For this example we need ot find the bevel tool, so i typed "bevel" into my search bar and found a file in my c4d/resource/modules/modeling/res/description
called "toolbevel.h" inside that file(you can just open it with textedit) you should see something that looks like this:

#ifndef _ToolBevel_H_
#define _ToolBevel_H_
enum
{
MDATA_BEVEL_PRESERVEGROUPS = 2040, // BOOL
MDATA_BEVEL_OFFSET1 = 2041, // REAL
MDATA_BEVEL_OFFSET2 = 2042, // REAL
MDATA_BEVEL_ANGLE = 2043, // REAL
MDATA_BEVEL_VARIANCE1 = 2044, // REAL
MDATA_BEVEL_VARIANCE2 = 2045, // REAL
MDATA_BEVEL_SUBDIVISION = 2172, // LONG
MDATA_BEVEL_CREATENGONS = 2173, // BOOL
MDATA_BEVEL_PATH = 2174, // Spline
MDATA_BEVEL_MODE      = 2175, // LONG
        MDATA_BEVEL_MODE_LINEAR                 = 0,
MDATA_BEVEL_MODE_OUTER_CIRCLE           = 1,
MDATA_BEVEL_MODE_INNER_CIRCLE           = 2,
MDATA_BEVEL_MODE_BEZIER                 = 3,
MDATA_BEVEL_MODE_USER                   = 10,
MDATA_BEVEL_
};
#endif



so you can see that all the options you need are already available to use in python, but you need to find the correct c4d enumeration file in order to get the data names you need. this is helpful to understand once you are trying to make your own plugins, because nearly everything inside c4d is already a plugin! the bevel tool is constructed the same as a user-made plugin! but it has been integrated much deeper into the c4d shell. so moving on...


3)    if (not op) | (not op.CheckType(c4d.Opolygon)): return op


This is what i believe can be known as a line of escape code. It is always checking to see whether there is:
a. Not An Object ( "if (not op)" )
OR ( "|" )
     b. the object's type is not a c4d.Opolygon object("not op.CheckType(c4d.Opolygon)")

in the case that either of these parameters are met, the function is escaped using the "return op" code, simply sending back whatever was sent to the function. if neither of these parameters are met, the code continues...

4)      doc = c4d.documents.BaseDocument()
doc.InsertObject(op, None, None)


this line is basically like our bc = c4d.BaseContainer() except we are declaring a document. this is the SAFEST way to use a python generator with modeling functions. you have to create a sort of virtual 
document that we can process our commands inside of and once we are done we can return the modeled object to our original document. that is why the second of these two lines then inserts our object into the BaseDocument we just created using the "doc.InsertObject()" Function.

5)     print c4d.utils.SendModelingCommand(

                              command = 450000005,#bevel
                              list = [op],
                              bc = bc,
                              doc = doc,
                              mode = c4d.MODIFY_EDGESELECTION )

- Finally, we reach the send modelling command. this is a tricky command because it tends to change usage based upon the modeling command you are using. In all cases, you execute the sendModelingCommand(SMC) with the "print" function. this is because the modeling command will return different printed results to let you know the success/failure of your SMC. In this case, the command ("command = 450000005,#bevel") will modify the original object("list = [op]"). the BaseContianer we created before is passed to the SMC ("bc = bc")  and the document as well ("doc = doc") , and finally we have to tell the SMC what aspect of the object we are working on("mode = c4d.MODIFY_EDGESELECTION ").

6)    return op.GetClone()

   - once we have sucesfully completed our bevel, we dont want to return our original object directly, because this will cause a loop within our bevel function so we use the "GetClone()" function, which will return a duplicate of our beveled object without affecting our original. now we need to run this function on something generated by our Python Generator Object. for this we will move to our main function inside the python object.

since the default code makes a cube, lets just modify it a bit to assign the cube to a variable and bevel it. the code goes something like this:


def main():
    cube = c4d.BaseObject(c4d.Ocube)
    cube1 = cube.GetCache()
    cube2 = Bevel(cube1,10)
    return cube2

The Initial step is to get our basic cube ("cube = c4d.BaseObject(c4d.Ocube)"). then we need to retrieve the polygon object that represents the cube using the "GetCache()" function(this returns a copy of our object, but in it's editable polygon form... A.K.A. a "c4d.PolygonObject()"). then we pass our polygon to our bevel function( "cube2 = Bevel(cube1,10)"). finally we return this new beveled cube.


so all in all your final code should read similar to the following:


import c4d

#Welcome to the world of Python
def Bevel(op,offset):
    bc = c4d.BaseContainer()
    bc[c4d.MDATA_BEVEL_OFFSET2] = offset
    bc[c4d.MDATA_BEVEL_MODE] = c4d.MDATA_BEVEL_MODE_LINEAR
    if (not op) | (not op.CheckType(c4d.Opolygon)): return op
    doc = c4d.documents.BaseDocument()
    doc.InsertObject(op, None, None)
    print c4d.utils.SendModelingCommand(
                              command = 450000005,#bevel
                              list = [op],
                              bc = bc,
                              doc = doc,
                              mode = c4d.MODIFY_EDGESELECTION )
    return op.GetClone()
def main():
    cube = c4d.BaseObject(c4d.Ocube)
    cube1 = cube.GetCache()
    cube2 = Bevel(cube1,10)
    return cube2

3 comments:

  1. Job offer:
    i'm searching for a PYTHON PROGRAMMER to build a little plugin for C4D.
    My name is Alessandro Boncio from Italy
    www.renderking.it
    a.boncio@renderking.it

    ReplyDelete
  2. This is a great introduction to Python and C4D. Will you be doing any more? Look forward to them if you are.

    ReplyDelete
  3. DEFINITELY! glad people are finding it useful! ill continue with the next part of the tutorial as soon as possible. unfortunately, i have too much stuff going on right now. but maybe this weekend, i will try to post the next part. thanks for the feedback!

    ReplyDelete