Skip to main content

Python

  • Cinema 4D API offers basically two means to uniquely identify objects: BaseObject::GetGUID() and BaseList2D::GetMarker() which respectively access the object's unique ID and object's GeMarker.

Every instance of c4d.BaseList2D owns a reference to an object of the c4d.BaseContainer class. It is basically is an associative array containg all the values of the parameters an object has in the Attribute-Manager.

import c4d

bc = op.GetDataInstance()
bc.SetVector(c4d.PRIM_CUBE_LEN, c4d.Vector(500,100,20))

Note, c4d.BaseContainer.GetDataInstance() returns the original container, NOT A COPY. Modifieng this container will directly modify the object’s parameters. If you want to get a copy, use c4d.BaseObject.GetData()

Accessing the BaseContainer and using subscripting

There is a more convenient way, an alternative of using methods like SetVector, SetReal, etc. The trick is called subscripting using the bracket [] syntax.

import c4d
op[c4d.PRIM_CUBE_LEN] = c4d.Vector( 500, 100, 20)

import c4d

bc = op.GetDataInstance()

bc[c4d.PRIM_CUBE_LEN] = c4d.Vector( 500 , 100 , 20)

import c4d

op[c4d.PRIM_CUBE_LEN] = c4d.Vector( 500, 100, 20 )

Usually Cinema4D store settings for nodes and tools in a BaseContainer.
BaseContainer is very useful class that allow you to store different data type in easy way.
But you will notice there are some cases where Cinema don’t store parameter in BaseContainer, for example some of light and camera parameters are not reachable by Get/Set BaseContainer Methods.

To do

C++ Stuff

  • For C++. The [] stuff needs to be converted to GetParameter()&SetParameter(). As well as the button codes.
  • Here's the .h file resources: ........\resource\modules\ca\res\description oolcaweight.h
  • C++ doesn't have multiple return values. If you want to return multiple things, you pass by reference or by using pointers to allocated memory.
  • It's also faster to do this. When you return an array in Python, it allocates a new block of memory, fills it (often copying from something else), returns it, and then deallocates the original. That's a lot of overhead.
  • So think of the *map as your return value. You pass the function an array of floats, and it fills it. The array should be the same size as the number of points.
  • I'll side with you on HashMap over std::map since we all know std::map is way too slow
  • using standard library components is not advised since the standard library uses/throws exceptions. And exceptions are a concept not supported/used by the Cinema API.

General

  • https://developers.maxon.net/docs/Cinema4DPythonSDK/html/types/tags.html
  • get-pip.py for mayapy.exe and c4dpy.exe
  • When it’s frozen try Shift-Shift-Del (Windows) or Cmd-Opt-Shift-Del (Mac) to trigger a crash inside C4D.
  • so what we have to bear in mind when we work with dialog is that dialog can’t be registered or opened by itself. Basically what is needed is a call by another plugin, In most of case we have to create a simple command Plugin (CommandData) that will open Dialog for us, the same operation can be done in a lot of mode like by double click on material open Material Editor and so on.
  • BaseDocument.GetDocumentPath(self)
  • being GeMarker class not available in Python you are out of real chances to distinguish two layers sharing the same name.

Why, because I need them, I didn't say I'm using OpenGL. Besides, the matrix would still not be usable in OpenGL because c4d uses left-handed coordinate system and OpenGL uses right handed-one.

I figured it out in the meantime, so in case anybody ever needs something similar, it's like this: C4d uses left-handed system. HPB rotation translates to rotation about axes in this way: H is rotation around (0,-1,0) axis (y-axis, the angle is negated), P around (-1,0,0) and B around (0,0-1). Accounting for OpenGL's right-handed system, the rotations will become rx = p ry = h rz = -b When applying the rotation, the order is rotY(ry)*rotX(rx)*rotZ(rz)

def _GlobalToLocal(self, obj, global_pos) : #""" Returns a point in global coordinate in local space. """
obj_mg = obj.GetMg()
return ~obj_mg * global_pos 

UUID Pointers

General Commands

  • Get All Objects
  • Get All Objects by Type
  • Get Selected Object
    • method 1: doc.GetActiveObject()
    • method 2: op
  • Get Object by Full Name: doc.SearchObject("object_name")
  • Get Object by Partial Name
  • Get Object from Python Tag: op.GetObject()
  • Get Tag from Selected Object: op.GetTag(objectID)
  • Get Material
  • Get Objects Position/Rotation/Scale (either in Global or Local Space): Matrix Fundamentals
  • Get Tag
  • Select by Same Layer
  • Query Parameter:
    • The easiest way to query the parameter is to either drag it into the console and hit enter or drag it directly to the script manager. Be it noted that you need to modify it to a proper object, if you query it using the script manager. You can see a better illustration in the official documentatioin
      # Dragging into the console
      Cube[c4d.PRIM_CUBE_LEN,c4d.VECTOR_X] # Result 200.0

      # Dragging into the script manager and modify
      Cube = doc.SearchObject("Cube")
      Cube[c4d.PRIM_CUBE_LEN,c4d.VECTOR_X] # Result 200.0
  • Set Parameter
    • Method 1: Like quering, you can just the drag the parameter but this time add a = sign and the corresponding value.
    • Cube[c4d.PRIM_CUBE_LEN,c4d.VECTOR_X] = 300
    • Method 2:
      bc = op.GetDataInstance() # Get object

      # Note: c4d.BaseContainer.GetDataInstance() returns the original container, NOT A COPY.
      # Modifying this container will directly modify the object’s parameters.
      # If you want to get a copy, use c4d.BaseObject.GetData()

      bc.SetVector(c4d.PRIM_CUBE_LEN, c4d.Vector(500, 100, 20)) # Set Parameter
  • All capital commands/attribute in C4D requires c4d as a prefix. For instance, [ID_BASELIST_NAME] should be [c4d.ID_BASELIST_NAME] for it to work
  • Get Current Frame c4d.documents.GetActiveDocument().GetTime().GetFrame(doc.GetFps())
  • Empirical evidence would suggest that you should be calling c4d.threading.GeIsMainThread() before attempting to access the thread via c4d.threading.GeGetCurrentThread().
  • And scripts in Script Manager are actually a very simplified version of CommandData plugins, in a way that these are basically one command per script.
  • The thing is, opposite to a script and instead of implementing CommandData.Execute() you can also implement GetSubContainer() and ExecuteSubID(). By this you can have several commands in one single CommandData plugin, which can then be assigned with different keyboard shortcuts each, either in code or in Customize Commands manager
  • Quick documentation: Print help(Cube). This will return all available commands for the cube object
  • For Tags it’s T[tagname] and similarly for Objects is O[object] Onull, Ocube, Osphere etc you can also call shader types with X[shader] Xgradient, Xlayer
  • Capital letters most likely represent global parameters. Refrain in using it as a variable.
  • Special defined variables: doc (refers to the active document) and op (refers to the active object).
  • It's explicit equivalent is doc = c4d.documents.GetActiveDocument() and op = doc.GetActiveObject()
  • Computers like radians, but it is easier for user to manipulate in degrees.
  • Just use a utility conversion such as c4d.utils.DegToRad(d)
  • DESCID is like an address to all the attribute in the attribute manager
  • GetMg() retrieve the global matrix
  • One disadvantage of the CallCommand() in Cinema 4D is the way it records separate Undo steps for each call to the function. This makes it impossible to have a single Undo for the whole script as the separate steps are recorded to the Undo stack individually.

  • When you get to more complicated scripts, a “call command” is basically breaking the codeflow. You can’t control the undo steps that it makes, and you are never really sure what exactly happens behind the “call command”.

  • GetBit Active to test if it is selected

  • C4D Base Container is a generic class that holds settings. Every object in cinema has a base container.

  • Object and Tag types *[] bracket operator in a case of a BaseList2D is an alias for Set/GetParameter. *[] bracket operator in a case of BaseContainer is an alias for SetData / GetData, see setitem and getitem.

  • Easing in the utils.rangemapper using spline. Thanks Andy Needham!
  • nes emulation plugin mario
  • Anim Selector: https://www.highend3d.com/maya/script/animselector-the-fast-picker-for-maya
  • In tags or XPresso nodes, changing the actual object tree is forbidden and may crash C4D (see documentation for Remove() and InsertUnderLast()).
  • Almost all of them are methods. GetTime()
  • “…CINEMA 4D R16Commandline.exe” -render “…TestComp_3Dmap.c4d” -frame 0 0
  • The Timer will not be executed during a dragging operation. It blocks the main thread. Not even messages and events are processed during that time. So, a thread that calls SpecialEventAdd() wouldn’t allow this either.
  • You can use asynchronous dialogs whenever you want, just not in scripts. Scripts only live while they’re being executed, so asynchronous dialogs can’t work.
  • Description based GUI's are mostly used with tag and object creation plugins.
  • You've got it backwards Scott - ResEdit only works with the dialog resource syntax for plugins. It doesn't work with the description resource syntax for tags & objects.
  • In general parameters of objects (or tags, materials,...) are specified in so called Descriptions (also here). With User Data it's a bit special, as you first need to obtain the User Data containers, which store the Descriptions, via GetUserDataContainer().
  • nes emulation plugin mario
import c4d  
data = c4d.gui.FontDialog()

for k, v in data:
print k, v

font = c4d.FontData()
font.SetFont(font)
  • All capital commands/attribute in C4D requires “c4d.” as a prefix. For instance, [ID_BASELIST_NAME] should be [c4d.ID_BASELIST_NAME] for it to work
  • Check attributes: Drag any object attribute to the console. Hit enter and it will return current attribute. For instance, Cube[c4d.ID_BASEOBJECT_REL_SCALE,c4d.VECTOR_X] will return 1.0 by default\
  • Quick documentation: Print help(Cube). This will return all available commands for the cube object
  • For Tags it’s T[tagname] and similarly for Objects is O[object] Onull, Ocube, Osphere etc you can also call shader types with X[shader] Xgradient, Xlayer
  • Capital letters most likely represent global parameters. Refrain in using it as a variable.
  • Square brackets in the attribute documentation are optional
  • Special defined variables: Doc (refers to the active document) and Op (refers to the active object)
  • Computers like radians, but it is easier for user to manipulate in degrees. Hence, convert degree to radians. Command is under the utils
  • n to split the lines
  • DESCID is like an address to all the attribute in the attribute manager
  • GetMg() retrieve the global matrix
  • One disadvantage of the CallCommand() in Cinema 4D is the way it records separate Undo steps for each call to the function. This makes it impossible to have a single Undo for the whole script as the separate steps are recorded to the Undo stack individually.
  • When you get to more complicated scripts, a “call command” is basically breaking the codeflow. You can’t control the undo steps that it makes, and you are never really sure what exactly happens behind the “call command”.
  • When looking through the Cinema 4D Python documentation we find the c4d.documents module that has all the functionality to access our scene. The function GetActiveDocument() function returns the currently active scene as a BaseDocument object.
  • Now that we know how to access the scene we are left with finding out how to get a list of the selected objects in the scene. Looking at BaseDocument we see that it has a GetActiveObjects() method that returns a list of BaseObject objects.
  • op.GetName-
  • Almost all of them are methods. GetTime()
  • Object and Tag types
  • [] bracket operator in a case of a BaseList2D is an alias for Set/GetParameter.
  • [] bracket operator in a case of BaseContainer is an alias for SetData / GetData, see setitem and getitem.
  • Easing in the utils.rangemapper using spline. Thanks Andy Needham!
  • Anim Selector: https://www.highend3d.com/maya/script/animselector-the-fast-picker-for-maya
  • In tags or XPresso nodes, changing the actual object tree is forbidden and may crash C4D (see documentation for Remove() and InsertUnderLast()).
  • Assertion Error. Insert them separately. If you have multiple insert object in the same control
  • GetBit Active to test if it is selected
  • C4D Base Container is a generic class that holds settings. Every object in cinema has a base container.\
  • “…CINEMA 4D R16Commandline.exe” -render “…TestComp_3Dmap.c4d” -frame 0 0
  • The Timer will not be executed during a dragging operation. It blocks the main thread. Not even
  • messages and events are processed during that time. So, a thread that calls SpecialEventAdd()
  • wouldn’t allow this either.
  • You can use asynchronous dialogs whenever you want, just not in scripts. Scripts only live while they’re being executed, so asynchronous dialogs can’t work.

Pipeline

Environment Variables include: C4D_BROWSERLIBS (lib4D presets) C4D_PLUGINS_DIR (plugins) C4D_SCRIPTS_DIR (scripts - R19+)