Working version of attributes on extractor

This commit is contained in:
Toke Stuart Jepsen 2024-02-20 15:48:51 +00:00
parent 87b3d7fcec
commit 698e5db01e
8 changed files with 404 additions and 396 deletions

View file

@ -70,6 +70,7 @@ def extract_alembic(
worldSpace=False,
writeColorSets=False,
writeCreases=False,
writeNormals=False,
writeFaceSets=False,
writeUVSets=False,
writeVisibility=False

View file

@ -1,26 +1,16 @@
from maya import cmds
from openpype.hosts.maya.api import lib, plugin
from openpype.hosts.maya.api.alembic import ALEMBIC_ARGS
from openpype.lib import (
BoolDef,
TextDef,
NumberDef,
EnumDef,
UISeparatorDef,
UILabelDef,
)
from openpype.pipeline import CreatedInstance
def _get_animation_attr_defs(cls):
"""Get Animation generic definitions.
The line is blurry between what's "Animation" generic and "Alembic",
but the rule of thumb is that whatever "AlembicExport -h" accepts
is "Alembic" and the other ones are "Animation".
"""
"""Get Animation generic definitions."""
defs = lib.collect_animation_defs()
defs.extend(
[
@ -30,205 +20,16 @@ def _get_animation_attr_defs(cls):
BoolDef(
"includeParentHierarchy", label="Include Parent Hierarchy"
),
BoolDef(
"includeUserDefinedAttributes",
label="Include User Defined Attributes"
),
]
)
return defs
def _get_abc_export_flags(cls):
"""Get two sets with the Alembic Export flags.
Alembic flags are treated as booleans, so here we get all the possible
options, and work out a list with all the ones that can be toggled by
the user, and the ones defined in the settings.
"""
# The Arguments that can be modified by the Publisher
abc_export_overrides = set(getattr(cls, "abc_export_overrides", set()))
# What we have set in the Settings as defaults.
default_abc_export_flags = set(getattr(cls, "abc_export_flags", set()))
# Set of un-toggleable flags, specified by the settings
abc_export_flags = {
arg
for arg in default_abc_export_flags
if arg not in abc_export_overrides
}
# Set of all the available Alembic Export Flags
abc_boolean_flags = {
arg for arg, arg_type in ALEMBIC_ARGS.items() if arg_type is bool
}
# Set of togglable flags
abc_export_toggleable_flags = {
arg for arg in abc_export_overrides if arg in abc_boolean_flags
}
return abc_export_flags, abc_export_toggleable_flags
def _get_animation_abc_attr_defs(cls):
"""Get definitions relating to Alembic.
An admin can define in settings the default arguments, which are then not
modifiable by the person publishing, unless they are added to the Alembic
Overrides setting, which is mapped to `abc_args_overrides`.
Most of the Alembic Arguments are flags, treated as booleans, and there are
two possible lists: the defaults (from settings) and the the toggleable by
the user, these two define an EnumDef respectively.
We use a combination of the two above to only show a muiltiselection
dropdown for booleans, and disabling the non-boolean arguments on the
interface.
There's also a new separator so it's clearer what belongs to common
Animation publishes versus what is Almebic specific, the line is blurry,
but the rule of thumb is that whatever "AlembicExport -h" accepts is
"Alembic" and the other ones are "Animation".
"""
abc_defs = None
abc_defs = [
UISeparatorDef("sep_alembic_options"),
UILabelDef("Alembic Options"),
]
# The Arguments that can be modified by the Publisher
abc_args_overrides = set(getattr(cls, "abc_args_overrides", set()))
# What we have set in the Settings as defaults.
default_abc_export_flags = set(getattr(cls, "abc_export_flags", set()))
(
abc_export_flags,
abc_export_toggleable_flags,
) = _get_abc_export_flags(cls)
abc_defs.append(
EnumDef(
"abcExportFlags",
list(abc_export_flags),
default=list(abc_export_flags),
multiselection=True,
label="Settings Defined Arguments",
disabled=True,
hidden=True,
)
)
# Only display Boolan flags that the Admin defined as overrideable
abc_export_toggleable_defaults = [
arg
for arg in abc_export_toggleable_flags
if arg in default_abc_export_flags
]
abc_defs.append(
EnumDef(
"abcExportTogglableFlags",
list(abc_export_toggleable_flags)
if abc_export_toggleable_flags
else [""],
default=abc_export_toggleable_defaults,
multiselection=True,
label="Export Flags",
disabled=True if not abc_export_toggleable_flags else False,
)
)
abc_defs.append(
TextDef(
"attr",
label="Custom Attributes",
default=getattr(cls, "attr", None),
placeholder="attr1; attr2; ...",
disabled=True if "attr" not in abc_args_overrides else False,
)
)
abc_defs.append(
TextDef(
"attrPrefix",
label="Custom Attributes Prefix",
default=getattr(cls, "attrPrefix", None),
placeholder="prefix1; prefix2; ...",
disabled=True if "attrPrefix" not in abc_args_overrides else False,
)
)
abc_defs.append(
EnumDef(
"dataFormat",
label="Data Format",
default=getattr(cls, "dataFormat", None),
items=["ogawa", "HDF"],
disabled=True if "dataFormat" not in abc_args_overrides else False,
)
)
abc_defs.append(
NumberDef(
"preRollStartFrame",
label="Start frame for preroll",
default=getattr(cls, "preRollStartFrame", None),
tooltip=(
"The frame to start scene evaluation at. This is used to set"
" the starting frame for time dependent translations and can"
" be used to evaluate run-up that isn't actually translated."
),
disabled=True
if "preRollStartFrame" not in abc_args_overrides
else False,
)
)
return abc_defs
def _ensure_defaults(cls, instance_data):
"""Ensure we get default values when an attribute is not overrideable.
In instances where an attribute used to be modifiable, and then was locked
again, we want to make sure that we pass the default (what's on the
settings) instead of any value that might have been stored in the scene
when the attribute was modifiable.
"""
abc_args_overrides = set(getattr(cls, "abc_args_overrides", set()))
creator_attr = instance_data["creator_attributes"]
attr_default = getattr(cls, "attr", "")
if "attr" not in abc_args_overrides:
creator_attr["attr"] = attr_default
if "attrPrefix" not in abc_args_overrides:
creator_attr["attrPrefix"] = getattr(cls, "attrPrefix", "")
if "dataFormat" not in abc_args_overrides:
creator_attr["dataFormat"] = getattr(cls, "dataFormat", "")
if "preRollStartFrame" not in abc_args_overrides:
creator_attr["preRollStartFrame"] = getattr(
cls, "preRollStartFrame", ""
)
(
abc_boolean_defaults,
abc_boolean_overrides,
) = _get_abc_export_flags(cls)
creator_attr["abcExportFlags"] = list(abc_boolean_defaults)
if creator_attr.get("abcExportTogglableFlags", []):
abc_boolean_args = creator_attr["abcExportTogglableFlags"].copy()
creator_attr["abcExportTogglableFlags"] = [
arg for arg in abc_boolean_args if arg not in abc_boolean_overrides
]
return instance_data
class CreateAnimation(plugin.MayaHiddenCreator):
"""Animation output for character rigs
@ -257,18 +58,12 @@ class CreateAnimation(plugin.MayaHiddenCreator):
for node in cached_subsets.get(self.identifier, []):
node_data = self.read_instance_node(node)
_ensure_defaults(self, node_data)
created_instance = CreatedInstance.from_existing(node_data, self)
self._add_instance_to_context(created_instance)
def get_instance_attr_defs(self):
super(CreateAnimation, self).get_instance_attr_defs()
defs = _get_animation_attr_defs(self)
abc_defs = _get_animation_abc_attr_defs(self)
if abc_defs:
defs.extend(abc_defs)
return defs
@ -292,18 +87,12 @@ class CreatePointCache(plugin.MayaCreator):
for node in cached_subsets.get(self.identifier, []):
node_data = self.read_instance_node(node)
_ensure_defaults(self, node_data)
created_instance = CreatedInstance.from_existing(node_data, self)
self._add_instance_to_context(created_instance)
def get_instance_attr_defs(self):
super(CreatePointCache, self).get_instance_attr_defs()
defs = _get_animation_attr_defs(self)
abc_defs = _get_animation_abc_attr_defs(self)
if abc_defs:
defs.extend(abc_defs)
return defs
def create(self, subset_name, instance_data, pre_create_data):

View file

@ -10,11 +10,6 @@ class CollectPointcache(pyblish.api.InstancePlugin):
families = ["pointcache"]
label = "Collect Pointcache"
hosts = ["maya"]
legacy_settings = {
"write_color_sets": "writeColorSets",
"write_face_sets": "writeFaceSets",
"include_user_defined_attributes": "includeUserDefinedAttributes"
}
def process(self, instance):
if instance.data.get("farm"):

View file

@ -9,9 +9,18 @@ from openpype.hosts.maya.api.lib import (
maintained_selection,
iter_visible_nodes_in_range,
)
from openpype.lib import (
BoolDef,
TextDef,
NumberDef,
EnumDef,
UISeparatorDef,
UILabelDef,
)
from openpype.pipeline.publish import OpenPypePyblishPluginMixin
class ExtractAlembic(publish.Extractor):
class ExtractAlembic(publish.Extractor, OpenPypePyblishPluginMixin):
"""Produce an alembic of just point positions and normals.
Positions and normals, uvs, creases are preserved, but nothing more,
@ -25,6 +34,19 @@ class ExtractAlembic(publish.Extractor):
hosts = ["maya"]
families = ["pointcache", "model", "vrayproxy.alembic"]
targets = ["local", "remote"]
flags = []
attr = []
attrPrefix = []
dataFormat = "ogawa"
melPerFrameCallback = ""
melPostJobCallback = ""
preRollStartFrame = 0
pythonPerFrameCallback = ""
pythonPostJobCallback = ""
stripNamespaces = -1
userAttr = ""
userAttrPrefix = ""
export_overrides = []
def process(self, instance):
if instance.data.get("farm"):
@ -37,20 +59,22 @@ class ExtractAlembic(publish.Extractor):
start = float(instance.data.get("frameStartHandle", 1))
end = float(instance.data.get("frameEndHandle", 1))
# Collect Alembic Arguments
creator_attributes = instance.data.get("creator_attributes")
abc_flags = creator_attributes.get(
"abcExportTogglableFlags"
) + creator_attributes.get("abcExportTogglableFlags")
attribute_values = self.get_attr_values_from_data(
instance.data
)
abc_attrs = [
attrs = [
attr.strip()
for attr in creator_attributes.get("attr", "").split(";")
for attr in attribute_values.get("attr", "").split(";")
if attr.strip()
]
attrs += instance.data.get("userDefinedAttributes", [])
attrs += ["cbId"]
abc_attr_prefixes = [
attr_prefix.strip()
for attr_prefix in instance.data.get("attrPrefix", "").split(";")
attr_prefixes = [
attr.strip()
for attr in attribute_values.get("attrPrefix", "").split(";")
if attr.strip()
]
self.log.debug("Extracting pointcache...")
@ -60,51 +84,52 @@ class ExtractAlembic(publish.Extractor):
filename = "{name}.abc".format(**instance.data)
path = os.path.join(parent_dir, filename)
abc_root = None
root = None
if not instance.data.get("includeParentHierarchy", True):
# Set the root nodes if we don't want to include parents
# The roots are to be considered the ones that are the actual
# direct members of the set
abc_root = roots
root = roots
abc_writeUVSets = False
if int(cmds.about(version=True)) >= 2017:
# Since Maya 2017 alembic supports multiple uv sets - write them.
if "writeUVSets" in abc_flags:
abc_writeUVSets = True
extract_abc_args = {
args = {
"file": path,
"attr": abc_attrs,
"attrPrefix": abc_attr_prefixes,
"dataFormat": creator_attributes.get("dataFormat", "ogawa"),
"attr": attrs,
"attrPrefix": attr_prefixes,
"dataFormat": attribute_values.get("dataFormat", "ogawa"),
"endFrame": end,
"eulerFilter": True if "eulerFilter" in abc_flags else False,
"noNormals": True if "noNormals" in abc_flags else False,
"preRoll": True if "preRoll" in abc_flags else False,
"preRollStartFrame": creator_attributes.get(
"eulerFilter": False,
"noNormals": False,
"preRoll": False,
"preRollStartFrame": attribute_values.get(
"preRollStartFrame", 0
),
"renderableOnly": True if "renderableOnly" in abc_flags else False,
"root": abc_root,
"selection": True, # Should this stay like so?
"renderableOnly": False,
"root": root,
"selection": True,
"startFrame": start,
"step": creator_attributes.get("step", 1.0),
"stripNamespaces": True,
"uvWrite": True if "uvWrite" in abc_flags else False,
"verbose": True if "verbose" in abc_flags else False,
"wholeFrameGeo": True if "wholeFrameGeo" in abc_flags else False,
"worldSpace": True if "worldSpace" in abc_flags else False,
"writeColorSets": True if "writeColorSets" in abc_flags else False,
"writeCreases": True if "writeCreases" in abc_flags else False,
"writeFaceSets": True if "writeFaceSets" in abc_flags else False,
"writeUVSets": abc_writeUVSets,
"writeVisibility": True
if "writeVisibility" in abc_flags
else False,
"step": instance.data.get(
"creator_attributes", {}
).get("step", 1.0),
"stripNamespaces": False,
"uvWrite": False,
"verbose": False,
"wholeFrameGeo": False,
"worldSpace": False,
"writeColorSets": False,
"writeCreases": False,
"writeFaceSets": False,
"writeUVSets": False,
"writeVisibility": False,
}
# Export flags are defined as default enabled flags excluding flags
# that are exposed to the user, plus the flags the user has enabled
# when publishing.
flags = list(set(self.flags) - set(self.export_overrides))
flags += attribute_values["flag_overrides"]
for flag in flags:
args[flag] = True
if instance.data.get("visibleOnly", False):
# If we only want to include nodes that are visible in the frame
# range then we need to do our own check. Alembic's `visibleOnly`
@ -121,10 +146,10 @@ class ExtractAlembic(publish.Extractor):
cmds.select(nodes, noExpand=True)
self.log.debug(
"Running `extract_alembic` with the arguments: {}".format(
extract_abc_args
args
)
)
extract_alembic(**extract_abc_args)
extract_alembic(**args)
if "representations" not in instance.data:
instance.data["representations"] = []
@ -148,17 +173,17 @@ class ExtractAlembic(publish.Extractor):
return
path = path.replace(".abc", "_proxy.abc")
extract_abc_args["file"] = path
args["file"] = path
if not instance.data.get("includeParentHierarchy", True):
# Set the root nodes if we don't want to include parents
# The roots are to be considered the ones that are the actual
# direct members of the set
extract_abc_args["root"] = instance.data["proxyRoots"]
args["root"] = instance.data["proxyRoots"]
with suspended_refresh(suspend=suspend):
with maintained_selection():
cmds.select(instance.data["proxy"])
extract_alembic(**extract_abc_args)
extract_alembic(**args)
representation = {
"name": "proxy",
@ -172,9 +197,146 @@ class ExtractAlembic(publish.Extractor):
def get_members_and_roots(self, instance):
return instance[:], instance.data.get("setMembers")
@classmethod
def get_attribute_defs(cls):
override_defs = {
"attr": {
"def": TextDef,
"kwargs": {
"label": "Custom Attributes",
"placeholder": "attr1; attr2; ...",
}
},
"attrPrefix": {
"def": TextDef,
"kwargs": {
"label": "Custom Attributes Prefix",
"placeholder": "prefix1; prefix2; ...",
}
},
"dataFormat": {
"def": EnumDef,
"kwargs": {
"label": "Data Format",
"items": ["ogawa", "HDF"],
}
},
"melPerFrameCallback": {
"def": TextDef,
"kwargs": {
"label": "melPerFrameCallback",
}
},
"melPostJobCallback": {
"def": TextDef,
"kwargs": {
"label": "melPostJobCallback",
}
},
"preRollStartFrame": {
"def": NumberDef,
"kwargs": {
"label": "Start frame for preroll",
"tooltip": (
"The frame to start scene evaluation at. This is used"
" to set the starting frame for time dependent "
"translations and can be used to evaluate run-up that"
" isn't actually translated."
),
}
},
"pythonPerFrameCallback": {
"def": TextDef,
"kwargs": {
"label": "pythonPerFrameCallback",
}
},
"pythonPostJobCallback": {
"def": TextDef,
"kwargs": {
"label": "pythonPostJobCallback",
}
},
"stripNamespaces": {
"def": NumberDef,
"kwargs": {
"label": "stripNamespaces",
"tooltip": (
"If this flag is present namespaces will be stripped "
"off of the node before being written to Alembic. The "
"int after the flag specifies how many namespaces will"
" be stripped off of the node name. Be careful that "
"the new stripped name does not collide with other "
"sibling node names.\n\nExamples:\n taco:foo:bar would"
" be written as just bar with -sn 0\ntaco:foo:bar "
"would be written as foo:bar with -sn 1"
),
}
},
"userAttr": {
"def": TextDef,
"kwargs": {
"label": "userAttr",
}
},
"userAttrPrefix": {
"def": TextDef,
"kwargs": {
"label": "userAttrPrefix",
}
},
"visibleOnly": {
"def": BoolDef,
"kwargs": {
"label": "Visible Only",
}
}
}
defs = super(ExtractAlembic, cls).get_attribute_defs()
defs.extend([
UISeparatorDef("sep_alembic_options"),
UILabelDef("Alembic Options"),
])
# The Arguments that can be modified by the Publisher
export_overrides = set(getattr(cls, "export_overrides", set()))
# What we have set in the Settings as defaults.
flags = set(getattr(cls, "flags", set()))
enabled_flags = [x for x in flags if x in export_overrides]
flag_overrides = export_overrides - set(override_defs.keys())
defs.append(
EnumDef(
"flag_overrides",
flag_overrides,
default=enabled_flags,
multiselection=True,
label="Export Flags",
)
)
for key, value in override_defs.items():
if key not in export_overrides:
continue
kwargs = value["kwargs"]
kwargs["default"] = getattr(cls, key, None)
defs.append(
value["def"](key, **value["kwargs"])
)
defs.append(
UISeparatorDef("sep_alembic_options")
)
return defs
class ExtractAnimation(ExtractAlembic):
label = "Extract Animation"
label = "Extract Animation (Alembic)"
families = ["animation"]
def get_members_and_roots(self, instance):

View file

@ -1150,6 +1150,32 @@
"pointcache",
"model",
"vrayproxy.alembic"
],
"flags": [
"stripNamespaces",
"writeColorSets",
"writeNormals",
"worldSpace"
],
"attr": "",
"attrPrefix": "",
"dataFormat": "ogawa",
"melPerFrameCallback": "",
"melPostJobCallback": "",
"preRollStartFrame": 0,
"pythonPerFrameCallback": "",
"pythonPostJobCallback": "",
"userAttr": "",
"userAttrPrefix": "",
"visibleOnly": false,
"export_overrides": [
"attr",
"attrPrefix",
"worldSpace",
"writeNormals",
"writeFaceSets",
"renderableOnly",
"visibleOnly"
]
},
"ExtractObj": {

View file

@ -158,114 +158,10 @@
"key": "refresh",
"label": "Refresh"
},
{
"type": "enum",
"key": "abc_export_flags",
"multiselection": true,
"label": "Export Flags (.abc)",
"enum_items": [
{"autoSubd": "autoSubd"},
{"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"},
{"eulerFilter": "eulerFilter"},
{"noNormals": "noNormals"},
{"preRoll": "preRoll"},
{"renderableOnly": "renderableOnly"},
{"selection": "selection"},
{"stripNamespaces": "stripNamespaces"},
{"uvWrite": "uvWrite"},
{"uvsOnly": "uvsOnly"},
{"verbose": "verbose"},
{"wholeFrameGeo": "wholeFrameGeo"},
{"worldSpace": "worldSpace"},
{"writeColorSets": "writeColorSets"},
{"writeCreases": "writeCreases"},
{"writeFaceSets": "writeFaceSets"},
{"writeUVSets": "writeUVSets"},
{"writeVisibility": "writeVisibility"}
]
},
{
"type": "enum",
"key": "abc_export_overrides",
"multiselection": true,
"label": "Export Overrides (.abc)",
"enum_items": [
{"attr": "attr"},
{"attrPrefix": "attrPrefix"},
{"autoSubd": "autoSubd"},
{"dataFormat": "dataFormat"},
{"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"},
{"endFrame": "endFrame"},
{"eulerFilter": "eulerFilter"},
{"frameRange": "frameRange"},
{"frameRelativeSample": "frameRelativeSample"},
{"melPerFrameCallback": "melPerFrameCallback"},
{"melPostJobCallback": "melPostJobCallback"},
{"noNormals": "noNormals"},
{"preRoll": "preRoll"},
{"preRollStartFrame": "preRollStartFrame"},
{"pythonPerFrameCallback": "pythonPerFrameCallback"},
{"pythonPostJobCallback": "pythonPostJobCallback"},
{"renderableOnly": "renderableOnly"},
{"root": "root"},
{"selection": "selection"},
{"startFrame": "startFrame"},
{"step": "step"},
{"stripNamespaces": "stripNamespaces"},
{"userAttr": "userAttr"},
{"userAttrPrefix": "userAttrPrefix"},
{"uvWrite": "uvWrite"},
{"uvsOnly": "uvsOnly"},
{"verbose": "verbose"},
{"wholeFrameGeo": "wholeFrameGeo"},
{"worldSpace": "worldSpace"},
{"writeColorSets": "writeColorSets"},
{"writeCreases": "writeCreases"},
{"writeFaceSets": "writeFaceSets"},
{"writeUVSets": "writeUVSets"},
{"writeVisibility": "writeVisibility"}
]
},
{
"type": "number",
"key": "step",
"label": "Step",
"minimum": 0.0,
"decimal": 4
},
{
"type": "boolean",
"key": "visibleOnly",
"label": "Visible Only default"
},
{
"type": "text",
"key": "attr",
"label": "Attributes"
},
{
"type": "text",
"key": "attrPrefix",
"label": "Attr Prefix"
},
{
"type": "enum",
"key": "dataFormat",
"label": "Data Format",
"enum_items": [
{
"ogawa": "ogawa"
},
{
"HDF": "HDF"
}
]
},
{
"type": "number",
"key": "preRollStartFrame",
"label": "Pre Roll Start Frame",
"minimum": 0
}
]
},

View file

@ -750,26 +750,6 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "ExtractAlembic",
"label": "Extract Alembic",
"checkbox_key": "enabled",
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"key": "families",
"label": "Families",
"type": "list",
"object_type": "text"
}
]
},
{
"type": "dict",
"collapsible": true,
@ -1008,6 +988,165 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "ExtractAlembic",
"label": "Extract Pointcache/Animation",
"checkbox_key": "enabled",
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"key": "families",
"label": "Families",
"type": "list",
"object_type": "text"
},
{
"type": "splitter"
},
{
"type": "label",
"label": "Export Defaults"
},
{
"type": "enum",
"key": "flags",
"multiselection": true,
"label": "Flags",
"enum_items": [
{"autoSubd": "autoSubd"},
{"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"},
{"eulerFilter": "eulerFilter"},
{"noNormals": "noNormals"},
{"preRoll": "preRoll"},
{"renderableOnly": "renderableOnly"},
{"stripNamespaces": "stripNamespaces"},
{"uvWrite": "uvWrite"},
{"uvsOnly": "uvsOnly"},
{"verbose": "verbose"},
{"wholeFrameGeo": "wholeFrameGeo"},
{"worldSpace": "worldSpace"},
{"writeColorSets": "writeColorSets"},
{"writeFaceSets": "writeFaceSets"},
{"writeUVSets": "writeUVSets"},
{"writeVisibility": "writeVisibility"}
]
},
{
"type": "text",
"key": "attr",
"label": "Attributes"
},
{
"type": "text",
"key": "attrPrefix",
"label": "Attr Prefix"
},
{
"type": "enum",
"key": "dataFormat",
"label": "Data Format",
"enum_items": [
{
"ogawa": "ogawa"
},
{
"HDF": "HDF"
}
]
},
{
"type": "text",
"key": "melPerFrameCallback",
"label": "melPerFrameCallback"
},
{
"type": "text",
"key": "melPostJobCallback",
"label": "melPostJobCallback"
},
{
"type": "number",
"key": "preRollStartFrame",
"label": "Pre Roll Start Frame",
"minimum": 0
},
{
"type": "text",
"key": "pythonPerFrameCallback",
"label": "pythonPerFrameCallback"
},
{
"type": "text",
"key": "pythonPostJobCallback",
"label": "pythonPostJobCallback"
},
{
"type": "text",
"key": "userAttr",
"label": "userAttr"
},
{
"type": "text",
"key": "userAttrPrefix",
"label": "userAttrPrefix"
},
{
"type": "boolean",
"key": "visibleOnly",
"label": "visibleOnly"
},
{
"type": "splitter"
},
{
"type": "label",
"label": "These attributes are exposed to the user when publishing with default values from above."
},
{
"type": "enum",
"key": "export_overrides",
"multiselection": true,
"label": "Export Overrides",
"enum_items": [
{"attr": "attr"},
{"attrPrefix": "attrPrefix"},
{"autoSubd": "autoSubd"},
{"dataFormat": "dataFormat"},
{"dontSkipUnwrittenFrames": "dontSkipUnwrittenFrames"},
{"eulerFilter": "eulerFilter"},
{"melPerFrameCallback": "melPerFrameCallback"},
{"melPostJobCallback": "melPostJobCallback"},
{"noNormals": "noNormals"},
{"preRoll": "preRoll"},
{"preRollStartFrame": "preRollStartFrame"},
{"pythonPerFrameCallback": "pythonPerFrameCallback"},
{"pythonPostJobCallback": "pythonPostJobCallback"},
{"renderableOnly": "renderableOnly"},
{"stripNamespaces": "stripNamespaces"},
{"userAttr": "userAttr"},
{"userAttrPrefix": "userAttrPrefix"},
{"uvWrite": "uvWrite"},
{"uvsOnly": "uvsOnly"},
{"verbose": "verbose"},
{"visibleOnly": "visibleOnly"},
{"wholeFrameGeo": "wholeFrameGeo"},
{"worldSpace": "worldSpace"},
{"writeColorSets": "writeColorSets"},
{"writeCreases": "writeCreases"},
{"writeFaceSets": "writeFaceSets"},
{"writeNormals": "writeNormals"},
{"writeUVSets": "writeUVSets"},
{"writeVisibility": "writeVisibility"}
]
}
]
},
{
"type": "dict",
"collapsible": true,

View file

@ -1485,7 +1485,7 @@ class CreatorAttrsWidget(QtWidgets.QWidget):
class PublishPluginAttrsWidget(QtWidgets.QWidget):
"""Widget showing publsish plugin attributes for selected instances.
Attributes are defined on publish plugins. Publihs plugin may define
Attributes are defined on publish plugins. Publish plugin may define
attribute definitions but must inherit `OpenPypePyblishPluginMixin`
(~/openpype/pipeline/publish). At the moment requires to implement
`get_attribute_defs` and `convert_attribute_values` class methods.