images for the material shaders not assigned to the specified shader
-
Hello everyone.
I am new to C4D scripts.
I may have posted this message in the wrong section of this forum, please let me know if that happened.I want to create a script to get a material with certain properties and assign it to an object selected in the scene.
For the Brightness and Displacement channels, images from the disk are added.import c4d from c4d import storage def main(): # doc = c4d.documents.BaseDocument # Step 1: Create a new material material = c4d.BaseMaterial(c4d.Mmaterial) # Creating new material material.SetName("MyMat") # Assigning a name to a material doc.InsertMaterial(material) # type: ignore # Adding material to a document # Step 2: Disable the Color Channel material[c4d.MATERIAL_USE_COLOR] = False # Step 3: Disable the Reflectivity channel material[c4d.MATERIAL_USE_REFLECTION] = False #Step 4: Enable the Brightness channel material[c4d.MATERIAL_USE_LUMINANCE] = True # Step 5: Select a texture file for the Brightness channel luminance_texture_path = storage.LoadDialog(type=c4d.FILESELECTTYPE_IMAGES, title="Select a texture for the Brightness channel") if luminance_texture_path: # Checking if the user has selected a file luminance_shader = c4d.BaseShader(c4d.Xbitmap) # Create a shader for the image luminance_shader[c4d.BITMAPSHADER_FILENAME] = luminance_texture_path # Assigning a path to a file material[c4d.MATERIAL_LUMINANCE_SHADER] = luminance_shader # Assigning a shader to a material # Step 6: Enable the DISPLACEMENTt channel material[c4d.MATERIAL_USE_DISPLACEMENT] = True # Step 7: Select a texture file for the Displacement channel displacement_texture_path = storage.LoadDialog(type=c4d.FILESELECTTYPE_IMAGES, title="Select a texture for the Displacement channel") if displacement_texture_path: # Checking if the user has selected a file displacement_shader = c4d.BaseShader(c4d.Xbitmap) # Create a shader for the image displacement_shader[c4d.BITMAPSHADER_FILENAME] = displacement_texture_path # Assigning a path to a file material[c4d.MATERIAL_DISPLACEMENT_SHADER] = displacement_shader # Assigning a path to a file # Updating the material to apply the changes material.Update(True, True) # Step 8: Assign material to selected objects selected_objects = doc.GetActiveObjects(c4d.GETACTIVEOBJECTFLAGS_0) # type: ignore # Get a list of selected objects if selected_objects: # Checking if objects are selected for obj in selected_objects: texture_tag = c4d.TextureTag() # Create a new texture tag texture_tag.SetMaterial(material) # Assigning the material to the tag obj.InsertTag(texture_tag) # Add a tag to the object # Updating the scene c4d.EventAdd() # Script running if __name__ == '__main__': main()
Why are the selected images for the material shaders not assigned to the specified shader?
edit (@ferdinand):
@lednevandrey said:Script for S4D 2023.2
-
Hello @lednevandrey,
Welcome to the Maxon developers forum and its community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.
- Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
- Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
- Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.
About your First Question
The issue with your script is that you never insert your bitmap shaders. A shader must be inserted into a scene graph just like any other node before it can be used. Referencing a shader in a shader parameter does not replace inserting a shader. While for shaders this might be a bit surprising for beginners, one can look at it from an generator object angle to gain clarity. You would not expect to be able to link a that cube object into the document first. From this angle it is a bit more obvious that shaders are not any different and must be inserted before being linked anywhere.
In most cases shaders are owned by the material which use them and inserted with c4d.BaseList2D.InsertShader(). But there is some complexity to how shaders are structured when shaders own shaders. See Base Shader Manual: Access and Structure (C++) for details. The manual is for C++ but also applies to Python.
So, for the displacement part it should be for example something like this.
import c4d import mxutils # ... path: str = storage.LoadDialog(type=c4d.FILESELECTTYPE_IMAGES, title="Select a texture for the Displacement channel") if path: # The mxutils.CheckType call is here so that we can be sure that allocation did not fail. Then # we insert the shader into the document by attaching it to the material which itself # is being inserted into the document. shader: c4d.BaseShader = mxutils.CheckType(c4d.BaseShader(c4d.Xbitmap) material.InsertShader(shader) # Now we can link the file in the shader, and shader in the material. A shader can only # be linked/used once by a material in the classic APU of Cinema 4D. So, if we wanted # to use #path twice, we would have insert a second shader for the second usage. shader[c4d.BITMAPSHADER_FILENAME] = path material[c4d.MATERIAL_DISPLACEMENT_SHADER] = shader
Cheers,
Ferdinand -
Hi Ferdinand!
Thank you for your attention to my problem.
I took into account your comments and reworked the script. But when I run it, the console shows an error:ModuleNotFoundError:No module named 'mxutils'
How to install a module 'mxutils' in the C4D environment?
-
Hey @lednevandrey,
Oh, my bad, I did not see that you were using 2023.
mxutils
is a feature of Cinema 4D 2024 and onwards. You cannot install it on 2023. Just replace lines like:shader: c4d.BaseShader = mxutils.CheckType(c4d.BaseShader(c4d.Xbitmap) material.InsertShader(shader)
with
shader: c4d.BaseShader = c4d.BaseShader(c4d.Xbitmap) if shader is None: raise MemoryError("Failed to allocate shader.") material.InsertShader(shader)
you technically also can write just
shader: c4d.BaseShader = c4d.BaseShader(c4d.Xbitmap) material.InsertShader(shader)
when you are fine with a bit cryptic error when shader allocation fails;
BaseList2D.InsertShader
will then complain that it cannot deal with being passed aNoneType
for itsshader
argument. Type checking only exists in Python for code to fail more gracefully. mxutils.CheckType just makes this just a little bit less cumbersome to do.Cheers,
Ferdinand -
Hi Ferdinand!
Thank you. It worked. -
Great to hear that you found your solution! Do not hesitate to ask more questions when you run into problems while exploring our Python API. But just as a heads up, we prefer users opening new topics for new questions. For details see the Support Procedures linked above.
Cheers,
Ferdinand