Skip to content

Commit 2aaa3c4

Browse files
committed
Python SDK: Release 2025.2.0
1 parent d8accd5 commit 2aaa3c4

14 files changed

+811
-12
lines changed

plugins/py-ocio_node_2025/py-ocio_node_2025.pyp

-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import os
2424
import c4d
2525
import maxon
2626

27-
c4d.DRAW_TEXTUREFLAGS_USE_PROFILE_COLOR = 16 # Temporary 2025.1 fix for botched symbol export.
2827

2928
class OcioNode2025(c4d.plugins.ObjectData):
3029
"""Realizes a NodeData plugin that is meant to operate in OCIO enabled scenes.

plugins/py-rounded_tube_r13/py-rounded_tube_r13.pyp

+3-3
Original file line numberDiff line numberDiff line change
@@ -242,9 +242,9 @@ class RoundedTube(c4d.plugins.ObjectData, RoundedTubeHelper):
242242
num_seg = op[c4d.PY_TUBEOBJECT_SEG] if op[c4d.PY_TUBEOBJECT_SEG] is not None else 36
243243

244244
# Calculates LOD for subdivision parameter
245-
sub = c4d.utils.CalcLOD(num_sub, 1, 1, 1000)
246-
rsub = c4d.utils.CalcLOD(num_rsub, 1, 1, 1000)
247-
seg = c4d.utils.CalcLOD(num_seg, 1, 3, 1000)
245+
sub = c4d.utils.CalcLOD(int(num_sub), 1., 1, 1000)
246+
rsub = c4d.utils.CalcLOD(int(num_rsub), 1., 1, 1000)
247+
seg = c4d.utils.CalcLOD(int(num_seg), 1., 3, 1000)
248248

249249
# Defines list of vector position of points
250250
ptCount = 4 * (sub + rsub)

scripts/02_data_algorithms/basecontainer_iterates_r13.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ def main():
1818
raise ValueError("op is none, please select one object")
1919

2020
# Retrieves the data stored into the BaseContainer of the object
21-
bc = op.GetData()
21+
bc = op.GetDataInstance()
2222
if bc is None:
2323
raise RuntimeError("Failed to retrieve bc")
2424

scripts/04_3d_concepts/open_color_io_2025_2.py

+449
Large diffs are not rendered by default.

scripts/04_3d_concepts/rendering/render_current_frame_r12.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313

1414

1515
def main():
16-
# Retrieves a copy of the current documents render settings
17-
rd = doc.GetActiveRenderData().GetClone().GetData()
16+
# Retrieves the current documents render settings
17+
rd = doc.GetActiveRenderData().GetDataInstance()
1818
if rd is None:
1919
raise RuntimeError("Failed to retrieve the clone of the active Render Settings.")
2020

scripts/04_3d_concepts/rendering/render_ogl_half_size_r12.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
def main():
1717
# Retrieves a copy of the current documents render settings
18-
rd = doc.GetActiveRenderData().GetClone().GetData()
18+
rd = doc.GetActiveRenderData().GetClone().GetDataInstance()
1919
if rd is None:
2020
raise RuntimeError("Failed to retrieve the clone of the active Render Settings.")
2121

scripts/04_3d_concepts/rendering/render_with_progress_hook_r21.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ def main():
8585
bmp.AddChannel(True, True)
8686

8787
# Renders the document
88-
if c4d.documents.RenderDocument(doc, rd.GetData(), bmp, c4d.RENDERFLAGS_EXTERNAL, prog=PythonCallBack,
88+
if c4d.documents.RenderDocument(doc, rd.GetDataInstance(), bmp, c4d.RENDERFLAGS_EXTERNAL, prog=PythonCallBack,
8989
wprog=PythonWriteCallBack) != c4d.RENDERRESULT_OK:
9090
raise RuntimeError("Failed to render the temporary document.")
9191

scripts/04_3d_concepts/scene_elements/scene_management/basedocument_creates_render_r13.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def main():
4040
raise RuntimeError("Failed to initialize the bitmap.")
4141

4242
# Renders the document
43-
if c4d.documents.RenderDocument(tempDoc, rd.GetData(), bmp, c4d.RENDERFLAGS_EXTERNAL) != c4d.RENDERRESULT_OK:
43+
if c4d.documents.RenderDocument(tempDoc, rd.GetDataInstance(), bmp, c4d.RENDERFLAGS_EXTERNAL) != c4d.RENDERRESULT_OK:
4444
raise RuntimeError("Failed to render the temporary document.")
4545

4646
# Frees the temporary document, means that all objects from this document are no longer alive.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""Demonstrates the technical aspects of #GraphDescription.ApplyDescription calls.
2+
3+
To run the script, paste it into the Script Manager of Cinema 4D and execute it.
4+
5+
Note:
6+
This example is more about the technical aspects of #GraphDescription.ApplyDescription and
7+
therefore less relevant for beginners.
8+
"""
9+
import c4d
10+
import maxon
11+
import mxutils
12+
13+
__author__ = "Ferdinand Hoppe"
14+
__copyright__ = "Copyright (C) 2024 Maxon Computer GmbH"
15+
__version__ = "2025.0.0+"
16+
17+
doc: c4d.documents.BaseDocument # The active document
18+
19+
def main() -> None:
20+
"""Called when Cinema 4D runs this script.
21+
"""
22+
# The graph description we are going to apply in the different #ApplyDescription calls, the
23+
# content of the graph is here irrelevant for this example, other than it is intended for a
24+
# Redshift material graph.
25+
redshiftDescription: dict = {
26+
"$type": "Output",
27+
"Surface": {
28+
"$type": "Standard Material",
29+
"Base/Color": (1, 0, 0),
30+
"Metalness": 1.0,
31+
"Reflection/IOR": 1.1415
32+
}
33+
}
34+
35+
# A Standard Renderer and Redshift material graph we are going to use in the example.
36+
standardGraph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
37+
name="Standard Material", nodeSpaceId=maxon.NodeSpaceIdentifiers.StandardMaterial)
38+
redshiftGraph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
39+
name="Redshift Material", nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
40+
41+
# This is how we usually call #ApplyDescription, we just pass the graph to modify and the
42+
# description to apply.
43+
maxon.GraphDescription.ApplyDescription(redshiftGraph, redshiftDescription)
44+
45+
# A description that is not intended for the graph its is applied to will always fail at some
46+
# point because the node types referenced in the description are then not contained in the node
47+
# space of the graph. But because node spaces share some node types, as for example the value
48+
# node, it can take some time until the graph description fails. But one can never end up with
49+
# a half way executed graph description due to the transaction logic of the Nodes API. A graph
50+
# description #ApplyDescription call either succeeds in its entirety or fails as a whole.
51+
52+
# This cannot work, we are trying to apply a Redshift graph description to a standard graph.
53+
try:
54+
maxon.GraphDescription.ApplyDescription(standardGraph, redshiftDescription)
55+
except Exception as e:
56+
print(e) # Will print :
57+
# "The node type reference 'Output' (lang: 'en-US', space: 'net.maxon.nodespace.standard')
58+
# is not associated with any IDs. [graphdescription_impl.cpp(1648)] ..."
59+
60+
# #ApplyDescription offers a node space ID argument which allows the user to manually state of
61+
# which type the passed graph description is. There is no concrete technical advantage in doing
62+
# this other than ending up with more verbose code and the fact that such calls for a mis-matching
63+
# graph will fail right away and not only when already half of the description has been executed
64+
# and the graph then having to be unwound for a failed graph transaction.
65+
66+
# Cannot work either, but might will a bit more gracefully for complex graph descriptions.
67+
try:
68+
maxon.GraphDescription.ApplyDescription(
69+
standardGraph, redshiftDescription, nodeSpace=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
70+
except Exception as e:
71+
print(e) # Will print:
72+
# "User defined node space 'com.redshift3d.redshift4c4d.class.nodespace' does not match
73+
# passed graph."
74+
75+
# A more impactful option is the language of the graph description. Currently graph descriptions
76+
# only support the English language for label references. When no language is given, Cinema 4D
77+
# assumes the system language (because at its core graph descriptions are already designed for
78+
# other languages environments than en the English language). By passing "en-US" we can force
79+
# force a non-English interface labels Cinema 4D instance to resolve the graph description as if
80+
# it were English.
81+
82+
maxon.GraphDescription.ApplyDescription(
83+
standardGraph, redshiftDescription, nodeSpace=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
84+
85+
if __name__ == "__main__":
86+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""Demonstrates how to retrieve graphs from scene elements.
2+
3+
To run the script, paste it into the Script Manager of Cinema 4D and execute it.
4+
"""
5+
import c4d
6+
import maxon
7+
import mxutils
8+
9+
__author__ = "Ferdinand Hoppe"
10+
__copyright__ = "Copyright (C) 2024 Maxon Computer GmbH"
11+
__version__ = "2025.0.0+"
12+
13+
doc: c4d.documents.BaseDocument # The active document
14+
15+
def main() -> None:
16+
"""Called when Cinema 4D runs this script.
17+
"""
18+
# The most common use case for GetGraph is to create a new material and graph from scratch.
19+
# The call below will result in a new material being created in the active document with the
20+
# name "matA". Since we are not passing an explicit node space, the currently active material
21+
# node space will be used (which usually is the Redshift node space for most users).
22+
graphA: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(name="ActiveNodeSpace")
23+
print(f"{graphA = }")
24+
25+
# This will create a material named "matB" that has a graph in the Redshift material node space,
26+
# no matter what the currently active material node space is.
27+
graphB: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
28+
name="RedshiftSpace", nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
29+
print(f"{graphB = }")
30+
31+
# But we can also use the function to create or get a graph from an existing material. The call
32+
# below will either create a new Redshift graph for the first material in the document #material
33+
# or return its existing Redshift graph. The CheckType call exists so that the script halts when
34+
# there is no materials in a document. But since we just crated #matA and #matB, we will here
35+
# just retrieve the graph from "matB" (because new materials inserted in front at not at the end).
36+
# I.e., #graphB and #graphC are the same.
37+
material: c4d.BaseMaterial = mxutils.CheckType(doc.GetFirstMaterial())
38+
graphC: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
39+
material, nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
40+
print(f"{graphC = }")
41+
print(f"{graphB == graphC = }")
42+
43+
# We could also create a Standard Renderer graph, or a third party render engine graph such
44+
# as Arnold or VRay for example.
45+
graphStandard: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
46+
name="StandardSpace", nodeSpaceId=maxon.NodeSpaceIdentifiers.StandardMaterial)
47+
48+
try:
49+
vrayGraph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
50+
name="VRaySpace", nodeSpaceId="com.chaos.class.vray_node_renderer_nodespace")
51+
except Exception as e:
52+
print (e)
53+
54+
# We can also pass a document or an object for the first argument #element of GetGraph. If we
55+
# pass a document, it will yield the scene nodes graph of that document, and if we pass an object,
56+
# it will yield its capsule graph (if it has one).
57+
sceneGraph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(doc)
58+
print(f"{sceneGraph = }")
59+
60+
# Finally, we can determine the contents of a newly created graph. We can either get an empty
61+
# graph or the so-called default graph with a few default nodes as it would be created in
62+
# Cinema 4D when the user creates a new graph for that space. #createEmpty=True is the default
63+
# of a #GetGraph call.
64+
emptyGraph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
65+
name="ActiveSpaceEmptyGraph", createEmpty=True)
66+
defaultGraph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
67+
name="ActiveSpaceDefaultGraph", createEmpty=False)
68+
69+
# Finally, there is also GraphDescription.GetMaterialGraphs which simplifies iterating over all
70+
# material graphs of a given node space in a document. This can simplify carrying out batch
71+
# operations on material graphs for a specific render engine, while keeping other render engines
72+
# untouched. Other than GetGraph, this function will not create new graphs when none exist.
73+
74+
# Print all Redshift renderer material graphs in a document. This will be at least one graph,
75+
# because created explicitly one in this script.
76+
graph: maxon.NodesGraphModelRef
77+
for graph in maxon.GraphDescription.GetMaterialGraphs(doc, maxon.NodeSpaceIdentifiers.RedshiftMaterial):
78+
print(f"Redshift graph: {graph}")
79+
80+
# Same for the Standard renderer material graphs. This will also be at least one graph, because
81+
# created explicitly one in this script.
82+
for graph in maxon.GraphDescription.GetMaterialGraphs(doc, maxon.NodeSpaceIdentifiers.StandardMaterial):
83+
print(f"Standard graph: {graph}")
84+
85+
if __name__ == "__main__":
86+
main()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""Demonstrates the core concepts of graph descriptions by creating a simple material.
2+
3+
This script will create a red metallic material with an index of refraction of 1.1415 in the Redshift
4+
node space. To run the script, paste it into the Script Manager of Cinema 4D and execute it.
5+
6+
Note:
7+
This is the brief version of the hello_world script. See hello_world_narrative.py for a narrative
8+
documentation of this script.
9+
"""
10+
__author__ = "Ferdinand Hoppe"
11+
__copyright__ = "Copyright (C) 2024 Maxon Computer GmbH"
12+
__version__ = "2025.0.0+"
13+
14+
import c4d
15+
import maxon
16+
17+
doc: c4d.documents.BaseDocument # The active document
18+
19+
def main() -> None:
20+
"""Called when Cinema 4D runs this script.
21+
"""
22+
# Get a new material named "Hello World" which has a graph in the Redshift node space.
23+
graph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
24+
name="Hello World", nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
25+
26+
# Apply a graph description to the graph.
27+
maxon.GraphDescription.ApplyDescription(
28+
graph,
29+
# The description which is applied to the graph. We are going to write a simple description
30+
# of a Standard Material node connected to an Output node. Note that graph descriptions flip
31+
# the reading direction of graphs from the usual left-to-right to right-to-left. They
32+
# start out with the terminal end node of the graph and then describe the nodes leading to
33+
# that node. Each scope (a pair of curly braces) in the description describes a node.
34+
{
35+
# We describe the terminal Output node of the graph.
36+
"$type": "Output",
37+
38+
# We describe the value of the Surface port of the Output node which is connected to the
39+
# outColor port of a Standard Material node.
40+
"Surface": {
41+
"$type": "Standard Material",
42+
43+
# We set a few ports of the Standard Material node to literals, i.e., constant
44+
# values that are not driven by other nodes.
45+
"Base/Color": (1, 0, 0),
46+
"Metalness": 1.0,
47+
"Reflection/IOR": 1.1415
48+
}
49+
}
50+
)
51+
52+
if __name__ == "__main__":
53+
main()

0 commit comments

Comments
 (0)