<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DescID and DescLevel for Shader Layers and properties]]></title><description><![CDATA[<p dir="auto">So, I am working on a couple things dealing with layer shaders from a Shader Field.</p>
<p dir="auto">1.)	Adding ports to an Object Xpresso Operator with the Shader as the linked object.<br />
2.)	Adding keyframes to an animation track for the Gradient in a Gradient Layer of the Shader.</p>
<p dir="auto">I have had no issue creating XPresso nodes and ports in general. As an example, here is a snippet of how I add ports for object operators:</p>
<pre><code>ObjectNode = NodeMaster.CreateNode(Root,c4d.ID_OPERATOR_OBJECT, insert=None, x=700, y=850)

ObjectNode.SetParameter(c4d.DescID(c4d.GV_OBJECT_OBJECT_ID), Null, c4d.DESCFLAGS_SET_NONE)

ObjectNodeIn = ObjectNode.AddPort(c4d.GV_PORT_INPUT, c4d.DescID(c4d.DescLevel(c4d.ID_BASEOBJECT_GLOBAL_POSITION)), flag=c4d.GV_PORT_FLAG_IS_VISIBLE, message=False)
</code></pre>
<p dir="auto">I can add an object operator, and link the shader to the object operator, but I am having trouble creating the ports using the DescID and DescLevels, simply because I’m not sure what IDs to use in the DescLevel to access the parameters (as shown in the image below).</p>
<p dir="auto">Similarly, I have no problems creating tracks and keys for animated parameters, so long as I know the DescID and DescLevels for those parameters. I have combed through the forum and SDKs, and can’t seem to find how to access the shader layer parameters through this method. I also have no problem creating Shader Layers and accessing their parameters after instantiation, but not how to reference them through the DescLevel method.</p>
<p dir="auto">Here is an example of how I set key and tracks normally:</p>
<pre><code>pid_pmix = c4d.DescID(c4d.DescLevel(c4d.ID_USERDATA, c4d.DTYPE_SUBCONTAINER, 0), c4d.DescLevel(4))

mixtrack = c4d.CTrack(Xpresso, pid_pmix)

Xpresso.InsertTrackSorted(mixtrack)

defkey, dub = doc.GetDefaultKey()

mixcurve = mixtrack.GetCurve()

mixval1 = 0

mixkey1 = defkey.GetClone()

mixkey1.SetValue(mixcurve, mixval1)

mixkey1.SetTime(mixcurve, c4d.BaseTime(46/20))

mixcurve.InsertKey(mixkey1, True
</code></pre>
<p dir="auto">As mentioned above, the only problem I’m having is finding and accessing the DescLevel IDs for the shader layers and their properties for these two scenarios. I am currently working on C4D 2025.2.0. Any advice or direction would be greatly appreciated. Thank you.</p>
<p dir="auto"><img src="/forum/assets/uploads/files/1756867791754-dev-post.png" alt="dev post.PNG" class=" img-fluid img-markdown" /></p>
]]></description><link>http://developers.maxon.net/forum/topic/16321/descid-and-desclevel-for-shader-layers-and-properties</link><generator>RSS for Node</generator><lastBuildDate>Fri, 17 Apr 2026 15:26:26 GMT</lastBuildDate><atom:link href="http://developers.maxon.net/forum/topic/16321.rss" rel="self" type="application/rss+xml"/><pubDate>Wed, 03 Sep 2025 02:52:51 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Thu, 11 Sep 2025 19:06:21 GMT]]></title><description><![CDATA[<p dir="auto"><a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/ferdinand">@<bdi>ferdinand</bdi></a> Thank you for your in-depth analysis. It would have taken me way too long to figure this out lol, especially finding "CUSTOMDATA_BLEND_LIST". The first example seems to work for me, but like you said, it may not be reliable. The Python node example also works great. Again, thank you for your time.</p>
]]></description><link>http://developers.maxon.net/forum/post/76815</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76815</guid><dc:creator><![CDATA[alcol68]]></dc:creator><pubDate>Thu, 11 Sep 2025 19:06:21 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Thu, 11 Sep 2025 14:02:46 GMT]]></title><description><![CDATA[<p dir="auto">Hey <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/alcol68">@<bdi>alcol68</bdi></a>,</p>
<p dir="auto">so, I spent some time with this today, and both the Xpresso and Layer Shader API drive me nuts every time I have to deal with them. The TLDR is here that both Xpresso and the Layer Shader are irregular in their implementations, especially in regards to how they store and access parameters.</p>
<p dir="auto">Find below some narrative code which guides you through all of this.</p>
<p dir="auto">Cheers,<br />
Ferdinand</p>
<h4>Result</h4>
<p dir="auto">The setup expects a material to be selected which has a layer shader in its color channel with a singular transform layer in it. It will then create this Xpresso setup which drives the angle of the transform layer with a user data field in the null object holding the Xpresso setup.</p>
<p dir="auto"><img src="/forum/assets/uploads/files/1757599041220-595958de-534d-40fe-a45e-5d1d973c8ad8-image.png" alt="595958de-534d-40fe-a45e-5d1d973c8ad8-image.png" class=" img-fluid img-markdown" /></p>
<p dir="auto">edit: lol, now I see that also the first example is working in my own screenshot. I have absolutely no idea why. I would still stay away from this. The layer shader internals are a mess.</p>
<h4>Code</h4>
<pre><code class="language-py">"""Explores concepts around driving layer shader parameters from XPresso.

This script assumes that the active material has a layer shader in its color channel. As explained 
below, this code assumes the ID of the first parameter of the first layer in the shader, which does 
not seem to be entirely deterministic.

When push comes to shove, you might have to comment out #Solution.InternalLayerShaderAccess(...) in
#Solution.main() when it throws errors on your system.
"""

import c4d
import mxutils

from c4d.modules.graphview import GvNodeMaster, GvNode, GvPort, XPressoTag
from mxutils import CheckType

doc: c4d.documents.BaseDocument  # The currently active document.
op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.

class Solution:
    """Provides a dummy class so that we can organize our code in a more readable way.
    """
    @staticmethod
    def main() -&gt; None:
        """Called by Cinema 4D when the script is being executed.
        """
        # Get the first material and get its color channel shader, assuming it is a layer shader.
        material: c4d.BaseMaterial = CheckType(doc.GetActiveMaterial())
        shader: c4d.BaseShader = CheckType(material[c4d.MATERIAL_COLOR_SHADER], c4d.LayerShader)

        # Create the XPresso setup used by both examples below and also already add the user data
        # field to the null object used by the second example.
        null: c4d.BaseObject = CheckType(c4d.BaseObject(c4d.Onull))
        doc.InsertObject(null)

        # Create the "Angle" user data field on the null object.
        bc: c4d.BaseContainer = c4d.GetCustomDataTypeDefault(c4d.DTYPE_REAL)
        bc[c4d.DESC_NAME] = "Angle"
        bc[c4d.DESC_UNIT] = c4d.DESC_UNIT_DEGREE
        null.AddUserData(bc)
        null[c4d.ID_USERDATA, 1] = c4d.utils.DegToRad(42.0) # Initial value.

        # Create the XPresso setup.
        tag: XPressoTag = CheckType(null.MakeTag(c4d.Texpresso))
        master: GvNodeMaster = CheckType(tag.GetNodeMaster())

        # Call this first example, you might have to comment this out, as it relies on the discussed
        # magic numbers.
        Solution.InternalLayerShaderAccess(shader, master)
        # Call the second example, which uses a Python node to access the layer shader. This will
        # always work.
        Solution.PythonNodeLayerShaderAccess(shader, null, master)

        c4d.EventAdd()

    @staticmethod
    def InternalLayerShaderAccess(
            shader: c4d.BaseShader, master: GvNodeMaster) -&gt; None:
        """Explores the internal structure of the layer shader data type and how it interfaces with 
        XPresso.

        Args:
            shader: The layer shader to inspect.
            master: The node master of the XPresso tag.
        """
        # You claimed on the forum that this ID worked for you for animating the transform angle of
        # a layer shader.
        #
        # c4d.DescID(c4d.DescLevel(c4d.SLA_LAYER_BLEND), c4d.DescLevel(10120, c4d.DTYPE_REAL))

        # Even when we ignore the concrete numeric offset and replace it with our own, it still does
        # not work for me. Could it be that you used abstracted GeListNode.__set/getitem__ access 
        # instead, i.e., what the console drag and drop generates? Because there this works for me.
        print(shader[c4d.SLA_LAYER_BLEND, 10060])

        # The problem with these IDs is that they are not deterministic from a public API point of
        # view. What holds true, is that data will placed with a stride of 20 (i.e., with 
        # BLEND_DATA_STEP_SIZE) and starts in theory at 10000 (BLEND_DATA_OFFSET). 
        # 
        # But the elements in a layer then do not follow the symbols exposed in the C++ API for the 
        # layer abstraction but are rather just sequentially numbered on a per layer type basis. E.g. 
        # for the transform layer, a parameter set event looks like this:
        #
        # void BlendEffectTransform::SetParameter(Int32 lItem, const DescID &amp;id, ...)
        # {
        #     if (lItem == 0)
        #     {
        #         m_rAngle = t_data.GetFloat();
        #         flags |= DESCFLAGS_SET::PARAM_SET;
        #     }
        #     else if (lItem == 1)
        #     {
        #         m_bMirror = t_data.GetInt32() != 0;
        #         flags |= DESCFLAGS_SET::PARAM_SET;
        #     }
        #     ...
        #
        # Where in the API the angle has actually the id LAYER_S_PARAM_TRANS_ANGLE (2000) and the 
        # mirror has the id LAYER_S_PARAM_TRANS_MIRROR (2001). So, the internal values 0 and 1 are 
        # hardcoded and absolutely ignore the public API symbols. We can of course make here the 
        # observation that PUBLIC_SYMBOL - 2000 == INTERNAL_INDEX, but there is no guarantee at all 
        # that this will always hold true.
        #
        # The bigger problem is that the the layer start index seems to be somewhat arbitrary. You 
        # normally would expect that the first layer starts at 10000 (because BLEND_DATA_OFFSET and 
        # that is what the code suggests), but this does not hold true. When I added a single 
        # transform layer, its angle parameter had most of the time the ID 10060, i.e., this first 
        # layer seems to start at what would be the third slot. Once I have also seen it starting at
        # 10040 without having a good explanation why.

        # So, internally, the layer shader is a bit irregular, or at least more complex than it looks
        # like. But let's continue for now and accept these magic numbers.

        # To more formally define the DescID for this parameter, we need to define its DescID. For 
        # that we need the data type of SLA_LAYER_BLEND. It is not exposed as a symbol in the public
        # API (but you could look up its numeric value in the description of a layer shader). So, we 
        # define it here.
        CUSTOMDATA_BLEND_LIST: int = 1011131

        # Now we can define the DescID for the transform angle parameter of the first layer. This 
        # assumes that it uses the magic number 10060 for the first property of the first layer and 
        # that it is of type float. If it is a transform layer or not does not really matter.
        did: c4d.DescID = c4d.DescID(
            # First desc level is the datatype unexposed to the public API (both C++ and Python)
            c4d.DescLevel(    
                c4d.SLA_LAYER_BLEND,   # The parameter ID for the layer blend list.
                CUSTOMDATA_BLEND_LIST, # Its data type.
                shader.GetType()),     # And the owner type of the parameter, i.e., the layer shader.
            # The sub-channel of the parameter, i.e., we reach here into the internals of the 
            # unexposed data type.
            c4d.DescLevel(             
                10060,                 # Our magic number for the first property of the first layer.
                c4d.DTYPE_REAL,        # The angle is a real number, you got that absolutely right.
                0))                    # For sub-channels, the owner type is usually 0.
        
        # With this DescID, we can now access the parameter value formally.
        if not shader.SetParameter(did, c4d.utils.DegToRad(30), c4d.DESCFLAGS_SET_NONE):
            raise RuntimeError("Could not set parameter value.")
        
        angle: float | None = shader.GetParameter(did, c4d.DESCFLAGS_GET_NONE)
        if angle is None:
            raise RuntimeError("Could not get parameter value.")

        print(f"{angle = }")

        # The bad news is that all this will not work in XPresso. The code below will run and create 
        # a port but the port is absolutely garbage, as it is a "color profile" port. But I had a 
        # look at how the C++ XPresso code creates the ports for #ID_OPERATOR_OBJECT nodes and there
        # is custom logic also covering layer shaders that is not replicable in the public API
        # (neither C++ nor Python). Which is likely why this fails with a nonsense port.

        # edit: okay, sometimes it seems to work. I would still avoid all this layer shader internals mess.

        root: GvNode = CheckType(master.GetRoot())
        objNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_OBJECT, x=50, y=50))
        objNode[c4d.GV_OBJECT_OBJECT_ID] = shader
        brokenAnglePort: GvPort | None = objNode.AddPort(c4d.GV_PORT_INPUT, did)
        if not brokenAnglePort:
            raise RuntimeError("Could not add port to node.")
        print(f"{brokenAnglePort = }")

    @staticmethod
    def PythonNodeLayerShaderAccess(
        shader: c4d.BaseShader, null: c4d.BaseObject,master: GvNodeMaster) -&gt; None:
        """Creates a Python XPresso node that accesses the transform angle parameter of the first
        layer in the given layer shader.

        Args:
            shader: The layer shader to manipulate.
            null: The null object that hosts the XPresso tag and the 'Angle' user data field.
            master: The node master of the XPresso tag.
        """
        # As you said yourself, an easy way out is to use a Python node. So, let's start by creating
        # a Python node.
        root: GvNode = CheckType(master.GetRoot())
        pyNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_PYTHON, x=200, y=200))

        # Create a parameter description for a link box named "Shader Link" and add it as a user 
        # data field to the Python node. There are other ways to get "in" a BaseList2D into a Python
        # node, but it does not have a BaseLink port type, so we always have get a bit creative.
        # Another way could be to pass the UUID of the node, but this then always requires us 
        # searching the node which costs performance.
        bc: c4d.BaseContainer = c4d.GetCustomDataTypeDefault(c4d.DTYPE_BASELISTLINK)
        bc[c4d.DESC_NAME] = "Shader Link"
        pyNode.AddUserData(bc)

        # Now set the user data to our shader and the code to the code we define below.
        pyNode[c4d.ID_USERDATA, 1] = shader 
        pyNode[c4d.GV_PYTHON_CODE] = Solution.CODE

        # Now remove all the default ports from the node.
        ports: list[GvPort] = CheckType((pyNode.GetInPorts() or []) + (pyNode.GetOutPorts() or []))
        for p in ports:
            pyNode.RemovePort(p)

        # Now we are going to add in and output ports to the Python node. This works a bit differently
        # than for other nodes, see https://developers.maxon.net/forum/topic/16302/ for a more in 
        # depth discussion of the subject.
        angleInPort: GvPort = CheckType(pyNode.AddPort(
            c4d.GV_PORT_INPUT,
            c4d.DescID(
                c4d.DescLevel(c4d.IN_REAL, c4d.DTYPE_SUBCONTAINER, pyNode.GetType()),
                c4d.DescLevel(1000, c4d.DTYPE_REAL, 0)
            )))
        angleInPort.SetName("angle_in")
        
        angleOutPort: GvPort = CheckType(pyNode.AddPort(
            c4d.GV_PORT_OUTPUT,
            c4d.DescID(
                c4d.DescLevel(c4d.OUT_REAL, c4d.DTYPE_SUBCONTAINER, pyNode.GetType()),
                c4d.DescLevel(1000, c4d.DTYPE_REAL, 0)
            )))
        angleOutPort.SetName("angle_out")

        # Now we create nodes for the null object and a result node and connect them to the Python.
        objNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_OBJECT, x=50, y=100))
        resNode: GvNode = CheckType(master.CreateNode(root, c4d.ID_OPERATOR_RESULT, x=350, y=200))

        # The output port for our "Angle" user data field on the null object.
        objOutPort: GvPort | None = objNode.AddPort(
            c4d.GV_PORT_OUTPUT,
            c4d.DescID(
                c4d.DescLevel(c4d.ID_USERDATA, c4d.DTYPE_SUBCONTAINER, objNode.GetType()),
                c4d.DescLevel(1, c4d.DTYPE_REAL, 0)
            ))
        if not objOutPort:
            raise RuntimeError("Could add user data port to object node.")
        
        # A result node can only have one input port, so we can also just do this without any danger
        # that future versions of Cinema 4D might break this code. We could also be more verbose.
        resInPort: GvPort | None = resNode.GetInPorts()[0] if resNode.GetInPorts() else None
        if not resInPort:
            raise RuntimeError("Could not get input port from result node.")
        
        # And we wire everything up and are done, yay!
        objOutPort.Connect(angleInPort)
        angleOutPort.Connect(resInPort)


    # The Python code used by the second, Python node based example, to set the code of the Python
    # node.
    CODE: str = '''import c4d

op: c4d.modules.graphview.GvNode # The Python Xpresso node containing this code.

def main() -&gt; None:
    """Sets the value of the transform angle parameter of the first layer in the linked layer 
    shader and outputs the value to the output port.
    """
    global angle_out

    try:
        shader: c4d.BaseShader = op[c4d.ID_USERDATA, 1] # The shader linked in the user data field.
        if not isinstance(shader, c4d.LayerShader):
            angle_out = 0.0
            print("The linked shader is not a layer shader.")
            return
        
        # It is of course up to you to define the details of this. My code does not make much sense.
        layer: c4d.LayerShaderLayer = shader.GetFirstLayer()
        if layer.GetType() != c4d.TypeTransform:
            angle_out = 0.0
            print("The first layer is not a transform layer.")
            return

        if not layer.SetParameter(c4d.LAYER_S_PARAM_TRANS_ANGLE, angle_in):
            angle_out = 0.0
            print("Could not set angle parameter.")
            return

        angle_out = angle_in
    except:
        angle_out = 0.0
'''


if __name__ == '__main__':
    Solution.main()
</code></pre>
]]></description><link>http://developers.maxon.net/forum/post/76812</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76812</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Thu, 11 Sep 2025 14:02:46 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Wed, 10 Sep 2025 19:45:44 GMT]]></title><description><![CDATA[<p dir="auto">hahaha I apreciate your help. Yea this has been wild trying to figure this out, but the creator id note gives me even more direction. Thank you.</p>
]]></description><link>http://developers.maxon.net/forum/post/76810</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76810</guid><dc:creator><![CDATA[alcol68]]></dc:creator><pubDate>Wed, 10 Sep 2025 19:45:44 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Wed, 10 Sep 2025 15:38:48 GMT]]></title><description><![CDATA[<p dir="auto">Hey <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/alcol68">@<bdi>alcol68</bdi></a>,</p>
<p dir="auto">sorry, I have not forgotten you, but I was busy with the fall release. I will have a look tomorrow. What you are doing <code>c4d.DescLevel(10120, ...)</code> (i.e., <code>BLEND_DATA_OFFSET </code> + X) is the right direction.</p>
<p dir="auto">Xpresso is an unholy hellhole when it comes to its operator IDs and things can fail when you are not ultra precise in your <code>DescID</code> definitions (your <code>DescLevel</code> is there for example missing the creator, and Xpresso can be picky about that - everywhere else the creator usually does not matter).</p>
<p dir="auto">Cheers,<br />
Ferdinand</p>
]]></description><link>http://developers.maxon.net/forum/post/76807</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76807</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Wed, 10 Sep 2025 15:38:48 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Sun, 07 Sep 2025 04:06:09 GMT]]></title><description><![CDATA[<p dir="auto">To slightly answer my own, most recent question and to complete my second initial question about adding keyframes to an animation track for the Gradient in a Gradient Layer of the Shader:</p>
<p dir="auto">As shown in the picture below, if you right-click on the “Layer” parameter of the Shader, navigate over User Interface, and click ‘Show Subchannels,” you can then see all the parameters of the Shader Layers. You can then drag the Transform Angle parameter into the console and get its description ID. Doing this, I was able to get that the dynamic DescID of the Transform Angle parameter (like you showed before): “Layer[c4d.SLA_LAYER_BLEND, 10120].”<br />
<img src="/forum/assets/uploads/files/1757216193811-dev-response1.png" alt="dev response1.PNG" class=" img-fluid img-markdown" /><br />
Putting this into the correct DescID format, I can create an animation track for any of the Layer parameters. I can create an animation track for this specific Transform Angle parameter with the above DescID.</p>
<pre><code>c4d.DescID(c4d.DescLevel(c4d.SLA_LAYER_BLEND), c4d.DescLevel(10120, c4d.DTYPE_REAL))
</code></pre>
<p dir="auto">HOWEVER, when I use this same DescID to add a port into the Layer/Object operator that I am working with, an empty port is created, not the Transform Angle port I want. But, if I go to manually add the correct port I want on this same operator, it is renamed to “Color Profile” and greyed out, as if it is currently being used (shown in the picture below). So, there is something missing in how I’m adding this port, either another level or a declared type with the first “c4d.SLA_LAYER_BLEND” level. I’m not sure what exactly is incorrect or missing with the adding of this port.<br />
<img src="/forum/assets/uploads/files/1757216280545-dev-response2.png" alt="dev response2.PNG" class=" img-fluid img-markdown" /><br />
We are getting closer to the answer, though.</p>
]]></description><link>http://developers.maxon.net/forum/post/76800</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76800</guid><dc:creator><![CDATA[alcol68]]></dc:creator><pubDate>Sun, 07 Sep 2025 04:06:09 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Sat, 06 Sep 2025 05:52:17 GMT]]></title><description><![CDATA[<p dir="auto">I appreciate your help, Ferdinand. I'm in no rush for this. I am now going through the C++ header files to try to get a better understanding of this and the code you posted.</p>
<p dir="auto">I saw an <a href="https://developers.maxon.net/forum/topic/15329/undo-method-for-layershaderlayer" target="_blank" rel="noopener noreferrer nofollow ugc">older post</a> where you noted that A LayerShaderLayer is not a scene element, but stored in the data container of a LayerShader. If that is true, is there a way to return a list of all the data stored within that LayerShader data container, like you can with the "GetUserDataContainer" function?</p>
]]></description><link>http://developers.maxon.net/forum/post/76799</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76799</guid><dc:creator><![CDATA[alcol68]]></dc:creator><pubDate>Sat, 06 Sep 2025 05:52:17 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Fri, 05 Sep 2025 08:39:36 GMT]]></title><description><![CDATA[<p dir="auto">Hey <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/alcol68">@<bdi>alcol68</bdi></a>,</p>
<p dir="auto">We cannot always answer right away.</p>
<p dir="auto">A layer shader stores its layer in a data type unexposed to the Python and public C++ API (<code>BlendDataType</code>) in its parameter <code>SLA_LAYER_BLEND</code>. Which is why this abstraction with <code>LayerShaderLayer</code> exists.</p>
<p dir="auto">Your code went into the right direction, as you probably have to use id decomposition (<code>(param, sub_channel)</code>) when you want to access such value without the layer abstraction. The issue with your code is that you do not take into account that this must be a dynamic description, i.e., you have to encode which layer you mean.</p>
<p dir="auto">I currently do not have much time due to approaching releases and would probably only find time next week to have a closer look. Here is how the data type reroutes parameter set events internally. As you can see, it encodes both the layer and the item into a desc level. How this works exactly and if this translates to Python, I would have to find out myself.</p>
<pre><code class="language-cpp">static const Int32 BLEND_DATA_OFFSET = 10000;
static const Int32 BLEND_DATA_STEP_SIZE = 20;

Bool Foo::GetParameter(...)
{
  iBlendDataType *s = (iBlendDataType*)data;
  if (!s)
    return false;

  Int32 lID = id[0].id;
  lID -= BLEND_DATA_OFFSET;

  DescID idNew = id &lt;&lt; 1; // push out id[0] 

  Int32 lItem = lID % BLEND_DATA_STEP_SIZE;
  Int32 lLayer = lID / BLEND_DATA_STEP_SIZE;

  //BlendLayer is what you know as LayerShaderLayer
  BlendLayer* pLayer = s-&gt;m_BlendLayers.SearchLayer(lLayer); 
  if (!pLayer)
    return false;

  pLayer-&gt;GetParameter(lItem, idNew, t_data, flags);

  return CustomDataTypeClass::GetParameter(data, id, t_data, flags);
}
</code></pre>
<p dir="auto">Cheers,<br />
Ferdinand</p>
]]></description><link>http://developers.maxon.net/forum/post/76796</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76796</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Fri, 05 Sep 2025 08:39:36 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Thu, 04 Sep 2025 23:43:02 GMT]]></title><description><![CDATA[<p dir="auto">I guess another way to do this is to create a Python node, rather than an object node, and access the Shader Layer parameters that way.</p>
]]></description><link>http://developers.maxon.net/forum/post/76795</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76795</guid><dc:creator><![CDATA[alcol68]]></dc:creator><pubDate>Thu, 04 Sep 2025 23:43:02 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Wed, 03 Sep 2025 16:08:36 GMT]]></title><description><![CDATA[<p dir="auto">I have previously looked at the “How to Create and Populate a Layer Shader” post. The shader that I am referencing above, I can easily create using those same methods. Below is the code for creating the Shader I am working with:</p>
<pre><code>Field1 = doc.SearchObject("Shader Field")
Shader = c4d.LayerShader()

ShaderLayer1 = Shader.AddLayer(c4d.TypeShader)
GradientShader1 = c4d.BaseList2D(c4d.Xgradient)
GradientShader1[c4d.ID_BASELIST_NAME] = "Gradient Circular"
GradientShader1[c4d.SLA_GRADIENT_TYPE] = c4d.SLA_GRADIENT_TYPE_2D_CIRC
GradientShader1[c4d.SLA_GRADIENT_CYCLE] = False
GradientShader1[c4d.SLA_GRADIENT_ANGLE] = 3.14159
Gradient1 = GradientShader1[c4d.SLA_GRADIENT_GRADIENT]
Gradient1.FlushKnots()
Gradient1.InsertKnot(c4d.Vector(0,0,0), 1, 0, 0.5, 0)
Gradient1.InsertKnot(c4d.Vector(0,0,0), 1, 1, 0.5, 1)
GradientShader1[c4d.SLA_GRADIENT_GRADIENT] = Gradient1
GradientShader1.InsertUnder(Shader)
ShaderLayer1.SetParameter(c4d.LAYER_S_PARAM_SHADER_LINK, GradientShader1)

ShaderLayer2 = Shader.AddLayer(c4d.TypeShader)
GradientShader2 = c4d.BaseList2D(c4d.Xgradient)
GradientShader2[c4d.ID_BASELIST_NAME] = "Gradient U"
GradientShader2[c4d.SLA_GRADIENT_CYCLE] = False
GradientShader2[c4d.SLA_GRADIENT_ANGLE] = 3.14159
Gradient2 = GradientShader1[c4d.SLA_GRADIENT_GRADIENT]
Gradient2.FlushKnots()
Gradient2.InsertKnot(c4d.Vector(0,0,0), 1, 0.125, 0.5, 0)
Gradient2.InsertKnot(c4d.Vector(1,1,1), 1, 1, 0.5, 1)
GradientShader2[c4d.SLA_GRADIENT_GRADIENT] = Gradient2
GradientShader2.InsertUnder(Shader)
ShaderLayer2.SetParameter(c4d.LAYER_S_PARAM_SHADER_LINK, GradientShader2)

ShaderLayer3 = Shader.AddLayer(c4d.TypeShader)
NoiseShader = c4d.BaseList2D(c4d.Xnoise)
NoiseShader[c4d.SLA_NOISE_COLOR1] = c4d.Vector(1,1,1)
NoiseShader[c4d.SLA_NOISE_COLOR2] = c4d.Vector(0,0,0)
NoiseShader[c4d.SLA_NOISE_SEED] = 666
NoiseShader[c4d.SLA_NOISE_GLOBAL_SCALE] = 2
NoiseShader[c4d.SLA_NOISE_ANI_SPEED] = 1
NoiseShader[c4d.SLA_NOISE_CONTRAST] = 1
NoiseShader.InsertUnder(Shader)
ShaderLayer3.SetParameter(c4d.LAYER_S_PARAM_SHADER_LINK, NoiseShader)
ShaderLayer3.SetParameter(c4d.LAYER_S_PARAM_SHADER_MODE, 11)

ShaderLayer4 = Shader.AddLayer(c4d.TypeTransform)
ShaderLayer4.SetParameter(c4d.LAYER_S_PARAM_TRANS_ANGLE, 3.14159)
Field1.InsertShader(Shader)
Field1[c4d.ID_MG_SHADER_SHADER] = Shader
</code></pre>
<p dir="auto">Using the port-creation example I gave before, for an object operator, I use the DescLevel to get the ID of the property I want to create a port for. In the above example, I use the integer “c4d.ID_BASEOBJECT_GLOBAL_POSITION.” Then if I want to use the specific Z parameter, I use the second level integer of “c4d.VECTOR_Z.”</p>
<p dir="auto">I assume there is a similar level system to grab the angle parameter of the Transform layer in my Shader. The Shader itself is linked to the object operator, so accessing its layers should be the first level.</p>
<pre><code>ObjectNode.AddPort(c4d.GV_PORT_INPUT, c4d.DescID(c4d.DescLevel(c4d.SLA_LAYER_BLEND), c4d.DescLevel(c4d.LAYER_S_PARAM_TRANS_ANGLE)), flag=c4d.GV_PORT_FLAG_IS_VISIBLE, message=False)
</code></pre>
<p dir="auto">If I use just the single level integer of “c4d. LAYER_S_PARAM_TRANS_ANGLE” the port is not created. So, I assume there is another level above this one that needs to be called as well, namely the actual Transform layer itself. But I don’t know how to call that layer as an integer in the DescLevel method, because I cannot pass an object (“ShaderLayer4” from my above creation code) in the integer parameter of DescLevel. I used the placeholder of “c4d.SLA_LAYER_BLEND” as that first level, but it creates an empty port, not the angle parameter port. Using “c4d.TypeTransform” or “c4d. LAYER_S_PARAM_ALL_ACTIVE” do not work either.</p>
<p dir="auto">This is what I am trying to figure out: What is the integer that I need to use in order to access the Transform Layer itself to use in the DescLevel method, to use in the AddPort method? Unless there is a different way to add a port, I cannot find any information on this particular issue.</p>
]]></description><link>http://developers.maxon.net/forum/post/76794</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76794</guid><dc:creator><![CDATA[alcol68]]></dc:creator><pubDate>Wed, 03 Sep 2025 16:08:36 GMT</pubDate></item><item><title><![CDATA[Reply to DescID and DescLevel for Shader Layers and properties on Wed, 03 Sep 2025 08:52:22 GMT]]></title><description><![CDATA[<p dir="auto">Hey <a class="plugin-mentions-user plugin-mentions-a" href="/forum/user/alcol68">@<bdi>alcol68</bdi></a>,</p>
<p dir="auto">Welcome to the Maxon developers forum and its community, it is great to have you with us!</p>
<h4>Getting Started</h4>
<p dir="auto">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.</p>
<ul>
<li><a href="/forum/topic/15242/">Forum Overview</a>: 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.</li>
<li><a href="/forum/topic/15244/">Support Procedures</a>: 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.</li>
<li><a href="/forum/topic/15243/">Forum Features</a>: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.</li>
</ul>
<p dir="auto">It is <strong>strongly</strong> recommended to read the first two topics carefully, especially the section <a href="/forum/topic/15244/support-procedures/4">Support Procedures: How to Ask Questions</a>.</p>
<h4>About your First Question</h4>
<p dir="auto">Your question is a little bit ambiguous in what <code>DescID</code> you are looking for a layer shader, you probably mean the properties of one of its children and not the layer shader itself. I would recommend to have a look at <a href="https://developers.maxon.net/docs/cpp/2025_3_0/page_manual_baseshader.html#page_manual_baseshader_access" target="_blank" rel="noopener noreferrer nofollow ugc">BaseShader: Access and Structure (C++)</a> to understand how shaders are nested. You can then check out <a href="https://developers.maxon.net/forum/topic/14059/how-to-create-and-populate-a-layer-shader/2" target="_blank" rel="noopener noreferrer nofollow ugc">this posting</a> for how to manipulate a layer shader in Python where I also documented its more fringe parameters.</p>
<p dir="auto">Cheers,<br />
Ferdinand</p>
]]></description><link>http://developers.maxon.net/forum/post/76793</link><guid isPermaLink="true">http://developers.maxon.net/forum/post/76793</guid><dc:creator><![CDATA[ferdinand]]></dc:creator><pubDate>Wed, 03 Sep 2025 08:52:22 GMT</pubDate></item></channel></rss>