Skip to content

Commit a2bf564

Browse files
committed
Python SDK example for Cinema 4D 2024.5.0
1 parent 16990bb commit a2bf564

File tree

4 files changed

+63
-63
lines changed

4 files changed

+63
-63
lines changed

plugins/py-texture_baker_r18/py-texture_baker_r18.pyp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,16 @@ class TextureBakerThread(c4d.threading.C4DThread):
7474
self.bakeData = bakeData
7575

7676
# Initializes bake process
77-
bakeInfo = c4d.utils.InitBakeTexture(self.doc, self.textags, self.texuvws, self.destuvws, self.bakeData, self.Get())
77+
bakeInfo = c4d.utils.InitBakeTexture(self.doc, self.textags, self.texuvws, self.destuvws,
78+
self.bakeData, self.Get())
7879
self.bakeDoc = bakeInfo[0]
7980
self.bakeError = bakeInfo[1]
8081

8182
if self.bakeError != c4d.BAKE_TEX_ERR_NONE or self.bakeDoc is None:
8283
return False
8384

8485
# Starts bake thread
85-
self.Start(c4d.THREADMODE_ASYNC, c4d.THREADPRIORITEXY_BELOW)
86+
self.Start(c4d.THREADMODE_ASYNC, c4d.THREADPRIORITYEX_BELOW)
8687

8788
return True
8889

@@ -93,7 +94,8 @@ class TextureBakerThread(c4d.threading.C4DThread):
9394

9495
def Main(self):
9596
# Bake Texture Thread Main routine
96-
self.bakeError = c4d.utils.BakeTexture(self.bakeDoc, self.bakeData, self.bakeBmp, self.Get(), self.BakeTextureHook)
97+
self.bakeError = c4d.utils.BakeTexture(self.bakeDoc, self.bakeData, self.bakeBmp, self.Get(),
98+
self.BakeTextureHook)
9799

98100
# Sends core message once baking has finished
99101
c4d.SpecialEventAdd(PLUGIN_ID)

scripts/04_3d_concepts/modeling/geometry/geometry_splineobject_s26.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ def main(doc: c4d.documents.BaseDocument) -> None:
262262
doc: The active document.
263263
"""
264264
# Run the example constructing the two spline objects.
265-
linSpline, bezSpline = ConstructSplineObject(doc)
265+
linSpline, bezSpline = ConstructSplineObject()
266266

267267
# Insert both spline objects into the document wrapped into an undo.
268268
if not doc.StartUndo():

scripts/04_3d_concepts/simulation/particle_group_object_2024_4.py

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,6 @@
1010
particle group object, press play to simulate the particles, and then run the script. The script
1111
will print selected particle data and create null objects representing these particles in the scene.
1212
13-
Note:
14-
- The method ParticleGroupObject.GetAttributeChannelData() is currently bugged for the color and
15-
alignment channels. We must use here the specialized functions GetParticleColorsR and
16-
GetParticleAlignmentsR. This will be fixed in a future version of Cinema 4D.
17-
- The setter c4d.Quaternion.v is currently bugged. This will be fixed in a future version of
18-
Cinema 4D. But this makes it currently impossible to construct a c4d.Quaternion from the
19-
maxon::Quaternion32 data exposed by the particle system. As a workaround, we can construct
20-
a particle alignment from the particle velocity.
21-
2213
See Also:
2314
- Python Software Foundation (2024). struct module: Formatting Characters.
2415
url: https://docs.python.org/3/library/struct.html#format-characters
@@ -30,12 +21,13 @@
3021
__version__ = "2024.4"
3122

3223
import c4d
24+
import maxon
3325
import pprint
3426
import struct
3527
import mxutils
3628

3729
doc: c4d.documents.BaseDocument # The currently active document.
38-
op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`.
30+
op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`.
3931

4032

4133
def GetParticleInfo(op: c4d.ParticleGroupObject, channel: str) -> dict:
@@ -160,52 +152,38 @@ def GetParticleColors(op: c4d.ParticleGroupObject) -> list[c4d.Vector4d]:
160152
161153
More of the same, but here we have a four component vector.
162154
"""
163-
# GetAttributeChannelData is currently bugged for the color channel, we must use the specialized
164-
# function here for now.
165-
166-
# colBuffer: memoryview = op.GetAttributeChannelData("net.maxon.particles.attribute.color")
167-
colBuffer: memoryview = op.GetParticleColorsR()
155+
colBuffer: memoryview = op.GetAttributeChannelData("net.maxon.particles.attribute.color")
168156
if colBuffer is None:
169157
return []
170158

171159
info: dict = GetParticleInfo(op, "net.maxon.particles.attribute.color")
172160
stride: int = info["Data Stride in bytes"]
173161

174-
return [c4d.Vector4d(*struct.unpack_from("ffff", colBuffer, i))
162+
return [maxon.ColorA(*struct.unpack_from("ffff", colBuffer, i))
175163
for i in range(0, len(colBuffer), stride)]
176164

177165

178166
def GetParticleAlignments(op: c4d.ParticleGroupObject) -> list[tuple]:
179-
"""Unpacks the particle alignments.
167+
"""Unpacks the particle alignments.
180168
181169
The alignments are expressed as maxon.Quaternion32. Note that this type is NOT identical to
182170
c4d.Quaternion.
183171
"""
184-
# GetAttributeChannelData is currently bugged for the alignment channel, we must use the
185-
# specialized function here for now.
186-
187-
# alignBuffer: memoryview = op.GetAttributeChannelData("net.maxon.particles.attribute.alignments")
188-
alignBuffer: memoryview = op.GetParticleAlignmentsR()
172+
alignBuffer: memoryview = op.GetAttributeChannelData("net.maxon.particles.attribute.alignments")
189173
if alignBuffer is None:
190174
return []
191175

192176
info: dict = GetParticleInfo(op, "net.maxon.particles.attribute.alignments")
193177
stride: int = info["Data Stride in bytes"]
194178

195-
# Unpack the quaternion data from the buffer into a list of quadruples. It is currently not
196-
# possible to construct a c4d.Quaternion from the data, as Quaternion.v is currently bugged.
197-
# This will be fixed in a future version of Cinema 4D.
179+
# Unpack the quaternion data from the buffer into a list of quadruples.
198180
quaternions: list[tuple] = []
199181
for i in range(0, len(alignBuffer), stride):
200182
x, y, z, w = struct.unpack_from("ffff", alignBuffer, i)
201-
quaternions.append((x, y, z, w))
202-
203-
# In a future version of Cinema 4D, the following code will work. SetAxis cannot be used to
204-
# set Quaternion.v, and .w directly.
205-
# quat: c4d.Quaternion = c4d.Quaternion()
206-
# quat.v = c4d.Vector(x, y, z)
207-
# quat.w = w
208-
# quaternions.append(quat)
183+
quat: c4d.Quaternion = c4d.Quaternion()
184+
quat.v = c4d.Vector(x, y, z)
185+
quat.w = w
186+
quaternions.append(quat)
209187

210188
return quaternions
211189

@@ -242,11 +220,6 @@ def main() -> None:
242220

243221
# Construct global transforms for the first five particles and insert null objects for them. See
244222
# Python API Matrix manual for details of how to construct a matrix from a position and a vector.
245-
# It is currently not possible to construct frames for the "true" alignments of particles, as
246-
# the Quaternion.v property is bugged. This will be fixed in a future version of Cinema 4D. This
247-
# approach is flawed as constructing a frame from one vector lacks information for one of the
248-
# three degrees of freedom. The chosen up vector will impact the banking of particles.
249-
250223
transforms: list[tuple[c4d.Vector, c4d.Vector]] = list(zip(positions, velocities))
251224
length: int = 5 if len(transforms) >= 5 else len(transforms)
252225
eps: float = 1E-5

scripts/05_modules/node/create_redshift_nodematerial_2024.py

Lines changed: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"""
1414
__author__ = "Ferdinand Hoppe"
1515
__copyright__ = "Copyright (C) 2023 MAXON Computer GmbH"
16-
__date__ = "01/09/2023"
16+
__date__ = "04/06/2024"
1717
__license__ = "Apache-2.0 License"
1818
__version__ = "2024.0.0"
1919

@@ -37,45 +37,69 @@ def main() -> None:
3737
# mix node. These and all other node IDs can be discovered in the node info overlay in the
3838
# bottom left corner of the Node Editor. Open the Cinema 4D preferences by pressing CTRL/CMD + E
3939
# and enable Node Editor -> Ids in order to see node and port IDs in the Node Editor.
40+
idOutputNode: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.node.output")
41+
idStandardMaterial: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial")
4042
idTextureNode: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler")
4143
idMixNode: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.rscolormix")
42-
idRsStandardMaterial: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial")
4344

44-
# Instantiate a material, get its node material and the graph for the RS material space.
45+
# Instantiate a material, get its node material, and add a graph for the RS material space.
4546
material: c4d.BaseMaterial = c4d.BaseMaterial(c4d.Mmaterial)
4647
if not material:
4748
raise MemoryError(f"{material = }")
4849

4950
nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
50-
graph: maxon.GraphModelRef = nodeMaterial.CreateDefaultGraph(
51-
maxon.Id("com.redshift3d.redshift4c4d.class.nodespace"))
51+
redshiftNodeSpaceId: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
52+
graph: maxon.GraphModelRef = nodeMaterial.CreateEmptyGraph(redshiftNodeSpaceId)
5253
if graph.IsNullValue():
53-
raise RuntimeError("Could not add RS graph to material.")
54-
54+
raise RuntimeError("Could not add Redshift graph to material.")
55+
56+
# Open an undo operation and insert the material into the document. We must do this before we
57+
# modify the graph of the material, as otherwise the viewport material will not correctly display
58+
# the textures of the material until the user manually refreshes the material. It is also
59+
# important to insert the material after we added the default graph to it, as otherwise we will
60+
# end up with two undo steps in the undo stack.
61+
if not doc.StartUndo():
62+
raise RuntimeError("Could not start undo stack.")
63+
5564
doc.InsertMaterial(material)
56-
c4d.EventAdd()
65+
if not doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, material):
66+
raise RuntimeError("Could not add undo item.")
5767

58-
# Attempt to find the core material node contained in the default graph setup.
59-
result: list[maxon.GraphNode] = []
60-
maxon.GraphModelHelper.FindNodesByAssetId(graph, idRsStandardMaterial, True, result)
61-
if len(result) < 1:
62-
raise RuntimeError("Could not find RS Standard node in material.")
63-
standardNode: maxon.GraphNode = result[0]
68+
# Define the user data for the transaction. This is optional, but can be used to tell the Nodes
69+
# API to add the transaction to the current undo stack instead of creating a new one. This will
70+
# then have the result that adding the material, adding the graph, and adding the nodes will be
71+
# one undo step in the undo stack.
72+
userData: maxon.DataDictionary = maxon.DataDictionary()
73+
userData.Set(maxon.nodes.UndoMode, maxon.nodes.UNDO_MODE.ADD)
6474

6575
# Start modifying the graph by opening a transaction. Node graphs follow a database like
6676
# transaction model where all changes are only finally applied once a transaction is committed.
67-
with graph.BeginTransaction() as transaction:
77+
with graph.BeginTransaction(userData) as transaction:
78+
79+
# Add the output, i.e., the terminal end node of the graph, as well as a standard material
80+
# node to the graph.
81+
outNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idOutputNode)
82+
materialNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idStandardMaterial)
83+
6884
# Add two texture nodes and a blend node to the graph.
6985
rustTexNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idTextureNode)
7086
sketchTexNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idTextureNode)
7187
mixNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idMixNode)
7288

89+
# Get the input 'Surface' port of the 'Output' node and the output 'Out Color' port of the
90+
# 'Standard Material' node and connect them.
91+
surfacePortOutNode: maxon.GraphNode = outNode.GetInputs().FindChild(
92+
"com.redshift3d.redshift4c4d.node.output.surface")
93+
outcolorPortMaterialNode: maxon.GraphNode = materialNode.GetOutputs().FindChild(
94+
"com.redshift3d.redshift4c4d.nodes.core.standardmaterial.outcolor")
95+
outcolorPortMaterialNode.Connect(surfacePortOutNode)
96+
7397
# Set the default value of the 'Mix Amount' port, i.e., the value the port has when no
7498
# wire is connected to it. This is equivalent to the user setting the value to "0.5" in
7599
# the Attribute Manager.
76100
mixAmount: maxon.GraphNode = mixNode.GetInputs().FindChild(
77101
"com.redshift3d.redshift4c4d.nodes.core.rscolormix.mixamount")
78-
mixAmount.SetDefaultValue(0.5)
102+
mixAmount.SetPortValue(0.5)
79103

80104
# Set the path sub ports of the 'File' ports of the two image nodes to the texture URLs
81105
# established above. Other than for the standard node space image node, the texture is
@@ -86,8 +110,8 @@ def main() -> None:
86110
"com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0").FindChild("path")
87111
pathSketchPort: maxon.GraphNode = sketchTexNode.GetInputs().FindChild(
88112
"com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0").FindChild("path")
89-
pathRustPort.SetDefaultValue(urlTexRust)
90-
pathSketchPort.SetDefaultValue(urlTexSketch)
113+
pathRustPort.SetPortValue(urlTexRust)
114+
pathSketchPort.SetPortValue(urlTexSketch)
91115

92116
# Get the color output ports of the two texture nodes and the color blend node.
93117
rustTexColorOutPort: maxon.GraphNode = rustTexNode.GetOutputs().FindChild(
@@ -102,7 +126,7 @@ def main() -> None:
102126
"com.redshift3d.redshift4c4d.nodes.core.rscolormix.input1")
103127
mixInput2Port: maxon.GraphNode = mixNode.GetInputs().FindChild(
104128
"com.redshift3d.redshift4c4d.nodes.core.rscolormix.input2")
105-
stdBaseColorInPort: maxon.GraphNode = standardNode.GetInputs().FindChild(
129+
stdBaseColorInPort: maxon.GraphNode = materialNode.GetInputs().FindChild(
106130
"com.redshift3d.redshift4c4d.nodes.core.standardmaterial.base_color")
107131

108132
# Wire up the two texture nodes to the blend node and the blend node to the BSDF node.
@@ -113,8 +137,9 @@ def main() -> None:
113137
# Finish the transaction to apply the changes to the graph.
114138
transaction.Commit()
115139

116-
# Insert the material into the document and push an update event.
117-
doc.InsertMaterial(material)
140+
if not doc.EndUndo():
141+
raise RuntimeError("Could not end undo stack.")
142+
118143
c4d.EventAdd()
119144

120145
if __name__ == "__main__":

0 commit comments

Comments
 (0)