PDA

View Full Version : Python: getting object type of materials?


cgbeige
10-09-2012, 10:58 PM
I'm trying to wrap my head around scripting creation of V-Ray for Cinema 4D materials and have no idea how to get the name of these types of nodes. How would I go about listing all of the Python-scriptable names of a material and connections for an existing shader? I can't just use this:

mat = c4d.BaseMaterial(c4d.Mmaterial)
mat.SetName( textureName )

Per-Anders
10-09-2012, 11:35 PM
Hi, the SDK docs themselves cover all the members of the various object types, you can find them at plugincafe.com and also oline here on my server http://peranders.com/w/index.php?title=Python/C4DSDK you can also use the standard Python methods to list all available members of a library, the output will go to the console (Script->Console)

For the names of the parameters that appear in the Attributes window or Material Manager there are several ways to get them. The easiest is to simply drag them into the Script Editor, they will appear fully formed. The next is to drag to a console (Script->Console) and drag in there, after that you can use the Script Log (Script->Script Log) and just edit parameters to see the code that's created there showing you editing values. And finally if you want to see the whole list for an object you can find it's resource file and look through them there, the resource files for VRay would be in the plugins res/description/ folder, the .h and .res files are humanly readable text files with all the values.

Per-Anders
10-09-2012, 11:45 PM
Now for the specifics for your question.

Cinema uses a reference/pointer Object/Node based system rather than a name based system for access to nodes. The shader fields in a material are smartlinks (so called BaseLink in C4D API parlance), so when you access them you actually get an inferred direct access to the shader so e.g.

def main():
mat = doc.GetFirstMaterial()
if mat:
colorShader = mat()[c4d.MATERIAL_COLOR_SHADER]
print(colorShader.GetName())

Will if you've set up a scene with a material and a shader in the color slot result in printing the name of the shader in that slot.

Now because it's a link you don't set the actual slot to the instance of a new shader alone. C4D uses hierarchical linked lists (it calls them BaseList2D objects) to store nodes in the scene, the shader has to be added in to the scene first, and then the ShaderLink (the parameter in the material) gets set. Here's a direct link on how to allocate and insert a shader into the document :

http://www.thirdpartyplugins.com/python/modules/c4d/C4DAtom/GeListNode/BaseList2D/index.html?highlight=shader#BaseList2D.InsertShader

After doing that you'd set the parameter to the shader, the smartpointer will be set and you should be good to go.

Per-Anders
10-10-2012, 12:09 AM
Oh and before I forget, you can iterate through all the entries on an object using it's basecontainer.

bc = op.GetData()
or
bc = op.GetDataInstance()

then you can just do a standard iteration through it to see what's in there, or use the type() call to make sure that the entry is either None (most likely a link) or a c4d.BaseShader.

e.g.


import c4d
from c4d import gui
from types import *

def main():
mat = doc.GetFirstMaterial()
bc = mat.GetData()
print(bc)
for (i, item) in bc:
try:
if type(item) == NoneType or type(item) == c4d.BaseShader:
print(i, item, type(item))
except:
print("unknown type\n")

if __name__=='__main__':
main()


Will print out the relevant entries in the BaseContainer to shaders without you having to manually enter each ID.

Per-Anders
10-10-2012, 12:47 AM
So to conclude all of that.

Objects are linked to by an id in the obejcts GUI also known as it's "description", and the data is stored by the id in a dictionary called the BaseContainer.

The BaseContainer is iteratable (unlike the object itself), just remmeber to use try and except as certain data types are unknown to python in there.

Depending on what you mean by "Python Scriptable Names" you can get the Container ID's, the Description ID's (and their names), the Nodes themselves (Shaders/Materials etc), and the types of objects that those nodes and entries are.

However as Cinema uses a reference model rather than a unique name model (names can be the same on many objects) only the direct reference is of use when accessing data (although it's possible to find objects by name).

Accessing an objects string name is as simple as name = object.GetName() or object.SetName("name"). Getting it's type is possible via type(object), object.GetType() for the integer type and object.GetTypeName() for the enumerated typename constant e.g. Mmaterial. This is likely the method you'd use to identify vray materials, while object.GetType() is more likely the method you'd use to identify a specific shader type that you don't already know the typename for.

As objects are handled by reference but there is no explicit dependency system in Cinema you must iterate through the BaseContainer or it's Description of each object recursively in order to build a complete picture of the linkage when dealing with materials. This is especially true as materials may store shaders on an internal linked list that you could iterate through, but there's no direct way of knowing which channel the shader is in on the material without going and checking the materials channels to see (as the link is one way in the material only).

Hope this helps and answers your question thoroughly.

tcastudios
10-10-2012, 01:11 AM
...there's no direct way of knowing which channel the shader is in on the material without going and checking the materials channels to see (as the link is one way in the material only).

Hope this helps and answers your question thoroughly.
Tremendous info Per Anders!
You just saved me a night of trying to recode a material system
where I need to know what shader in a shader tree (shaders within
layers, colorizers etc) belong to what material channel (Color, Luminance etc)

Right now I recurse iterate all shaders in the material and for each
shader I climb up (while sdh.GetUp()) to get the top shader and then
compere it with the channels shader. It works fine but I wondered if there was
a better way. As I now understand it from your post, this is how I need to do it
as the linking is one way. Correct?

Your spilled beans are always golden and at all times spur into multiple of
solutions in many ways.

(Sorry for the hi jack)

Cheers
Lennart

Per-Anders
10-10-2012, 01:26 AM
Yes that's correct, the BaseLink/ShaderLink links in the GUI and BaseContainer are one way. The BaseList2D hierarchy though (the stuff wher eyou call GetUp(), GetPred, GetNext etc) is navigable in all directions as the node only exists the once in the tree, but it's not linked directly to the GUI entry there.

cgbeige
10-10-2012, 03:16 AM
thanks for the help. I'll give this a try tomorrow.

CGTalk Moderation
10-10-2012, 03:16 AM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.