Isotropix Forums

Bug refreshing shading layer attributes?

Clarisse Scripting related topics

Bug refreshing shading layer attributes?

Unread postby bvz2000 » Wed Jun 13, 2018 4:55 am

I have the following project:

Code: Select all
#Isotropix_Serial_Version 1.1

#Isotropix_Clarisse_Version 3.5
#Isotropix_Clarisse_Project_Version 0.94
ShadingLayer {
    name "shading_layer"
    #version 0.94
    #created 1528857991
    #modified 1528858095
    selected_columns "is_visible" "material" "clip_map" "displacement" "shading_variables"
    shading_layer_is_active yes yes
    shading_layer_filters "bob" "nancy"
    is_visible yes yes
    material "" ""
    displacement "" ""
    shading_variables "" ""
    clip_map "" ""
    values_states yes no no no no yes no no no no
    custom_attributes {
        attribute_group "texAssign" {
            string "filter_0" {
                value "bob"
            }
            filename_open "texAssign_0" {
                value ""
            }
            string "filter_1" {
                value "nancy"
            }
            filename_open "texAssign_1" {
                value ""
            }
        }
    }
}


I also have the following code (you can copy it and paste it into the script editor and run it directly)

Code: Select all
try:
    import ix
except ImportError:
    ix = None

"""
This should also be in a different package (not Clam). But until I split out the common functions
into a generic clarisse lib, this will live here.
"""

# --------------------------------------------------------------------------------------------------
def get_shading_layer_rules(shadingLayer):
    """
    Given a shading layer, returns a list of dicts that contain all of the rules in that shading
    layer.

    The list is in the same order as the rules in the shading layer.

    The dict is of the format:

    isVisible = value
    filter = value
    material = value
    clip_map = value
    displacement = value
    shading_variables = value

    :param shadingLayer: The shading layer object.

    :return: A list of dicts
    """

    filtersL = ix.api.CoreStringArray()
    shadingLayer.get_attribute("shading_layer_filters").get_raw_values(filtersL)

    isVisibleL = ix.api.CoreStringArray()
    shadingLayer.get_attribute("is_visible").get_raw_values(isVisibleL)

    materialsL = ix.api.CoreStringArray()
    shadingLayer.get_attribute("material").get_raw_values(materialsL)

    displacementsL = ix.api.CoreStringArray()
    shadingLayer.get_attribute("displacement").get_raw_values(displacementsL)

    shadingVariablesL = ix.api.CoreStringArray()
    shadingLayer.get_attribute("shading_variables").get_raw_values(shadingVariablesL)

    clipMapsL = ix.api.CoreStringArray()
    shadingLayer.get_attribute("clip_map").get_raw_values(clipMapsL)

    outputL = list()
    for i in range(filtersL.get_count()):
        rulesD = dict()
        rulesD["isVisible"] = isVisibleL[i]
        rulesD["filter"] = filtersL[i]
        rulesD["material"] = materialsL[i]
        rulesD["clip_map"] = clipMapsL[i]
        rulesD["displacement"] = displacementsL[i]
        rulesD["shading_variables"] = shadingVariablesL[i]
        outputL.append(rulesD)

    return outputL


# --------------------------------------------------------------------------------------------------
def get_shading_layer_custom_attributes(shadingLayer):
    """
    Given a shading layer, returns a list of dicts that contain the custom attributes for the
    texAssign data.

    The list is in the same order as the attributes in the shading layer.

    The dict is of the format:

    filter = filename

    :param shadingLayer: The shading layer object.

    :return: A list of dicts
    """

    i = 0
    outputL = list()

    # We are just going to search forever
    while True:

        attrD = dict()

        try:
            filterString = shadingLayer.get_attribute("filter_" + str(i)).get_string()
            filename = shadingLayer.get_attribute("texAssign_" + str(i)).get_string()
        except AttributeError:
            break

        attrD[filterString] = filename

        outputL.append(attrD)

        i += 1

    return outputL


# --------------------------------------------------------------------------------------------------
def removeTexAssignAttributes(shadingLayer):
    """
    Removes all custom attributes on the shading layer.

    :param shadingLayer: The shading layer we want to remove the texAssign Custom attributes from.

    :return: Nothing.
    """
    i = 0

    # We are just going to search forever
    while True:

        filterString = shadingLayer.get_attribute("filter_" + str(i))
        filename = shadingLayer.get_attribute("texAssign_" + str(i))

        if filterString is None or filename is None:
            break

        ix.cmds.LockAttributes([shadingLayer.get_full_name() + ".filter_" + str(i)], False)
        ix.cmds.RemoveCustomAttribute([shadingLayer.get_full_name() + ".filter_" + str(i),
                                       shadingLayer.get_full_name() + ".texAssign_" + str(i)])

        i += 1


# --------------------------------------------------------------------------------------------------
def createShadingLayerToTexAssignMappingAttributes(shadingLayer):
    """
    Creates the attributes that match up a specific "filter" with a particular texAssignFile

    :param shadingLayer: The shading layer on which we want to assign these texAssign files.

    :return: Nothing.
    """

    # Start by getting a list of all the rules in this shading layer
    rulesL = get_shading_layer_rules(shadingLayer)

    # Next, get a list of all the custom texAssign attributes in this shading layer
    savedAttrsL = get_shading_layer_custom_attributes(shadingLayer)

    # Next, remove all the custom attributes
    removeTexAssignAttributes(shadingLayer)

    # Step through each rule and create a pair of custom attributes for this rule, pre-filled with
    # the filter text
    for i in range(len(rulesL)):

        # Create and populate the filter field
        ix.cmds.CreateCustomAttribute([shadingLayer],
                                      "filter_" + str(i),
                                      3,
                                      ["container",
                                       "vhint",
                                       "group",
                                       "count",
                                       "allow_expression"],
                                      ["CONTAINER_SINGLE",
                                       "VISUAL_HINT_DEFAULT",
                                       "texAssign",
                                       "1",
                                       "0"])
        ix.cmds.SetValues([shadingLayer.get_full_name() + ".filter_" + str(i) + "[0]"],
                          [rulesL[i]["filter"]])

        # Lock it because this is only for reference. I do not want the artists messing with it.
        ix.cmds.LockAttributes([shadingLayer.get_full_name() + ".filter_" + str(i)], True)

        # Create the filename field
        ix.cmds.CreateCustomAttribute([shadingLayer],
                                      "texAssign_" + str(i),
                                      3,
                                      ["container",
                                       "vhint",
                                       "group",
                                       "count",
                                       "allow_expression"],
                                      ["CONTAINER_SINGLE",
                                       "VISUAL_HINT_FILENAME_OPEN",
                                       "texAssign",
                                       "1",
                                       "0"])

        # Try to find this filter in the saved attributes. If so, re-fill out the filename field.
        for savedAttr in savedAttrsL:

            try:
                filename = savedAttr[rulesL[i]["filter"]]
            except KeyError:
                continue
            ix.cmds.SetValues([shadingLayer.get_full_name() + ".texAssign_" + str(i) + "[0]"],
                              [filename])

createShadingLayerToTexAssignMappingAttributes(ix.selection[0])



To run the code, select the shading layer and hit "run" in the script editor.


The code reads the rules in the shading layer and then uses them to build some custom attributes on the shading layer node.

The issue is that if you run the code, then edit one of the filters in the shading layer, and then run the code again, it still lists the old values for the attributes, and not the new ones you just edited.

Only when the scene is saved (or an auto-save) occurs does this code pick up the new filter attributes.

is this a bug? Or am I doing something wrong?

Is there a way to work around this?
bvz2000
 
Posts: 380
Joined: Thu Nov 13, 2014 7:05 pm

Re: Bug refreshing shading layer attributes?

Unread postby bvaldes » Wed Jun 13, 2018 12:17 pm

Hi,

The issue where in get_shading_layer_rule(), you didn't use the right functions to get the content of the rules. You have to use the module of the shading layer to get access to the content properly.

So I rewrite the function (maybe to much), the output is the same than yours but with the updated values.
I also avoid some warnings you had when you tried to get your customs attribute (I used attribute_exists() instead of get_attribute()). Moreover, I add a small condition about the unlock attribute to avoid the error prompted if the attribute is already unlocked (that happened only the first time you ran the script).

There is the edited script:

python code

try:
import ix
except ImportError:
ix = None

"""
This should also be in a different package (not Clam). But until I split out the common functions
into a generic clarisse lib, this will live here.
"""

# --------------------------------------------------------------------------------------------------
def get_shading_layer_rules(shadingLayer):
"""
Given a shading layer, returns a list of dicts that contain all of the rules in that shading
layer.

The list is in the same order as the rules in the shading layer.

The dict is of the format:

isVisible = value
filter = value
material = value
clip_map = value
displacement = value
shading_variables = value

:param shadingLayer: The shading layer object.

:return: A list of dicts
"""

outputL = list()
for i in range(shadingLayer.get_module().get_rules().get_count()): # shadingLayer.get_module().get_rules().get_count() gives you the number of rows
rulesD = dict()
# get the content for each columns
for column in ["filter", "is_visible", "material", "clip_map", "displacement", "shading_variables"]:
rulesD[column] = shadingLayer.get_module().get_rule_value(i, column)
outputL.append(rulesD)

return outputL


# --------------------------------------------------------------------------------------------------
def get_shading_layer_custom_attributes(shadingLayer):
"""
Given a shading layer, returns a list of dicts that contain the custom attributes for the
texAssign data.

The list is in the same order as the attributes in the shading layer.

The dict is of the format:

filter = filename

:param shadingLayer: The shading layer object.

:return: A list of dicts
"""

i = 0
outputL = list()

# We are just going to search forever
while True:

attrD = dict()

try:
filterString = shadingLayer.attribute_exists("filter_" + str(i)).get_string()
filename = shadingLayer.attribute_exists("texAssign_" + str(i)).get_string()
except AttributeError:
break

attrD[filterString] = filename

outputL.append(attrD)

i += 1

return outputL


# --------------------------------------------------------------------------------------------------
def removeTexAssignAttributes(shadingLayer):
"""
Removes all custom attributes on the shading layer.

:param shadingLayer: The shading layer we want to remove the texAssign Custom attributes from.

:return: Nothing.
"""
i = 0

# We are just going to search forever
while True:

filterString = shadingLayer.attribute_exists("filter_" + str(i))
filename = shadingLayer.attribute_exists("texAssign_" + str(i))

if filterString is None or filename is None:
break

if filterString.is_locked():
ix.cmds.LockAttributes([filterString.get_full_name()], False)
ix.cmds.RemoveCustomAttribute([filterString.get_full_name(),
filename.get_full_name()])

i += 1


# --------------------------------------------------------------------------------------------------
def createShadingLayerToTexAssignMappingAttributes(shadingLayer):
"""
Creates the attributes that match up a specific "filter" with a particular texAssignFile

:param shadingLayer: The shading layer on which we want to assign these texAssign files.

:return: Nothing.
"""

# Start by getting a list of all the rules in this shading layer
rulesL = get_shading_layer_rules(shadingLayer)

# Next, get a list of all the custom texAssign attributes in this shading layer
savedAttrsL = get_shading_layer_custom_attributes(shadingLayer)

# Next, remove all the custom attributes
removeTexAssignAttributes(shadingLayer)

# Step through each rule and create a pair of custom attributes for this rule, pre-filled with
# the filter text
for i in range(len(rulesL)):

# Create and populate the filter field
ix.cmds.CreateCustomAttribute([shadingLayer],
"filter_" + str(i),
3,
["container",
"vhint",
"group",
"count",
"allow_expression"],
["CONTAINER_SINGLE",
"VISUAL_HINT_DEFAULT",
"texAssign",
"1",
"0"])
ix.cmds.SetValues([shadingLayer.get_full_name() + ".filter_" + str(i) + "[0]"],
[rulesL[i]["filter"]])

# Lock it because this is only for reference. I do not want the artists messing with it.
ix.cmds.LockAttributes([shadingLayer.get_full_name() + ".filter_" + str(i)], True)

# Create the filename field
ix.cmds.CreateCustomAttribute([shadingLayer],
"texAssign_" + str(i),
3,
["container",
"vhint",
"group",
"count",
"allow_expression"],
["CONTAINER_SINGLE",
"VISUAL_HINT_FILENAME_OPEN",
"texAssign",
"1",
"0"])

# Try to find this filter in the saved attributes. If so, re-fill out the filename field.
for savedAttr in savedAttrsL:

try:
filename = savedAttr[rulesL[i]["filter"]]
except KeyError:
continue
ix.cmds.SetValues([shadingLayer.get_full_name() + ".texAssign_" + str(i) + "[0]"],
[filename])


createShadingLayerToTexAssignMappingAttributes(ix.selection[0])

I hope that will solve your issue. You wrote very detailed comments on your functions, that was really helpful.

Best regards,
Benoit
Benoit VALDES
Isotropix
Clarisse QA
User avatar
bvaldes
 
Posts: 247
Joined: Mon Sep 26, 2016 11:44 am

Re: Bug refreshing shading layer attributes?

Unread postby bvz2000 » Wed Jun 13, 2018 7:17 pm

That was super super helpful. Thanks!

I had originally started with using the module, but quickly became confused. Specifically because I didn't know how to get a count of how many items would be in the array. But your code shows me how that works.

Is there any documentation that explains what exactly a module is? I'm an artist doing python programming so I don't have a lot of computer science experience. I understand the concept of objects (and inheritance etc.) but I get confused a little with Clarisse because to the untrained eye (mine) I am never sure what my "object" is that I am querying.

For example, if I have a shading_layer object, I would expect that it contains a number of attribute objects. These attribute objects are what I would expect to query with regard to their attributes. But each shading_layer object appears to also have a module object that is separate from the attribute objects. But both appear to be able to get to the same information (at least a little bit, with the understanding that I ran into problems above). I am having trouble understanding what a module is, and when I would use it instead of directly trying to grab an attribute object.

Thanks again. The above code is very helpful.
bvz2000
 
Posts: 380
Joined: Thu Nov 13, 2014 7:05 pm

Re: Bug refreshing shading layer attributes?

Unread postby sam » Wed Jun 13, 2018 7:54 pm

Hey!

Have you read Introduction to Clarisse Object Model in the SDK docs? There is an overview of what object/class/module are
Sam Assadian
Isotropix
CEO/Founder
User avatar
sam
 
Posts: 1103
Joined: Sat Jan 26, 2013 12:33 am

Re: Bug refreshing shading layer attributes?

Unread postby bvz2000 » Wed Jun 13, 2018 8:15 pm

I think I read it a while back. But obviously didn't comprehend the whole thing. I'll look at it again.

Thanks.
bvz2000
 
Posts: 380
Joined: Thu Nov 13, 2014 7:05 pm

Re: Bug refreshing shading layer attributes?

Unread postby sam » Wed Jun 13, 2018 9:53 pm

Don't be hard on yourself, maybe we simply need to improve the topic. Let me know if it makes sense after a reread.
Cheers
Sam Assadian
Isotropix
CEO/Founder
User avatar
sam
 
Posts: 1103
Joined: Sat Jan 26, 2013 12:33 am

Re: Bug refreshing shading layer attributes?

Unread postby bvz2000 » Wed Jun 13, 2018 9:55 pm

Thanks :) Will do.
bvz2000
 
Posts: 380
Joined: Thu Nov 13, 2014 7:05 pm


Return to Scripting
cron