mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into bugfix/inventory_latest_version
This commit is contained in:
commit
edf87575f7
18 changed files with 273 additions and 36 deletions
|
|
@ -67,8 +67,6 @@ class Commands:
|
|||
install_ayon_plugins,
|
||||
get_global_context,
|
||||
)
|
||||
from ayon_core.tools.utils.host_tools import show_publish
|
||||
from ayon_core.tools.utils.lib import qt_app_context
|
||||
|
||||
# Register target and host
|
||||
import pyblish.api
|
||||
|
|
@ -134,6 +132,8 @@ class Commands:
|
|||
print(plugin)
|
||||
|
||||
if gui:
|
||||
from ayon_core.tools.utils.host_tools import show_publish
|
||||
from ayon_core.tools.utils.lib import qt_app_context
|
||||
with qt_app_context():
|
||||
show_publish()
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ class ValidateFileSaved(pyblish.api.ContextPlugin,
|
|||
if not context.data["currentFile"]:
|
||||
# File has not been saved at all and has no filename
|
||||
raise PublishValidationError(
|
||||
"Current file is empty. Save the file before continuing."
|
||||
"Current workfile has not been saved yet.\n"
|
||||
"Save the workfile before continuing."
|
||||
)
|
||||
|
||||
# Do not validate workfile has unsaved changes if only instances
|
||||
|
|
|
|||
|
|
@ -0,0 +1,94 @@
|
|||
import inspect
|
||||
from typing import List
|
||||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError,
|
||||
RepairAction
|
||||
)
|
||||
import ayon_core.hosts.blender.api.action
|
||||
|
||||
|
||||
class ValidateModelMeshUvMap1(
|
||||
pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin,
|
||||
):
|
||||
"""Validate model mesh uvs are named `map1`.
|
||||
|
||||
This is solely to get them to work nicely for the Maya pipeline.
|
||||
"""
|
||||
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
label = "Mesh UVs named map1"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction,
|
||||
RepairAction]
|
||||
optional = True
|
||||
enabled = False
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance) -> List:
|
||||
|
||||
invalid = []
|
||||
for obj in instance:
|
||||
if obj.mode != "OBJECT":
|
||||
cls.log.warning(
|
||||
f"Mesh object {obj.name} should be in 'OBJECT' mode"
|
||||
" to be properly checked."
|
||||
)
|
||||
|
||||
obj_data = obj.data
|
||||
if isinstance(obj_data, bpy.types.Mesh):
|
||||
mesh = obj_data
|
||||
|
||||
# Ignore mesh without UVs
|
||||
if not mesh.uv_layers:
|
||||
continue
|
||||
|
||||
# If mesh has map1 all is ok
|
||||
if mesh.uv_layers.get("map1"):
|
||||
continue
|
||||
|
||||
cls.log.warning(
|
||||
f"Mesh object {obj.name} should be in 'OBJECT' mode"
|
||||
" to be properly checked."
|
||||
)
|
||||
invalid.append(obj)
|
||||
|
||||
return invalid
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
for obj in cls.get_invalid(instance):
|
||||
mesh = obj.data
|
||||
|
||||
# Rename the first UV set to map1
|
||||
mesh.uv_layers[0].name = "map1"
|
||||
|
||||
def process(self, instance):
|
||||
if not self.is_active(instance.data):
|
||||
return
|
||||
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise PublishValidationError(
|
||||
f"Meshes found in instance without valid UV's: {invalid}",
|
||||
description=self.get_description()
|
||||
)
|
||||
|
||||
def get_description(self):
|
||||
return inspect.cleandoc(
|
||||
"""## Meshes must have map1 uv set
|
||||
|
||||
To accompany a better Maya-focused pipeline with Alembics it is
|
||||
expected that a Mesh has a `map1` UV set. Blender defaults to
|
||||
a UV set named `UVMap` and thus needs to be renamed.
|
||||
|
||||
"""
|
||||
)
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
import inspect
|
||||
from typing import List
|
||||
|
||||
import mathutils
|
||||
|
|
@ -5,29 +6,26 @@ import bpy
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from ayon_core.hosts.blender.api import plugin, lib
|
||||
import ayon_core.hosts.blender.api.action
|
||||
from ayon_core.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
OptionalPyblishPluginMixin,
|
||||
PublishValidationError
|
||||
PublishValidationError,
|
||||
RepairAction
|
||||
)
|
||||
|
||||
|
||||
class ValidateTransformZero(pyblish.api.InstancePlugin,
|
||||
OptionalPyblishPluginMixin):
|
||||
"""Transforms can't have any values
|
||||
|
||||
To solve this issue, try freezing the transforms. So long
|
||||
as the transforms, rotation and scale values are zero,
|
||||
you're all good.
|
||||
|
||||
"""
|
||||
"""Transforms can't have any values"""
|
||||
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
label = "Transform Zero"
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction]
|
||||
actions = [ayon_core.hosts.blender.api.action.SelectInvalidAction,
|
||||
RepairAction]
|
||||
|
||||
_identity = mathutils.Matrix()
|
||||
|
||||
|
|
@ -51,5 +49,46 @@ class ValidateTransformZero(pyblish.api.InstancePlugin,
|
|||
names = ", ".join(obj.name for obj in invalid)
|
||||
raise PublishValidationError(
|
||||
"Objects found in instance which do not"
|
||||
f" have transform set to zero: {names}"
|
||||
f" have transform set to zero: {names}",
|
||||
description=self.get_description()
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
|
||||
invalid = cls.get_invalid(instance)
|
||||
if not invalid:
|
||||
return
|
||||
|
||||
context = plugin.create_blender_context(
|
||||
active=invalid[0], selected=invalid
|
||||
)
|
||||
with lib.maintained_selection():
|
||||
with bpy.context.temp_override(**context):
|
||||
plugin.deselect_all()
|
||||
for obj in invalid:
|
||||
obj.select_set(True)
|
||||
|
||||
# TODO: Preferably this does allow custom pivot point locations
|
||||
# and if so, this should likely apply to the delta instead
|
||||
# using `bpy.ops.object.transforms_to_deltas(mode="ALL")`
|
||||
bpy.ops.object.transform_apply(location=True,
|
||||
rotation=True,
|
||||
scale=True)
|
||||
|
||||
def get_description(self):
|
||||
return inspect.cleandoc(
|
||||
"""## Transforms can't have any values.
|
||||
|
||||
The location, rotation and scale on the transform must be at
|
||||
the default values. This also goes for the delta transforms.
|
||||
|
||||
To solve this issue, try freezing the transforms:
|
||||
- `Object` > `Apply` > `All Transforms`
|
||||
|
||||
Using the Repair action directly will do the same.
|
||||
|
||||
So long as the transforms, rotation and scale values are zero,
|
||||
you're all good.
|
||||
"""
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
from ayon_core.lib import NumberDef
|
||||
|
||||
from ayon_core.hosts.fusion.api.plugin import GenericCreateSaver
|
||||
from ayon_core.hosts.fusion.api import get_current_comp
|
||||
|
||||
|
||||
class CreateImageSaver(GenericCreateSaver):
|
||||
|
|
|
|||
|
|
@ -50,11 +50,11 @@ class ImportTemplateLoader(load.LoaderPlugin):
|
|||
self.__class__.__name__
|
||||
)
|
||||
|
||||
def update(self, container, context):
|
||||
pass
|
||||
def update(self, container, context):
|
||||
pass
|
||||
|
||||
def remove(self, container):
|
||||
pass
|
||||
def remove(self, container):
|
||||
pass
|
||||
|
||||
|
||||
class ImportWorkfileLoader(ImportTemplateLoader):
|
||||
|
|
|
|||
|
|
@ -147,7 +147,6 @@ class HoudiniCreatorBase(object):
|
|||
def create_instance_node(
|
||||
folder_path, node_name, parent, node_type="geometry"
|
||||
):
|
||||
# type: (str, str, str) -> hou.Node
|
||||
"""Create node representing instance.
|
||||
|
||||
Arguments:
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ class CreateHDA(plugin.HoudiniCreator):
|
|||
maintain_selection = False
|
||||
|
||||
def _check_existing(self, folder_path, product_name):
|
||||
# type: (str) -> bool
|
||||
# type: (str, str) -> bool
|
||||
"""Check if existing product name versions already exists."""
|
||||
# Get all products of the current folder
|
||||
project_name = self.project_name
|
||||
|
|
|
|||
|
|
@ -40,9 +40,11 @@ class _NodeTypeAttrib(object):
|
|||
return "{}.{}".format(node, self.colour_space)
|
||||
|
||||
def __str__(self):
|
||||
return "_NodeTypeAttrib(name={}, fname={}, "
|
||||
"computed_fname={}, colour_space={})".format(
|
||||
self.name, self.fname, self.computed_fname, self.colour_space)
|
||||
return (
|
||||
"_NodeTypeAttrib(name={}, fname={}, "
|
||||
"computed_fname={}, colour_space={})".format(
|
||||
self.name, self.fname, self.computed_fname, self.colour_space)
|
||||
)
|
||||
|
||||
|
||||
NODETYPES = {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ class CollectAutoImage(pyblish.api.ContextPlugin):
|
|||
"""
|
||||
|
||||
label = "Collect Auto Image"
|
||||
order = pyblish.api.CollectorOrder
|
||||
hosts = ["photoshop"]
|
||||
order = pyblish.api.CollectorOrder + 0.2
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ class CollectAutoImageRefresh(pyblish.api.ContextPlugin):
|
|||
"""
|
||||
|
||||
label = "Collect Auto Image Refresh"
|
||||
order = pyblish.api.CollectorOrder
|
||||
hosts = ["photoshop"]
|
||||
order = pyblish.api.CollectorOrder + 0.2
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,87 @@ class _Cache:
|
|||
initialized = False
|
||||
|
||||
|
||||
def _new_get_last_versions(
|
||||
self,
|
||||
project_name,
|
||||
product_ids,
|
||||
active=True,
|
||||
fields=None,
|
||||
own_attributes=False
|
||||
):
|
||||
"""Query last version entities by product ids.
|
||||
|
||||
Args:
|
||||
project_name (str): Project where to look for representation.
|
||||
product_ids (Iterable[str]): Product ids.
|
||||
active (Optional[bool]): Receive active/inactive entities.
|
||||
Both are returned when 'None' is passed.
|
||||
fields (Optional[Iterable[str]]): fields to be queried
|
||||
for representations.
|
||||
own_attributes (Optional[bool]): Attribute values that are
|
||||
not explicitly set on entity will have 'None' value.
|
||||
|
||||
Returns:
|
||||
dict[str, dict[str, Any]]: Last versions by product id.
|
||||
|
||||
"""
|
||||
if fields:
|
||||
fields = set(fields)
|
||||
fields.add("productId")
|
||||
|
||||
versions = self.get_versions(
|
||||
project_name,
|
||||
product_ids=product_ids,
|
||||
latest=True,
|
||||
hero=False,
|
||||
active=active,
|
||||
fields=fields,
|
||||
own_attributes=own_attributes
|
||||
)
|
||||
return {
|
||||
version["productId"]: version
|
||||
for version in versions
|
||||
}
|
||||
|
||||
|
||||
def _new_get_last_version_by_product_id(
|
||||
self,
|
||||
project_name,
|
||||
product_id,
|
||||
active=True,
|
||||
fields=None,
|
||||
own_attributes=False
|
||||
):
|
||||
"""Query last version entity by product id.
|
||||
|
||||
Args:
|
||||
project_name (str): Project where to look for representation.
|
||||
product_id (str): Product id.
|
||||
active (Optional[bool]): Receive active/inactive entities.
|
||||
Both are returned when 'None' is passed.
|
||||
fields (Optional[Iterable[str]]): fields to be queried
|
||||
for representations.
|
||||
own_attributes (Optional[bool]): Attribute values that are
|
||||
not explicitly set on entity will have 'None' value.
|
||||
|
||||
Returns:
|
||||
Union[dict[str, Any], None]: Queried version entity or None.
|
||||
|
||||
"""
|
||||
versions = self.get_versions(
|
||||
project_name,
|
||||
product_ids=[product_id],
|
||||
latest=True,
|
||||
hero=False,
|
||||
active=active,
|
||||
fields=fields,
|
||||
own_attributes=own_attributes
|
||||
)
|
||||
for version in versions:
|
||||
return version
|
||||
return None
|
||||
|
||||
|
||||
def _new_get_last_version_by_product_name(
|
||||
self,
|
||||
project_name,
|
||||
|
|
@ -73,9 +154,15 @@ def initialize_ayon_connection(force=False):
|
|||
semver.VersionInfo.parse(ayon_api.__version__).to_tuple()
|
||||
)
|
||||
# TODO remove mokey patching after when AYON api is safely updated
|
||||
fix_last_version_by_product_name = ayon_api_version < (1, 0, 2)
|
||||
fix_before_1_0_2 = ayon_api_version < (1, 0, 2)
|
||||
# Monkey patching to fix 'get_last_version_by_product_name'
|
||||
if fix_last_version_by_product_name:
|
||||
if fix_before_1_0_2:
|
||||
ayon_api.ServerAPI.get_last_versions = (
|
||||
_new_get_last_versions
|
||||
)
|
||||
ayon_api.ServerAPI.get_last_version_by_product_id = (
|
||||
_new_get_last_version_by_product_id
|
||||
)
|
||||
ayon_api.ServerAPI.get_last_version_by_product_name = (
|
||||
_new_get_last_version_by_product_name
|
||||
)
|
||||
|
|
@ -85,12 +172,22 @@ def initialize_ayon_connection(force=False):
|
|||
if ayon_api.is_connection_created():
|
||||
con = ayon_api.get_server_api_connection()
|
||||
# Monkey patching to fix 'get_last_version_by_product_name'
|
||||
if fix_last_version_by_product_name:
|
||||
def _con_wrapper(*args, **kwargs):
|
||||
if fix_before_1_0_2:
|
||||
def _lvs_wrapper(*args, **kwargs):
|
||||
return _new_get_last_versions(
|
||||
con, *args, **kwargs
|
||||
)
|
||||
def _lv_by_pi_wrapper(*args, **kwargs):
|
||||
return _new_get_last_version_by_product_id(
|
||||
con, *args, **kwargs
|
||||
)
|
||||
def _lv_by_pn_wrapper(*args, **kwargs):
|
||||
return _new_get_last_version_by_product_name(
|
||||
con, *args, **kwargs
|
||||
)
|
||||
con.get_last_version_by_product_name = _con_wrapper
|
||||
con.get_last_versions = _lvs_wrapper
|
||||
con.get_last_version_by_product_id = _lv_by_pi_wrapper
|
||||
con.get_last_version_by_product_name = _lv_by_pn_wrapper
|
||||
con.set_site_id(site_id)
|
||||
con.set_client_version(version)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -46,7 +46,6 @@ class DeadlineModule(AYONAddon, IPluginPaths):
|
|||
|
||||
@staticmethod
|
||||
def get_deadline_pools(webservice, log=None):
|
||||
# type: (str) -> list
|
||||
"""Get pools from Deadline.
|
||||
Args:
|
||||
webservice (str): Server url.
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ class CollectDefaultDeadlineServer(pyblish.api.ContextPlugin):
|
|||
DL webservice addresses must be configured first in System Settings for
|
||||
project settings enum to work.
|
||||
|
||||
Default webservice could be overriden by
|
||||
Default webservice could be overridden by
|
||||
`project_settings/deadline/deadline_servers`. Currently only single url
|
||||
is expected.
|
||||
|
||||
This url could be overriden by some hosts directly on instances with
|
||||
This url could be overridden by some hosts directly on instances with
|
||||
`CollectDeadlineServerFromInstance`.
|
||||
"""
|
||||
|
||||
|
|
|
|||
|
|
@ -149,7 +149,7 @@ class ValidateExpectedFiles(pyblish.api.InstancePlugin):
|
|||
"""
|
||||
# no frames in file name at all, eg 'renderCompositingMain.withLut.mov'
|
||||
if not frame_placeholder:
|
||||
return set([file_name_template])
|
||||
return {file_name_template}
|
||||
|
||||
real_expected_rendered = set()
|
||||
src_padding_exp = "%0{}d".format(len(frame_placeholder))
|
||||
|
|
|
|||
|
|
@ -404,7 +404,7 @@ class OpenPypeTileAssembler(DeadlinePlugin):
|
|||
Args:
|
||||
output_width (int): Width of output image.
|
||||
output_height (int): Height of output image.
|
||||
tiles_info (list): List of tile items, each item must be
|
||||
tile_info (list): List of tile items, each item must be
|
||||
dictionary with `filepath`, `pos_x` and `pos_y` keys
|
||||
representing path to file and x, y coordinates on output
|
||||
image where top-left point of tile item should start.
|
||||
|
|
|
|||
|
|
@ -89,6 +89,10 @@ class PublishPuginsModel(BaseSettingsModel):
|
|||
default_factory=ValidatePluginModel,
|
||||
title="Validate Mesh No Negative Scale"
|
||||
)
|
||||
ValidateModelMeshUvMap1: ValidatePluginModel = SettingsField(
|
||||
default_factory=ValidatePluginModel,
|
||||
title="Validate Model Mesh Has UV map named map1"
|
||||
)
|
||||
ValidateTransformZero: ValidatePluginModel = SettingsField(
|
||||
default_factory=ValidatePluginModel,
|
||||
title="Validate Transform Zero"
|
||||
|
|
@ -181,6 +185,11 @@ DEFAULT_BLENDER_PUBLISH_SETTINGS = {
|
|||
"optional": False,
|
||||
"active": True
|
||||
},
|
||||
"ValidateModelMeshUvMap1": {
|
||||
"enabled": False,
|
||||
"optional": True,
|
||||
"active": True
|
||||
},
|
||||
"ValidateTransformZero": {
|
||||
"enabled": False,
|
||||
"optional": True,
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.7"
|
||||
__version__ = "0.1.8"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue