How to use GenerateDragArray() with Node Editor?
-
Hi community,
I want to simulate the drag and create a <Image> node inside node editor, like the picture below but from treeview, but If I use AddChild() it will automictically create a node when I select but not drag.
How can I do this?
def GetDragType(self, root, userdata, obj: TreeViewBaseObject): if not userdata.prop["use_drag"]: return c4d.NOTOK # this will crash # if c4d.IsCommandChecked(CID_NODE_EDITOR) and obj.asset_type == "image": # return c4d.DRAGTYPE_FILENAME_IMAGE return c4d.DRAGTYPE_ATOMARRAY def GenerateDragArray(self, root, userdata, obj: TreeViewBaseObject): shader = c4d.BaseShader(c4d.Xbitmap) # basic shader shader[c4d.BITMAPSHADER_FILENAME] = obj.path elements = [shader]
Cheers~
DunHou -
Hi @Dunhou, sadly this is not possible out of the box, since the node Editor is a new dialog relying on a complete different technology then the old one. And this new UI does not use the same drag and drop architecture than the rest of Cinema 4D which is sadly not compatible between each other yet.
I will ping the responsible developer to see if there is any workaround, but I doubt.
In the meantime, you can manually create the texture node like so:
import c4d import maxon # --- Temporary add NodeSpaceHelpersInterface.GetNodeSpaceData() # This is going to be added in 2024.2 as maxon.NodeSpaceHelpersInterface.GetNodeSpaceData() -------------------------- @maxon.interface.MAXON_INTERFACE_NONVIRTUAL(maxon.consts.MAXON_REFERENCE_STATIC, "net.maxon.nodes.interface.nodespacehelpers") class NodeSpaceHelpersInterface: @staticmethod @maxon.interface.MAXON_STATICMETHOD("net.maxon.nodes.interface.nodespacehelpers.GetNodeSpaceData") def GetNodeSpaceData(spaceId): pass # --- End of fix ----------------------------------------------------------------------------------- def main() -> None: mat = doc.GetFirstMaterial() if not mat: return nMat = mat.GetNodeMaterialReference() # Get the first available nimbus ref nimbusRefs = nMat.GetAllNimbusRefs() if not nimbusRefs: raise ValueError("Cannot retrieve the nimbus ref for that node space") nimbusRef = nimbusRefs[0][1] graph = nimbusRef.GetGraph() if graph is None: raise ValueError("Cannot retrieve the graph of this nimbus ref") # Retrieve the nodespace id from the graph spaceContext = graph.GetRoot().GetValue(maxon.nodes.NODESPACE.NodeSpaceContext) nodeSpaceId = spaceContext.Get(maxon.nodes.NODESPACE.SPACEID) # Retrieve the nodespace data, and its default picture node with the associated port Id spaceData = NodeSpaceHelpersInterface.GetNodeSpaceData(nodeSpaceId) assetId = spaceData.Get(maxon.nodes.NODESPACE.IMAGENODEASSETID) portData = spaceData.Get(maxon.nodes.NODESPACE.IMAGENODEPORTS) outColorPortId = portData[0] # The result port. inTexturePortId = portData[1] # The URL of the input image. inStartFramePortId = portData[2] # The index of the starting frame. inEndFramePortId = portData[3] # The index of the ending frame. # Add a new default Texture Node, as a Drag and Drop operation will perform texturePath = maxon.Url(r"C:\Users\m_adam\Desktop\maxon_logo.jpg") with graph.BeginTransaction() as transaction: newNode = graph.AddChild(maxon.Id(), assetId) # We need to convert the NodePath to a string, this is a bug that is going to be fixed in 2024.2 inTexturePort = newNode.GetInputs().FindChild(str(inTexturePortId)) inTexturePort.SetDefaultValue(texturePath) outColorPort = newNode.GetOutputs().FindChild(str(outColorPortId)) transaction.Commit() if __name__ == '__main__': main()
Cheers,
Maxime. -
Thanks for you answer @m_adam, I wonder what is the difference between this SpaceData and just use AddChild method, I test it and looks like AddChild().
Cheers~
DunHou -
Hi sorry for the delay,
spaceData
in my code represent a maxon.DataDictionary or maxon equivalent of a python dict. Which have the duty to store various information about a certain nodespace, like which asset need to be used as texture node, which asset id should be used as end node, etc...
So with that's said I do not really understand your question, as AddChild is a method to create a new node within a graph, so for me it's 2 unrelated thing.Cheers,
Maxime. -
sorry for jumping in here. What Maxime glanced a bit over (because he is so deep in the subject matter as a Nodes Team member), is the purpose of what he is doing there; which might have led to confusion.
The Nodes API uses the concept of a node space. It works similar to the concept of a space in math, as for example the idea of a vector space. Just as for the mathematical concept, a node space defines sets of legal operands, i.e., which nodes you can use when you create a node graph for this space.
In his example, Maxime is retrieving the node space ID for a given graph and then accesses information which is tied to that node space (normally
NodeSpaceInterface
, but Maxime is using here a helper data structure instead). ANodeSpaceInterface
has multiple functions, and one of them is storing the settings for that node space, the so called 'space data'. One thing one can find in these space data, is the definition of the so calledIMAGENODEASSETID
. This is the ID of the node which is used in this node space to express a texture/bitmap. If you want to know more details, you can have a look at this C++ manual.So, in short, what Maxime is doing here, is he is abstractly retrieving the template(i.e., type) ID of the node which is used in the node space of a given graph to represent a texture (and also port information). This has the advantage that his code will work for any node space (i.e., render engine). But you could also just hardcode that info and then could just use
AddChild
. But in this case you would have to at least check if the given graph is part of the node space you are targeting (which would be stored inspaceContext
).Cheers,
Ferdinand -
Thanks for that @ferdinand @m_adam ,
yes, Ferdinand is right, Maxime's point is too "pro" for me, and with some maxon decorators I never used in some way mess me up, with Ferdinand's "rookie" like explain that I can understand this more, thanks for your both respected work.
Cheers~
DunHou