Wow @ferdinand , super cool answer as always, super busy after ask, will get back and learning asap.
Cheers~
DunHou
Wow @ferdinand , super cool answer as always, super busy after ask, will get back and learning asap.
Cheers~
DunHou
Hi community,
First touch of modeling, I wonder how to create uv when importing object, for example, we have a polygon object in OM, how can we construct a new polygon with uv as same as the source?
I had read the awesome geometry_polygonobject_s26.py example and had a basic knowledge of polygon.
Cheers~
DunHou
Hi @Neekoe ,
There are many ways to save data, like c4d.storage.HyperFile
or json-like stuff
, or you can use WorldContainer
, I like to use json for my plugins, but for more "c4d-like", you can use WorldContainer.
Cheers~
DunHou
import c4d
YOUR_UID: int = 123456789
# Setting id, better to use a unique id, but not necessary
SETTING1: int = 1000
SETTING2: int = 1001
def main() -> None:
# Create a new BaseContainer for storing the plugin data
bcWorld: c4d.BaseContainer = c4d.GetWorldContainerInstance()
bcPlug: c4d.BaseContainer = c4d.BaseContainer()
bcPlug.SetBool(SETTING1, True)
bcPlug.SetInt32(SETTING2, 10)
bcWorld.SetContainer(YOUR_UID, bcPlug)
# Get the BaseContainer from the world container
bcPlug: c4d.BaseContainer = bcWorld.GetContainerInstance(YOUR_UID)
print(bcPlug.GetBool(SETTING1))
print(bcPlug[SETTING2])
if __name__=='__main__':
main()
Hi Neekoe,
Those look like a very old code snippet in boghma community website (closed and turn to discord).
If you want to use TreeView more easily and less codes, please check the new easy_tree
in our boghma lib, which I used in my Octane Lister plugin.
btw, in boghma library, we have two examples toshow how to use easey_tree.
Have fun.
Cheers~
DunHou
Hi @mia-elisenberg ,
If you want to add c4d.CKey
to a maxon.GraphNode
object, you need to:
maxon.GraphNode
, you need to get his host object aka the true node, the Standard Surface BRDF node, also it is a maxon.GraphNode
.GetBaseListForNode
to get the c4d.BaseList2D
BTW, you can find a topic which is a python library for renderers, in General Talk. I had added those methods but not pushed yet, will be pushed asap, you can take a look if needed.
Hope it helps.
Cheers~
DunHou
import c4d
import maxon
doc: c4d.documents.BaseDocument # The currently active document.
op: c4d.BaseObject | None # The primary selected object in `doc`. Can be `None`.
nodespaceId = "com.redshift3d.redshift4c4d.class.nodespace"
standardBRDF = "com.redshift3d.redshift4c4d.nodes.core.standardmaterial"
def main() -> None:
doc: c4d.documents.BaseDocument = c4d.documents.GetActiveDocument()
for material in doc.GetActiveMaterials():
nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
if not nodeMaterial.HasSpace(nodespaceId):
continue
graph: maxon.GraphModelInterface = nodeMaterial.GetGraph(nodespaceId)
nimbusRef: maxon.NimbusBaseRef = material.GetNimbusRef(nodespaceId)
result: list[maxon.GraphNode] = []
maxon.GraphModelHelper.FindNodesByAssetId(graph, standardBRDF, True, result)
if not result:
continue
# assume we just have one brdf node in this gragh
brdf_node: maxon.GraphNode = result[0]
opacityPort: maxon.GraphNode = brdf_node.GetInputs().FindChild('com.redshift3d.redshift4c4d.nodes.core.standardmaterial.opacity_color')
# try to find the BaseList2D for the node, this host on the true node in gragh
# 'opcacity' is a port, we have to get the host node: ie. the true node
parentNode: maxon.GraphNode = opacityPort.GetAncestor(maxon.NODE_KIND.NODE)
parentNodePath = parentNode.GetPath()
opacityPortBL2D: c4d.BaseList2D = nodeMaterial.GetBaseListForNode(nodespaceId, parentNodePath)
# create track and add key
opacityPortDescID: c4d.DescID = nimbusRef.GetDescID(opacityPort.GetPath())
track: c4d.CTrack = c4d.CTrack(opacityPortBL2D, opacityPortDescID)
opacityPortBL2D.InsertTrackSorted(track)
curve: c4d.CCurve = track.GetCurve()
key = c4d.CKey()
track.FillKey(doc, opacityPortBL2D, key)
ctime: c4d.BaseTime = c4d.BaseTime(doc.GetTime().GetFrame(doc.GetFps()), doc.GetFps())
key.SetValue(curve, 50.0)
key.SetTime(curve, ctime)
curve.InsertKey(key)
c4d.EventAdd()
if __name__ == '__main__':
main()
you need to change the id of you want
PREFS_REDSHIFT_MATPREVIEW_MODE_BACKGROUND: int = 3
PREFS_REDSHIFT_MATPREVIEW_MODE_IDLE: int = 1
PREFS_REDSHIFT_MATPREVIEW_MODE_OFF: int = 0
PREFS_REDSHIFT_MATPREVIEW_MODE_SUSPEND: int = 2
Hi @RTF , the c4d.PREFS_REDSHIFT_MATPREVIEW_MODE
doesn't seem stored in Redshift VideoPost, is this code generated by GPT or something similar?
Back to the topic, this parameter is stored in the Redshift tab of the preferences node. You should get the redshift preference node and then set the parameter.
If you installed Renderer lib or boghma hub, you can use this:
import c4d
from Renderer import Redshift
if __name__ == '__main__':
Redshift.SetMaterialPreview(preview_mode = 1)
or just few codes:
import c4d
ID_PREFERENCES_NODE = 465001632 # Prefs ID
def GetPreference() -> c4d.BaseList2D:
"""
Get the Redshift preferenc.
"""
prefs: c4d.plugins.BasePlugin = c4d.plugins.FindPlugin(ID_PREFERENCES_NODE)
if not isinstance(prefs, c4d.BaseList2D):
raise RuntimeError("Could not access preferences node.")
descIdSettings = c4d.DescID(
c4d.DescLevel(1036220, 1, 465001632), # pref ID Redshift
c4d.DescLevel(888, 133, 465001632)
)
return prefs[descIdSettings]
if __name__ == '__main__':
redshift_pref: c4d.BaseList2D = GetPreference()
redshift_pref[c4d.PREFS_REDSHIFT_MATPREVIEW_MODE] = 1
Cheers~
DunHou
Hi @m_adam ,
Thanks for your reply, it worked well.
But I still have some doubts about the usage of Node Path, such as how to calculate the port through NodePath instead of concatenating strings (which can easily lead to misoperation).
There are few examples and answers for NodePath on the forum, and it seems that people rarely use it. Do you have any insights on this, or in other words, what usage scenarios do you think NodePath is more suitable for.
Cheers~
DunHou
Hi community,
I notice GraphModelInterface.GetNode
method seems broken in R2025.0.2, and also the ToString()
seems broken too. Is my usage incorrect?
Cheers~
DunHou
btw: R2025 tag is missing in forum
import c4d
import maxon
import typing
doc: c4d.documents.BaseDocument # The active document.
def main():
for material in doc.GetMaterials():
nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
if not nodeMaterial.HasSpace("com.redshift3d.redshift4c4d.class.nodespace"):
continue
graph: maxon.GraphModelRef = nodeMaterial.GetGraph("com.redshift3d.redshift4c4d.class.nodespace")
root: maxon.GraphNode = graph.GetViewRoot()
spaceContext = root.GetValue(maxon.nodes.NODESPACE.NodeSpaceContext)
nodeSpaceId = spaceContext.Get(maxon.nodes.NODESPACE.SPACEID)
spaceData = maxon.NodeSpaceHelpersInterface.GetNodeSpaceData(nodeSpaceId)
assetId = spaceData.Get(maxon.nodes.NODESPACE.IMAGENODEASSETID)
inTexturePortId = spaceData.Get(maxon.nodes.NODESPACE.IMAGENODEPORTS)[1]
for node in root.GetInnerNodes(maxon.NODE_KIND.NODE, False):
assetId: maxon.Id = node.GetValue("net.maxon.node.attribute.assetid")[0]
if assetId != maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler"):
continue
pathPort: maxon.GraphNode = graph.GetNode(node.GetPath() + '>' + str(inTexturePortId))
url: str | None = pathPort.GetPortValue()
print (f"{pathPort}: {url}")
if __name__ == "__main__":
main()
Yes @Mats , it will 'scan' the material and convert to a description, then convert back to material.
I want to achieve material conversion between different renderers, which may seem like a very labor-intensive task, but the good news is that I have taken the first step in implementing it.
And thanks for your like for boghma.
Cheers~
DunHou
Graph Description is great and I love it.
I wrote a rough trace to generate descriptions from selected material and reconstruct a new material based on the descriptions.
I have more plans for this, but due to busy artistic work I had to suspend this project. It's really regrettable.
But I have more plans and ideas about this. Perhaps before I achieve my idea, the omnipotent @ferdinand already has these features built-in, who knows
Cheers~
DunHou
Wow @m_adam , thanks for that, I'll check this after work!
Hey @m_adam ,
I think this is equal to port.GetValue('value'), and it will get the current value for the node but not the default value.
if I change the roughness to 0.5, they both return 0.5 but bot 0 for default.
Cheers~
DunHou
Lovely tips! I'm a big fan of type hint (deeply influenced by the Ferdinand code style while studying)
Cheers~
DunHou
@tx3008 You just modify the rd but not rendering anythings.
Hey @tx3008 ,
You need check the Octane BaseVideoPost
to get the data container.
You can change the render settings and use RenderDocument
to render images.
In case if you didn't want to use my extra libs, the GetVideoPost
is not complicated.
Cheer~
DunHou
# 获取渲染器VideoPost
def GetVideoPost(document: c4d.documents.BaseDocument = None, videopost: int = ID_REDSHIFT) -> Optional[c4d.documents.BaseVideoPost]:
"""
Get the videopost of given render engine of filled document.
Args:
document (c4d.documents.BaseDocument, optional): Fill None to check active documents. Defaults to None.
videopost (int, optional): The id of the videopost. Defaults to ID_REDSHIFT.
Returns:
Optional[c4d.documents.BaseVideoPost]: The videopost we get.
"""
if not document:
document = c4d.documents.GetActiveDocument()
rdata: c4d.documents.RenderData = document.GetActiveRenderData()
vpost: c4d.documents.BaseVideoPost = rdata.GetFirstVideoPost()
theVp: c4d.documents.BaseVideoPost = None
while vpost:
if vpost.GetType() == int(videopost):
theVp = vpost
vpost = vpost.GetNext()
return theVp
import c4d
import Renderer
def main():
OctaneRenderer = Renderer.GetVideoPost(c4d.documents.GetActiveDocument(),
Renderer.ID_OCTANE)
OctaneRenderer[c4d.SET_PASSES_FILEFORMAT] = 10 # Octane PNG, 6 = PNG
OctaneRenderer[c4d.SET_PASSES_SAVE_MAINPASS] = True
OctaneRenderer[c4d.SET_PASSES_ENABLED] = True
OctaneRenderer[c4d.SET_PASSES_SAVEPATH] = "Render/$prj/$prj"
c4d.EventAdd()
if __name__=='__main__':
main()
Hey @m_adam ,
Thanks for that quick answer and a sweet codes for GraphModelInterface.GetModificationStamp
I took a look at this, but these might be some details I still need.
For example, I get a material from others. I want to check the material:
Can we get the this stored value here, aka the default value we create the new material?
Cheers~
DunHou
Hi community,
I would like to know if there is a way to obtain the default value of a port(maxon.GraphNode), then catch which port in the graph is changed. I'm not sure if I missed something, but I didn't find any relevant methods in the document.
Any suggestions are welcomed!
Cheers~
DunHou