mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-25 05:14:40 +01:00
Merge branch 'develop' into feature/OP-3842_Change-publish-template-settings-location
This commit is contained in:
commit
141735dad8
197 changed files with 2352 additions and 1224 deletions
51
CHANGELOG.md
51
CHANGELOG.md
|
|
@ -1,8 +1,27 @@
|
|||
# Changelog
|
||||
|
||||
## [3.14.1-nightly.3](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
## [3.14.2-nightly.1](https://github.com/pypeclub/OpenPype/tree/HEAD)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.0...HEAD)
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/3.14.1...HEAD)
|
||||
|
||||
**🆕 New features**
|
||||
|
||||
- Houdini: Publishing workfiles [\#3697](https://github.com/pypeclub/OpenPype/pull/3697)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Ftrack status fix typo prgoress -\> progress [\#3761](https://github.com/pypeclub/OpenPype/pull/3761)
|
||||
|
||||
**🔀 Refactored code**
|
||||
|
||||
- General: Move hostdirname functionality into host [\#3749](https://github.com/pypeclub/OpenPype/pull/3749)
|
||||
- Webpublisher: Webpublisher is used as addon [\#3740](https://github.com/pypeclub/OpenPype/pull/3740)
|
||||
- Houdini: Define houdini as addon [\#3735](https://github.com/pypeclub/OpenPype/pull/3735)
|
||||
- Flame: Defined flame as addon [\#3732](https://github.com/pypeclub/OpenPype/pull/3732)
|
||||
|
||||
## [3.14.1](https://github.com/pypeclub/OpenPype/tree/3.14.1) (2022-08-30)
|
||||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.14.1-nightly.4...3.14.1)
|
||||
|
||||
### 📖 Documentation
|
||||
|
||||
|
|
@ -12,35 +31,38 @@
|
|||
**🆕 New features**
|
||||
|
||||
- Webpublisher:change create flatten image into tri state [\#3678](https://github.com/pypeclub/OpenPype/pull/3678)
|
||||
- Blender: validators code correction with settings and defaults [\#3662](https://github.com/pypeclub/OpenPype/pull/3662)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- General: Thumbnail can use project roots [\#3750](https://github.com/pypeclub/OpenPype/pull/3750)
|
||||
- Settings: Remove settings lock on tray exit [\#3720](https://github.com/pypeclub/OpenPype/pull/3720)
|
||||
- General: Added helper getters to modules manager [\#3712](https://github.com/pypeclub/OpenPype/pull/3712)
|
||||
- Unreal: Define unreal as module and use host class [\#3701](https://github.com/pypeclub/OpenPype/pull/3701)
|
||||
- Settings: Lock settings UI session [\#3700](https://github.com/pypeclub/OpenPype/pull/3700)
|
||||
- General: Benevolent context label collector [\#3686](https://github.com/pypeclub/OpenPype/pull/3686)
|
||||
- Ftrack: Store ftrack entities on hierarchy integration to instances [\#3677](https://github.com/pypeclub/OpenPype/pull/3677)
|
||||
- Ftrack: More logs related to auto sync value change [\#3671](https://github.com/pypeclub/OpenPype/pull/3671)
|
||||
- Blender: ops refresh manager after process events [\#3663](https://github.com/pypeclub/OpenPype/pull/3663)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Maya: Fix typo in getPanel argument `with\_focus` -\> `withFocus` [\#3753](https://github.com/pypeclub/OpenPype/pull/3753)
|
||||
- General: Smaller fixes of imports [\#3748](https://github.com/pypeclub/OpenPype/pull/3748)
|
||||
- General: Logger tweaks [\#3741](https://github.com/pypeclub/OpenPype/pull/3741)
|
||||
- Nuke: missing job dependency if multiple bake streams [\#3737](https://github.com/pypeclub/OpenPype/pull/3737)
|
||||
- Nuke: color-space settings from anatomy is working [\#3721](https://github.com/pypeclub/OpenPype/pull/3721)
|
||||
- Settings: Fix studio default anatomy save [\#3716](https://github.com/pypeclub/OpenPype/pull/3716)
|
||||
- Maya: Use project name instead of project code [\#3709](https://github.com/pypeclub/OpenPype/pull/3709)
|
||||
- Settings: Fix project overrides save [\#3708](https://github.com/pypeclub/OpenPype/pull/3708)
|
||||
- Workfiles tool: Fix published workfile filtering [\#3704](https://github.com/pypeclub/OpenPype/pull/3704)
|
||||
- PS, AE: Provide default variant value for workfile subset [\#3703](https://github.com/pypeclub/OpenPype/pull/3703)
|
||||
- RoyalRender: handle host name that is not set [\#3695](https://github.com/pypeclub/OpenPype/pull/3695)
|
||||
- Flame: retime is working on clip publishing [\#3684](https://github.com/pypeclub/OpenPype/pull/3684)
|
||||
- Webpublisher: added check for empty context [\#3682](https://github.com/pypeclub/OpenPype/pull/3682)
|
||||
|
||||
**🔀 Refactored code**
|
||||
|
||||
- General: Move delivery logic to pipeline [\#3751](https://github.com/pypeclub/OpenPype/pull/3751)
|
||||
- General: Host addons cleanup [\#3744](https://github.com/pypeclub/OpenPype/pull/3744)
|
||||
- Webpublisher: Webpublisher is used as addon [\#3740](https://github.com/pypeclub/OpenPype/pull/3740)
|
||||
- Photoshop: Defined photoshop as addon [\#3736](https://github.com/pypeclub/OpenPype/pull/3736)
|
||||
- Harmony: Defined harmony as addon [\#3734](https://github.com/pypeclub/OpenPype/pull/3734)
|
||||
- General: Module interfaces cleanup [\#3731](https://github.com/pypeclub/OpenPype/pull/3731)
|
||||
|
|
@ -71,7 +93,6 @@
|
|||
**🚀 Enhancements**
|
||||
|
||||
- Ftrack: Addiotional component metadata [\#3685](https://github.com/pypeclub/OpenPype/pull/3685)
|
||||
- Ftrack: Set task status on farm publishing [\#3680](https://github.com/pypeclub/OpenPype/pull/3680)
|
||||
- Ftrack: Set task status on task creation in integrate hierarchy [\#3675](https://github.com/pypeclub/OpenPype/pull/3675)
|
||||
- Maya: Disable rendering of all lights for render instances submitted through Deadline. [\#3661](https://github.com/pypeclub/OpenPype/pull/3661)
|
||||
- General: Optimized OCIO configs [\#3650](https://github.com/pypeclub/OpenPype/pull/3650)
|
||||
|
|
@ -105,37 +126,19 @@
|
|||
|
||||
[Full Changelog](https://github.com/pypeclub/OpenPype/compare/CI/3.13.0-nightly.1...3.13.0)
|
||||
|
||||
**🆕 New features**
|
||||
|
||||
- Support for mutliple installed versions - 3.13 [\#3605](https://github.com/pypeclub/OpenPype/pull/3605)
|
||||
|
||||
**🚀 Enhancements**
|
||||
|
||||
- Editorial: Mix audio use side file for ffmpeg filters [\#3630](https://github.com/pypeclub/OpenPype/pull/3630)
|
||||
- Ftrack: Comment template can contain optional keys [\#3615](https://github.com/pypeclub/OpenPype/pull/3615)
|
||||
- Ftrack: Add more metadata to ftrack components [\#3612](https://github.com/pypeclub/OpenPype/pull/3612)
|
||||
|
||||
**🐛 Bug fixes**
|
||||
|
||||
- Maya: fix aov separator in Redshift [\#3625](https://github.com/pypeclub/OpenPype/pull/3625)
|
||||
- Fix for multi-version build on Mac [\#3622](https://github.com/pypeclub/OpenPype/pull/3622)
|
||||
- Ftrack: Sync hierarchical attributes can handle new created entities [\#3621](https://github.com/pypeclub/OpenPype/pull/3621)
|
||||
- General: Extract review aspect ratio scale is calculated by ffmpeg [\#3620](https://github.com/pypeclub/OpenPype/pull/3620)
|
||||
- Maya: Fix types of default settings [\#3617](https://github.com/pypeclub/OpenPype/pull/3617)
|
||||
- Integrator: Don't force to have dot before frame [\#3611](https://github.com/pypeclub/OpenPype/pull/3611)
|
||||
- AfterEffects: refactored integrate doesnt work formulti frame publishes [\#3610](https://github.com/pypeclub/OpenPype/pull/3610)
|
||||
- Maya look data contents fails with custom attribute on group [\#3607](https://github.com/pypeclub/OpenPype/pull/3607)
|
||||
- TrayPublisher: Fix wrong conflict merge [\#3600](https://github.com/pypeclub/OpenPype/pull/3600)
|
||||
|
||||
**🔀 Refactored code**
|
||||
|
||||
- General: Plugin settings handled by plugins [\#3623](https://github.com/pypeclub/OpenPype/pull/3623)
|
||||
- General: Naive implementation of document create, update, delete [\#3601](https://github.com/pypeclub/OpenPype/pull/3601)
|
||||
|
||||
**Merged pull requests:**
|
||||
|
||||
- Webpublisher: timeout for PS studio processing [\#3619](https://github.com/pypeclub/OpenPype/pull/3619)
|
||||
- Core: translated validate\_containers.py into New publisher style [\#3614](https://github.com/pypeclub/OpenPype/pull/3614)
|
||||
|
||||
## [3.12.2](https://github.com/pypeclub/OpenPype/tree/3.12.2) (2022-07-27)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,44 +1,82 @@
|
|||
# absolute_import is needed to counter the `module has no cmds error` in Maya
|
||||
from __future__ import absolute_import
|
||||
|
||||
import warnings
|
||||
import functools
|
||||
import pyblish.api
|
||||
|
||||
|
||||
def get_errored_instances_from_context(context):
|
||||
|
||||
instances = list()
|
||||
for result in context.data["results"]:
|
||||
if result["instance"] is None:
|
||||
# When instance is None we are on the "context" result
|
||||
continue
|
||||
|
||||
if result["error"]:
|
||||
instances.append(result["instance"])
|
||||
|
||||
return instances
|
||||
class ActionDeprecatedWarning(DeprecationWarning):
|
||||
pass
|
||||
|
||||
|
||||
def get_errored_plugins_from_data(context):
|
||||
"""Get all failed validation plugins
|
||||
|
||||
Args:
|
||||
context (object):
|
||||
|
||||
Returns:
|
||||
list of plugins which failed during validation
|
||||
def deprecated(new_destination):
|
||||
"""Mark functions as deprecated.
|
||||
|
||||
It will result in a warning being emitted when the function is used.
|
||||
"""
|
||||
|
||||
plugins = list()
|
||||
results = context.data.get("results", [])
|
||||
for result in results:
|
||||
if result["success"] is True:
|
||||
continue
|
||||
plugins.append(result["plugin"])
|
||||
func = None
|
||||
if callable(new_destination):
|
||||
func = new_destination
|
||||
new_destination = None
|
||||
|
||||
return plugins
|
||||
def _decorator(decorated_func):
|
||||
if new_destination is None:
|
||||
warning_message = (
|
||||
" Please check content of deprecated function to figure out"
|
||||
" possible replacement."
|
||||
)
|
||||
else:
|
||||
warning_message = " Please replace your usage with '{}'.".format(
|
||||
new_destination
|
||||
)
|
||||
|
||||
@functools.wraps(decorated_func)
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.simplefilter("always", ActionDeprecatedWarning)
|
||||
warnings.warn(
|
||||
(
|
||||
"Call to deprecated function '{}'"
|
||||
"\nFunction was moved or removed.{}"
|
||||
).format(decorated_func.__name__, warning_message),
|
||||
category=ActionDeprecatedWarning,
|
||||
stacklevel=4
|
||||
)
|
||||
return decorated_func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
if func is None:
|
||||
return _decorator
|
||||
return _decorator(func)
|
||||
|
||||
|
||||
@deprecated("openpype.pipeline.publish.get_errored_instances_from_context")
|
||||
def get_errored_instances_from_context(context):
|
||||
"""
|
||||
Deprecated:
|
||||
Since 3.14.* will be removed in 3.16.* or later.
|
||||
"""
|
||||
|
||||
from openpype.pipeline.publish import get_errored_instances_from_context
|
||||
|
||||
return get_errored_instances_from_context(context)
|
||||
|
||||
|
||||
@deprecated("openpype.pipeline.publish.get_errored_plugins_from_context")
|
||||
def get_errored_plugins_from_data(context):
|
||||
"""
|
||||
Deprecated:
|
||||
Since 3.14.* will be removed in 3.16.* or later.
|
||||
"""
|
||||
|
||||
from openpype.pipeline.publish import get_errored_plugins_from_context
|
||||
|
||||
return get_errored_plugins_from_context(context)
|
||||
|
||||
|
||||
# 'RepairAction' and 'RepairContextAction' were moved to
|
||||
# 'openpype.pipeline.publish' please change you imports.
|
||||
# There is no "reasonable" way hot mark these classes as deprecated to show
|
||||
# warning of wrong import.
|
||||
# Deprecated since 3.14.* will be removed in 3.16.*
|
||||
class RepairAction(pyblish.api.Action):
|
||||
"""Repairs the action
|
||||
|
||||
|
|
@ -65,6 +103,7 @@ class RepairAction(pyblish.api.Action):
|
|||
plugin.repair(instance)
|
||||
|
||||
|
||||
# Deprecated since 3.14.* will be removed in 3.16.*
|
||||
class RepairContextAction(pyblish.api.Action):
|
||||
"""Repairs the action
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,6 @@ from .plugin import (
|
|||
ValidateContentsOrder,
|
||||
ValidateSceneOrder,
|
||||
ValidateMeshOrder,
|
||||
ValidationException
|
||||
)
|
||||
|
||||
# temporary fix, might
|
||||
|
|
@ -94,8 +93,6 @@ __all__ = [
|
|||
"RepairAction",
|
||||
"RepairContextAction",
|
||||
|
||||
"ValidationException",
|
||||
|
||||
# get contextual data
|
||||
"version_up",
|
||||
"get_asset",
|
||||
|
|
|
|||
|
|
@ -45,6 +45,11 @@ from .entities import (
|
|||
get_workfile_info,
|
||||
)
|
||||
|
||||
from .operations import (
|
||||
create_project,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"OpenPypeMongoConnection",
|
||||
|
||||
|
|
@ -88,4 +93,6 @@ __all__ = (
|
|||
"get_thumbnail_id_from_source",
|
||||
|
||||
"get_workfile_info",
|
||||
|
||||
"create_project",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ from bson.objectid import ObjectId
|
|||
from pymongo import DeleteOne, InsertOne, UpdateOne
|
||||
|
||||
from .mongo import get_project_connection
|
||||
from .entities import get_project
|
||||
|
||||
REMOVED_VALUE = object()
|
||||
|
||||
|
|
@ -24,6 +25,7 @@ CURRENT_SUBSET_SCHEMA = "openpype:subset-3.0"
|
|||
CURRENT_VERSION_SCHEMA = "openpype:version-3.0"
|
||||
CURRENT_REPRESENTATION_SCHEMA = "openpype:representation-2.0"
|
||||
CURRENT_WORKFILE_INFO_SCHEMA = "openpype:workfile-1.0"
|
||||
CURRENT_THUMBNAIL_SCHEMA = "openpype:thumbnail-1.0"
|
||||
|
||||
|
||||
def _create_or_convert_to_mongo_id(mongo_id):
|
||||
|
|
@ -195,6 +197,29 @@ def new_representation_doc(
|
|||
}
|
||||
|
||||
|
||||
def new_thumbnail_doc(data=None, entity_id=None):
|
||||
"""Create skeleton data of thumbnail document.
|
||||
|
||||
Args:
|
||||
data (Dict[str, Any]): Thumbnail document data.
|
||||
entity_id (Union[str, ObjectId]): Predefined id of document. New id is
|
||||
created if not passed.
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: Skeleton of thumbnail document.
|
||||
"""
|
||||
|
||||
if data is None:
|
||||
data = {}
|
||||
|
||||
return {
|
||||
"_id": _create_or_convert_to_mongo_id(entity_id),
|
||||
"type": "thumbnail",
|
||||
"schema": CURRENT_THUMBNAIL_SCHEMA,
|
||||
"data": data
|
||||
}
|
||||
|
||||
|
||||
def new_workfile_info_doc(
|
||||
filename, asset_id, task_name, files, data=None, entity_id=None
|
||||
):
|
||||
|
|
@ -638,3 +663,89 @@ class OperationsSession(object):
|
|||
operation = DeleteOperation(project_name, entity_type, entity_id)
|
||||
self.add(operation)
|
||||
return operation
|
||||
|
||||
|
||||
def create_project(project_name, project_code, library_project=False):
|
||||
"""Create project using OpenPype settings.
|
||||
|
||||
This project creation function is not validating project document on
|
||||
creation. It is because project document is created blindly with only
|
||||
minimum required information about project which is it's name, code, type
|
||||
and schema.
|
||||
|
||||
Entered project name must be unique and project must not exist yet.
|
||||
|
||||
Note:
|
||||
This function is here to be OP v4 ready but in v3 has more logic
|
||||
to do. That's why inner imports are in the body.
|
||||
|
||||
Args:
|
||||
project_name(str): New project name. Should be unique.
|
||||
project_code(str): Project's code should be unique too.
|
||||
library_project(bool): Project is library project.
|
||||
|
||||
Raises:
|
||||
ValueError: When project name already exists in MongoDB.
|
||||
|
||||
Returns:
|
||||
dict: Created project document.
|
||||
"""
|
||||
|
||||
from openpype.settings import ProjectSettings, SaveWarningExc
|
||||
from openpype.pipeline.schema import validate
|
||||
|
||||
if get_project(project_name, fields=["name"]):
|
||||
raise ValueError("Project with name \"{}\" already exists".format(
|
||||
project_name
|
||||
))
|
||||
|
||||
if not PROJECT_NAME_REGEX.match(project_name):
|
||||
raise ValueError((
|
||||
"Project name \"{}\" contain invalid characters"
|
||||
).format(project_name))
|
||||
|
||||
project_doc = {
|
||||
"type": "project",
|
||||
"name": project_name,
|
||||
"data": {
|
||||
"code": project_code,
|
||||
"library_project": library_project
|
||||
},
|
||||
"schema": CURRENT_PROJECT_SCHEMA
|
||||
}
|
||||
|
||||
op_session = OperationsSession()
|
||||
# Insert document with basic data
|
||||
create_op = op_session.create_entity(
|
||||
project_name, project_doc["type"], project_doc
|
||||
)
|
||||
op_session.commit()
|
||||
|
||||
# Load ProjectSettings for the project and save it to store all attributes
|
||||
# and Anatomy
|
||||
try:
|
||||
project_settings_entity = ProjectSettings(project_name)
|
||||
project_settings_entity.save()
|
||||
except SaveWarningExc as exc:
|
||||
print(str(exc))
|
||||
except Exception:
|
||||
op_session.delete_entity(
|
||||
project_name, project_doc["type"], create_op.entity_id
|
||||
)
|
||||
op_session.commit()
|
||||
raise
|
||||
|
||||
project_doc = get_project(project_name)
|
||||
|
||||
try:
|
||||
# Validate created project document
|
||||
validate(project_doc)
|
||||
except Exception:
|
||||
# Remove project if is not valid
|
||||
op_session.delete_entity(
|
||||
project_name, project_doc["type"], create_op.entity_id
|
||||
)
|
||||
op_session.commit()
|
||||
raise
|
||||
|
||||
return project_doc
|
||||
|
|
|
|||
|
|
@ -1,13 +1,22 @@
|
|||
from .host import (
|
||||
HostBase,
|
||||
)
|
||||
|
||||
from .interfaces import (
|
||||
IWorkfileHost,
|
||||
ILoadHost,
|
||||
INewPublisher,
|
||||
)
|
||||
|
||||
from .dirmap import HostDirmap
|
||||
|
||||
|
||||
__all__ = (
|
||||
"HostBase",
|
||||
|
||||
"IWorkfileHost",
|
||||
"ILoadHost",
|
||||
"INewPublisher",
|
||||
|
||||
"HostDirmap",
|
||||
)
|
||||
|
|
|
|||
205
openpype/host/dirmap.py
Normal file
205
openpype/host/dirmap.py
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
"""Dirmap functionality used in host integrations inside DCCs.
|
||||
|
||||
Idea for current dirmap implementation was used from Maya where is possible to
|
||||
enter source and destination roots and maya will try each found source
|
||||
in referenced file replace with each destionation paths. First path which
|
||||
exists is used.
|
||||
"""
|
||||
|
||||
import os
|
||||
from abc import ABCMeta, abstractmethod
|
||||
|
||||
import six
|
||||
|
||||
from openpype.lib import Logger
|
||||
from openpype.modules import ModulesManager
|
||||
from openpype.settings import get_project_settings
|
||||
from openpype.settings.lib import get_site_local_overrides
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class HostDirmap(object):
|
||||
"""Abstract class for running dirmap on a workfile in a host.
|
||||
|
||||
Dirmap is used to translate paths inside of host workfile from one
|
||||
OS to another. (Eg. arstist created workfile on Win, different artists
|
||||
opens same file on Linux.)
|
||||
|
||||
Expects methods to be implemented inside of host:
|
||||
on_dirmap_enabled: run host code for enabling dirmap
|
||||
do_dirmap: run host code to do actual remapping
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self, host_name, project_name, project_settings=None, sync_module=None
|
||||
):
|
||||
self.host_name = host_name
|
||||
self.project_name = project_name
|
||||
self._project_settings = project_settings
|
||||
self._sync_module = sync_module # to limit reinit of Modules
|
||||
self._log = None
|
||||
self._mapping = None # cache mapping
|
||||
|
||||
@property
|
||||
def sync_module(self):
|
||||
if self._sync_module is None:
|
||||
manager = ModulesManager()
|
||||
self._sync_module = manager["sync_server"]
|
||||
return self._sync_module
|
||||
|
||||
@property
|
||||
def project_settings(self):
|
||||
if self._project_settings is None:
|
||||
self._project_settings = get_project_settings(self.project_name)
|
||||
return self._project_settings
|
||||
|
||||
@property
|
||||
def log(self):
|
||||
if self._log is None:
|
||||
self._log = Logger.get_logger(self.__class__.__name__)
|
||||
return self._log
|
||||
|
||||
@abstractmethod
|
||||
def on_enable_dirmap(self):
|
||||
"""Run host dependent operation for enabling dirmap if necessary."""
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def dirmap_routine(self, source_path, destination_path):
|
||||
"""Run host dependent remapping from source_path to destination_path"""
|
||||
pass
|
||||
|
||||
def process_dirmap(self):
|
||||
# type: (dict) -> None
|
||||
"""Go through all paths in Settings and set them using `dirmap`.
|
||||
|
||||
If artists has Site Sync enabled, take dirmap mapping directly from
|
||||
Local Settings when artist is syncing workfile locally.
|
||||
|
||||
Args:
|
||||
project_settings (dict): Settings for current project.
|
||||
"""
|
||||
|
||||
if not self._mapping:
|
||||
self._mapping = self.get_mappings(self.project_settings)
|
||||
if not self._mapping:
|
||||
return
|
||||
|
||||
self.log.info("Processing directory mapping ...")
|
||||
self.on_enable_dirmap()
|
||||
self.log.info("mapping:: {}".format(self._mapping))
|
||||
|
||||
for k, sp in enumerate(self._mapping["source-path"]):
|
||||
dst = self._mapping["destination-path"][k]
|
||||
try:
|
||||
print("{} -> {}".format(sp, dst))
|
||||
self.dirmap_routine(sp, dst)
|
||||
except IndexError:
|
||||
# missing corresponding destination path
|
||||
self.log.error((
|
||||
"invalid dirmap mapping, missing corresponding"
|
||||
" destination directory."
|
||||
))
|
||||
break
|
||||
except RuntimeError:
|
||||
self.log.error(
|
||||
"invalid path {} -> {}, mapping not registered".format(
|
||||
sp, dst
|
||||
)
|
||||
)
|
||||
continue
|
||||
|
||||
def get_mappings(self, project_settings):
|
||||
"""Get translation from source-path to destination-path.
|
||||
|
||||
It checks if Site Sync is enabled and user chose to use local
|
||||
site, in that case configuration in Local Settings takes precedence
|
||||
"""
|
||||
|
||||
local_mapping = self._get_local_sync_dirmap(project_settings)
|
||||
dirmap_label = "{}-dirmap".format(self.host_name)
|
||||
if (
|
||||
not self.project_settings[self.host_name].get(dirmap_label)
|
||||
and not local_mapping
|
||||
):
|
||||
return {}
|
||||
mapping_settings = self.project_settings[self.host_name][dirmap_label]
|
||||
mapping_enabled = mapping_settings["enabled"] or bool(local_mapping)
|
||||
if not mapping_enabled:
|
||||
return {}
|
||||
|
||||
mapping = (
|
||||
local_mapping
|
||||
or mapping_settings["paths"]
|
||||
or {}
|
||||
)
|
||||
|
||||
if (
|
||||
not mapping
|
||||
or not mapping.get("destination-path")
|
||||
or not mapping.get("source-path")
|
||||
):
|
||||
return {}
|
||||
return mapping
|
||||
|
||||
def _get_local_sync_dirmap(self, project_settings):
|
||||
"""
|
||||
Returns dirmap if synch to local project is enabled.
|
||||
|
||||
Only valid mapping is from roots of remote site to local site set
|
||||
in Local Settings.
|
||||
|
||||
Args:
|
||||
project_settings (dict)
|
||||
Returns:
|
||||
dict : { "source-path": [XXX], "destination-path": [YYYY]}
|
||||
"""
|
||||
|
||||
mapping = {}
|
||||
|
||||
if not project_settings["global"]["sync_server"]["enabled"]:
|
||||
return mapping
|
||||
|
||||
project_name = os.getenv("AVALON_PROJECT")
|
||||
|
||||
active_site = self.sync_module.get_local_normalized_site(
|
||||
self.sync_module.get_active_site(project_name))
|
||||
remote_site = self.sync_module.get_local_normalized_site(
|
||||
self.sync_module.get_remote_site(project_name))
|
||||
self.log.debug(
|
||||
"active {} - remote {}".format(active_site, remote_site)
|
||||
)
|
||||
|
||||
if (
|
||||
active_site == "local"
|
||||
and project_name in self.sync_module.get_enabled_projects()
|
||||
and active_site != remote_site
|
||||
):
|
||||
sync_settings = self.sync_module.get_sync_project_setting(
|
||||
project_name,
|
||||
exclude_locals=False,
|
||||
cached=False)
|
||||
|
||||
active_overrides = get_site_local_overrides(
|
||||
project_name, active_site)
|
||||
remote_overrides = get_site_local_overrides(
|
||||
project_name, remote_site)
|
||||
|
||||
self.log.debug("local overrides {}".format(active_overrides))
|
||||
self.log.debug("remote overrides {}".format(remote_overrides))
|
||||
for root_name, active_site_dir in active_overrides.items():
|
||||
remote_site_dir = (
|
||||
remote_overrides.get(root_name)
|
||||
or sync_settings["sites"][remote_site]["root"][root_name]
|
||||
)
|
||||
if os.path.isdir(active_site_dir):
|
||||
if "destination-path" not in mapping:
|
||||
mapping["destination-path"] = []
|
||||
mapping["destination-path"].append(active_site_dir)
|
||||
|
||||
if "source-path" not in mapping:
|
||||
mapping["source-path"] = []
|
||||
mapping["source-path"].append(remote_site_dir)
|
||||
|
||||
self.log.debug("local sync mapping:: {}".format(mapping))
|
||||
return mapping
|
||||
|
|
@ -1,37 +1,12 @@
|
|||
import logging
|
||||
import contextlib
|
||||
from abc import ABCMeta, abstractproperty, abstractmethod
|
||||
from abc import ABCMeta, abstractproperty
|
||||
import six
|
||||
|
||||
# NOTE can't import 'typing' because of issues in Maya 2020
|
||||
# - shiboken crashes on 'typing' module import
|
||||
|
||||
|
||||
class MissingMethodsError(ValueError):
|
||||
"""Exception when host miss some required methods for specific workflow.
|
||||
|
||||
Args:
|
||||
host (HostBase): Host implementation where are missing methods.
|
||||
missing_methods (list[str]): List of missing methods.
|
||||
"""
|
||||
|
||||
def __init__(self, host, missing_methods):
|
||||
joined_missing = ", ".join(
|
||||
['"{}"'.format(item) for item in missing_methods]
|
||||
)
|
||||
if isinstance(host, HostBase):
|
||||
host_name = host.name
|
||||
else:
|
||||
try:
|
||||
host_name = host.__file__.replace("\\", "/").split("/")[-3]
|
||||
except Exception:
|
||||
host_name = str(host)
|
||||
message = (
|
||||
"Host \"{}\" miss methods {}".format(host_name, joined_missing)
|
||||
)
|
||||
super(MissingMethodsError, self).__init__(message)
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class HostBase(object):
|
||||
"""Base of host implementation class.
|
||||
|
|
@ -185,347 +160,3 @@ class HostBase(object):
|
|||
yield
|
||||
finally:
|
||||
pass
|
||||
|
||||
|
||||
class ILoadHost:
|
||||
"""Implementation requirements to be able use reference of representations.
|
||||
|
||||
The load plugins can do referencing even without implementation of methods
|
||||
here, but switch and removement of containers would not be possible.
|
||||
|
||||
Questions:
|
||||
- Is list container dependency of host or load plugins?
|
||||
- Should this be directly in HostBase?
|
||||
- how to find out if referencing is available?
|
||||
- do we need to know that?
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_missing_load_methods(host):
|
||||
"""Look for missing methods on "old type" host implementation.
|
||||
|
||||
Method is used for validation of implemented functions related to
|
||||
loading. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
list[str]: Missing method implementations for loading workflow.
|
||||
"""
|
||||
|
||||
if isinstance(host, ILoadHost):
|
||||
return []
|
||||
|
||||
required = ["ls"]
|
||||
missing = []
|
||||
for name in required:
|
||||
if not hasattr(host, name):
|
||||
missing.append(name)
|
||||
return missing
|
||||
|
||||
@staticmethod
|
||||
def validate_load_methods(host):
|
||||
"""Validate implemented methods of "old type" host for load workflow.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
implementation.
|
||||
"""
|
||||
missing = ILoadHost.get_missing_load_methods(host)
|
||||
if missing:
|
||||
raise MissingMethodsError(host, missing)
|
||||
|
||||
@abstractmethod
|
||||
def get_containers(self):
|
||||
"""Retreive referenced containers from scene.
|
||||
|
||||
This can be implemented in hosts where referencing can be used.
|
||||
|
||||
Todo:
|
||||
Rename function to something more self explanatory.
|
||||
Suggestion: 'get_containers'
|
||||
|
||||
Returns:
|
||||
list[dict]: Information about loaded containers.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
# --- Deprecated method names ---
|
||||
def ls(self):
|
||||
"""Deprecated variant of 'get_containers'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.get_containers()
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class IWorkfileHost:
|
||||
"""Implementation requirements to be able use workfile utils and tool."""
|
||||
|
||||
@staticmethod
|
||||
def get_missing_workfile_methods(host):
|
||||
"""Look for missing methods on "old type" host implementation.
|
||||
|
||||
Method is used for validation of implemented functions related to
|
||||
workfiles. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
list[str]: Missing method implementations for workfiles workflow.
|
||||
"""
|
||||
|
||||
if isinstance(host, IWorkfileHost):
|
||||
return []
|
||||
|
||||
required = [
|
||||
"open_file",
|
||||
"save_file",
|
||||
"current_file",
|
||||
"has_unsaved_changes",
|
||||
"file_extensions",
|
||||
"work_root",
|
||||
]
|
||||
missing = []
|
||||
for name in required:
|
||||
if not hasattr(host, name):
|
||||
missing.append(name)
|
||||
return missing
|
||||
|
||||
@staticmethod
|
||||
def validate_workfile_methods(host):
|
||||
"""Validate methods of "old type" host for workfiles workflow.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
implementation.
|
||||
"""
|
||||
|
||||
missing = IWorkfileHost.get_missing_workfile_methods(host)
|
||||
if missing:
|
||||
raise MissingMethodsError(host, missing)
|
||||
|
||||
@abstractmethod
|
||||
def get_workfile_extensions(self):
|
||||
"""Extensions that can be used as save.
|
||||
|
||||
Questions:
|
||||
This could potentially use 'HostDefinition'.
|
||||
"""
|
||||
|
||||
return []
|
||||
|
||||
@abstractmethod
|
||||
def save_workfile(self, dst_path=None):
|
||||
"""Save currently opened scene.
|
||||
|
||||
Args:
|
||||
dst_path (str): Where the current scene should be saved. Or use
|
||||
current path if 'None' is passed.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def open_workfile(self, filepath):
|
||||
"""Open passed filepath in the host.
|
||||
|
||||
Args:
|
||||
filepath (str): Path to workfile.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_current_workfile(self):
|
||||
"""Retreive path to current opened file.
|
||||
|
||||
Returns:
|
||||
str: Path to file which is currently opened.
|
||||
None: If nothing is opened.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def workfile_has_unsaved_changes(self):
|
||||
"""Currently opened scene is saved.
|
||||
|
||||
Not all hosts can know if current scene is saved because the API of
|
||||
DCC does not support it.
|
||||
|
||||
Returns:
|
||||
bool: True if scene is saved and False if has unsaved
|
||||
modifications.
|
||||
None: Can't tell if workfiles has modifications.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def work_root(self, session):
|
||||
"""Modify workdir per host.
|
||||
|
||||
Default implementation keeps workdir untouched.
|
||||
|
||||
Warnings:
|
||||
We must handle this modification with more sofisticated way because
|
||||
this can't be called out of DCC so opening of last workfile
|
||||
(calculated before DCC is launched) is complicated. Also breaking
|
||||
defined work template is not a good idea.
|
||||
Only place where it's really used and can make sense is Maya. There
|
||||
workspace.mel can modify subfolders where to look for maya files.
|
||||
|
||||
Args:
|
||||
session (dict): Session context data.
|
||||
|
||||
Returns:
|
||||
str: Path to new workdir.
|
||||
"""
|
||||
|
||||
return session["AVALON_WORKDIR"]
|
||||
|
||||
# --- Deprecated method names ---
|
||||
def file_extensions(self):
|
||||
"""Deprecated variant of 'get_workfile_extensions'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
return self.get_workfile_extensions()
|
||||
|
||||
def save_file(self, dst_path=None):
|
||||
"""Deprecated variant of 'save_workfile'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
self.save_workfile()
|
||||
|
||||
def open_file(self, filepath):
|
||||
"""Deprecated variant of 'open_workfile'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.open_workfile(filepath)
|
||||
|
||||
def current_file(self):
|
||||
"""Deprecated variant of 'get_current_workfile'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.get_current_workfile()
|
||||
|
||||
def has_unsaved_changes(self):
|
||||
"""Deprecated variant of 'workfile_has_unsaved_changes'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.workfile_has_unsaved_changes()
|
||||
|
||||
|
||||
class INewPublisher:
|
||||
"""Functions related to new creation system in new publisher.
|
||||
|
||||
New publisher is not storing information only about each created instance
|
||||
but also some global data. At this moment are data related only to context
|
||||
publish plugins but that can extend in future.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_missing_publish_methods(host):
|
||||
"""Look for missing methods on "old type" host implementation.
|
||||
|
||||
Method is used for validation of implemented functions related to
|
||||
new publish creation. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Host module where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
list[str]: Missing method implementations for new publsher
|
||||
workflow.
|
||||
"""
|
||||
|
||||
if isinstance(host, INewPublisher):
|
||||
return []
|
||||
|
||||
required = [
|
||||
"get_context_data",
|
||||
"update_context_data",
|
||||
]
|
||||
missing = []
|
||||
for name in required:
|
||||
if not hasattr(host, name):
|
||||
missing.append(name)
|
||||
return missing
|
||||
|
||||
@staticmethod
|
||||
def validate_publish_methods(host):
|
||||
"""Validate implemented methods of "old type" host.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Host module to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
implementation.
|
||||
"""
|
||||
missing = INewPublisher.get_missing_publish_methods(host)
|
||||
if missing:
|
||||
raise MissingMethodsError(host, missing)
|
||||
|
||||
@abstractmethod
|
||||
def get_context_data(self):
|
||||
"""Get global data related to creation-publishing from workfile.
|
||||
|
||||
These data are not related to any created instance but to whole
|
||||
publishing context. Not saving/returning them will cause that each
|
||||
reset of publishing resets all values to default ones.
|
||||
|
||||
Context data can contain information about enabled/disabled publish
|
||||
plugins or other values that can be filled by artist.
|
||||
|
||||
Returns:
|
||||
dict: Context data stored using 'update_context_data'.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update_context_data(self, data, changes):
|
||||
"""Store global context data to workfile.
|
||||
|
||||
Called when some values in context data has changed.
|
||||
|
||||
Without storing the values in a way that 'get_context_data' would
|
||||
return them will each reset of publishing cause loose of filled values
|
||||
by artist. Best practice is to store values into workfile, if possible.
|
||||
|
||||
Args:
|
||||
data (dict): New data as are.
|
||||
changes (dict): Only data that has been changed. Each value has
|
||||
tuple with '(<old>, <new>)' value.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
|
|
|||
370
openpype/host/interfaces.py
Normal file
370
openpype/host/interfaces.py
Normal file
|
|
@ -0,0 +1,370 @@
|
|||
from abc import ABCMeta, abstractmethod
|
||||
import six
|
||||
|
||||
|
||||
class MissingMethodsError(ValueError):
|
||||
"""Exception when host miss some required methods for specific workflow.
|
||||
|
||||
Args:
|
||||
host (HostBase): Host implementation where are missing methods.
|
||||
missing_methods (list[str]): List of missing methods.
|
||||
"""
|
||||
|
||||
def __init__(self, host, missing_methods):
|
||||
joined_missing = ", ".join(
|
||||
['"{}"'.format(item) for item in missing_methods]
|
||||
)
|
||||
host_name = getattr(host, "name", None)
|
||||
if not host_name:
|
||||
try:
|
||||
host_name = host.__file__.replace("\\", "/").split("/")[-3]
|
||||
except Exception:
|
||||
host_name = str(host)
|
||||
message = (
|
||||
"Host \"{}\" miss methods {}".format(host_name, joined_missing)
|
||||
)
|
||||
super(MissingMethodsError, self).__init__(message)
|
||||
|
||||
|
||||
class ILoadHost:
|
||||
"""Implementation requirements to be able use reference of representations.
|
||||
|
||||
The load plugins can do referencing even without implementation of methods
|
||||
here, but switch and removement of containers would not be possible.
|
||||
|
||||
Questions:
|
||||
- Is list container dependency of host or load plugins?
|
||||
- Should this be directly in HostBase?
|
||||
- how to find out if referencing is available?
|
||||
- do we need to know that?
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_missing_load_methods(host):
|
||||
"""Look for missing methods on "old type" host implementation.
|
||||
|
||||
Method is used for validation of implemented functions related to
|
||||
loading. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
list[str]: Missing method implementations for loading workflow.
|
||||
"""
|
||||
|
||||
if isinstance(host, ILoadHost):
|
||||
return []
|
||||
|
||||
required = ["ls"]
|
||||
missing = []
|
||||
for name in required:
|
||||
if not hasattr(host, name):
|
||||
missing.append(name)
|
||||
return missing
|
||||
|
||||
@staticmethod
|
||||
def validate_load_methods(host):
|
||||
"""Validate implemented methods of "old type" host for load workflow.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
implementation.
|
||||
"""
|
||||
missing = ILoadHost.get_missing_load_methods(host)
|
||||
if missing:
|
||||
raise MissingMethodsError(host, missing)
|
||||
|
||||
@abstractmethod
|
||||
def get_containers(self):
|
||||
"""Retreive referenced containers from scene.
|
||||
|
||||
This can be implemented in hosts where referencing can be used.
|
||||
|
||||
Todo:
|
||||
Rename function to something more self explanatory.
|
||||
Suggestion: 'get_containers'
|
||||
|
||||
Returns:
|
||||
list[dict]: Information about loaded containers.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
# --- Deprecated method names ---
|
||||
def ls(self):
|
||||
"""Deprecated variant of 'get_containers'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.get_containers()
|
||||
|
||||
|
||||
@six.add_metaclass(ABCMeta)
|
||||
class IWorkfileHost:
|
||||
"""Implementation requirements to be able use workfile utils and tool."""
|
||||
|
||||
@staticmethod
|
||||
def get_missing_workfile_methods(host):
|
||||
"""Look for missing methods on "old type" host implementation.
|
||||
|
||||
Method is used for validation of implemented functions related to
|
||||
workfiles. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
list[str]: Missing method implementations for workfiles workflow.
|
||||
"""
|
||||
|
||||
if isinstance(host, IWorkfileHost):
|
||||
return []
|
||||
|
||||
required = [
|
||||
"open_file",
|
||||
"save_file",
|
||||
"current_file",
|
||||
"has_unsaved_changes",
|
||||
"file_extensions",
|
||||
"work_root",
|
||||
]
|
||||
missing = []
|
||||
for name in required:
|
||||
if not hasattr(host, name):
|
||||
missing.append(name)
|
||||
return missing
|
||||
|
||||
@staticmethod
|
||||
def validate_workfile_methods(host):
|
||||
"""Validate methods of "old type" host for workfiles workflow.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Object of host to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
implementation.
|
||||
"""
|
||||
|
||||
missing = IWorkfileHost.get_missing_workfile_methods(host)
|
||||
if missing:
|
||||
raise MissingMethodsError(host, missing)
|
||||
|
||||
@abstractmethod
|
||||
def get_workfile_extensions(self):
|
||||
"""Extensions that can be used as save.
|
||||
|
||||
Questions:
|
||||
This could potentially use 'HostDefinition'.
|
||||
"""
|
||||
|
||||
return []
|
||||
|
||||
@abstractmethod
|
||||
def save_workfile(self, dst_path=None):
|
||||
"""Save currently opened scene.
|
||||
|
||||
Args:
|
||||
dst_path (str): Where the current scene should be saved. Or use
|
||||
current path if 'None' is passed.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def open_workfile(self, filepath):
|
||||
"""Open passed filepath in the host.
|
||||
|
||||
Args:
|
||||
filepath (str): Path to workfile.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def get_current_workfile(self):
|
||||
"""Retreive path to current opened file.
|
||||
|
||||
Returns:
|
||||
str: Path to file which is currently opened.
|
||||
None: If nothing is opened.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def workfile_has_unsaved_changes(self):
|
||||
"""Currently opened scene is saved.
|
||||
|
||||
Not all hosts can know if current scene is saved because the API of
|
||||
DCC does not support it.
|
||||
|
||||
Returns:
|
||||
bool: True if scene is saved and False if has unsaved
|
||||
modifications.
|
||||
None: Can't tell if workfiles has modifications.
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
def work_root(self, session):
|
||||
"""Modify workdir per host.
|
||||
|
||||
Default implementation keeps workdir untouched.
|
||||
|
||||
Warnings:
|
||||
We must handle this modification with more sofisticated way because
|
||||
this can't be called out of DCC so opening of last workfile
|
||||
(calculated before DCC is launched) is complicated. Also breaking
|
||||
defined work template is not a good idea.
|
||||
Only place where it's really used and can make sense is Maya. There
|
||||
workspace.mel can modify subfolders where to look for maya files.
|
||||
|
||||
Args:
|
||||
session (dict): Session context data.
|
||||
|
||||
Returns:
|
||||
str: Path to new workdir.
|
||||
"""
|
||||
|
||||
return session["AVALON_WORKDIR"]
|
||||
|
||||
# --- Deprecated method names ---
|
||||
def file_extensions(self):
|
||||
"""Deprecated variant of 'get_workfile_extensions'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
return self.get_workfile_extensions()
|
||||
|
||||
def save_file(self, dst_path=None):
|
||||
"""Deprecated variant of 'save_workfile'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
self.save_workfile()
|
||||
|
||||
def open_file(self, filepath):
|
||||
"""Deprecated variant of 'open_workfile'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.open_workfile(filepath)
|
||||
|
||||
def current_file(self):
|
||||
"""Deprecated variant of 'get_current_workfile'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.get_current_workfile()
|
||||
|
||||
def has_unsaved_changes(self):
|
||||
"""Deprecated variant of 'workfile_has_unsaved_changes'.
|
||||
|
||||
Todo:
|
||||
Remove when all usages are replaced.
|
||||
"""
|
||||
|
||||
return self.workfile_has_unsaved_changes()
|
||||
|
||||
|
||||
class INewPublisher:
|
||||
"""Functions related to new creation system in new publisher.
|
||||
|
||||
New publisher is not storing information only about each created instance
|
||||
but also some global data. At this moment are data related only to context
|
||||
publish plugins but that can extend in future.
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_missing_publish_methods(host):
|
||||
"""Look for missing methods on "old type" host implementation.
|
||||
|
||||
Method is used for validation of implemented functions related to
|
||||
new publish creation. Checks only existence of methods.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Host module where to look for
|
||||
required methods.
|
||||
|
||||
Returns:
|
||||
list[str]: Missing method implementations for new publsher
|
||||
workflow.
|
||||
"""
|
||||
|
||||
if isinstance(host, INewPublisher):
|
||||
return []
|
||||
|
||||
required = [
|
||||
"get_context_data",
|
||||
"update_context_data",
|
||||
]
|
||||
missing = []
|
||||
for name in required:
|
||||
if not hasattr(host, name):
|
||||
missing.append(name)
|
||||
return missing
|
||||
|
||||
@staticmethod
|
||||
def validate_publish_methods(host):
|
||||
"""Validate implemented methods of "old type" host.
|
||||
|
||||
Args:
|
||||
Union[ModuleType, HostBase]: Host module to validate.
|
||||
|
||||
Raises:
|
||||
MissingMethodsError: If there are missing methods on host
|
||||
implementation.
|
||||
"""
|
||||
missing = INewPublisher.get_missing_publish_methods(host)
|
||||
if missing:
|
||||
raise MissingMethodsError(host, missing)
|
||||
|
||||
@abstractmethod
|
||||
def get_context_data(self):
|
||||
"""Get global data related to creation-publishing from workfile.
|
||||
|
||||
These data are not related to any created instance but to whole
|
||||
publishing context. Not saving/returning them will cause that each
|
||||
reset of publishing resets all values to default ones.
|
||||
|
||||
Context data can contain information about enabled/disabled publish
|
||||
plugins or other values that can be filled by artist.
|
||||
|
||||
Returns:
|
||||
dict: Context data stored using 'update_context_data'.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
def update_context_data(self, data, changes):
|
||||
"""Store global context data to workfile.
|
||||
|
||||
Called when some values in context data has changed.
|
||||
|
||||
Without storing the values in a way that 'get_context_data' would
|
||||
return them will each reset of publishing cause loose of filled values
|
||||
by artist. Best practice is to store values into workfile, if possible.
|
||||
|
||||
Args:
|
||||
data (dict): New data as are.
|
||||
changes (dict): Only data that has been changed. Each value has
|
||||
tuple with '(<old>, <new>)' value.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish.api
|
||||
from openpype.action import get_errored_plugins_from_data
|
||||
from openpype.lib import version_up
|
||||
from openpype.pipeline.publish import get_errored_plugins_from_context
|
||||
|
||||
from openpype.hosts.aftereffects.api import get_stub
|
||||
|
||||
|
|
@ -18,7 +18,7 @@ class IncrementWorkfile(pyblish.api.InstancePlugin):
|
|||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
errored_plugins = get_errored_plugins_from_data(instance.context)
|
||||
errored_plugins = get_errored_plugins_from_context(instance.context)
|
||||
if errored_plugins:
|
||||
raise RuntimeError(
|
||||
"Skipping incrementing current file because publishing failed."
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import pyblish.api
|
||||
|
||||
import openpype.api
|
||||
from openpype.pipeline import (
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
PublishXmlValidationError,
|
||||
legacy_io,
|
||||
)
|
||||
from openpype.hosts.aftereffects.api import get_stub
|
||||
|
||||
|
|
@ -50,7 +50,7 @@ class ValidateInstanceAsset(pyblish.api.InstancePlugin):
|
|||
label = "Validate Instance Asset"
|
||||
hosts = ["aftereffects"]
|
||||
actions = [ValidateInstanceAssetRepair]
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
|
||||
def process(self, instance):
|
||||
instance_asset = instance.data["asset"]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import bpy
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.api import get_errored_instances_from_context
|
||||
from openpype.pipeline.publish import get_errored_instances_from_context
|
||||
|
||||
|
||||
class SelectInvalidAction(pyblish.api.Action):
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
from typing import List
|
||||
|
||||
import mathutils
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.blender.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin):
|
||||
|
|
@ -14,21 +16,18 @@ class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin):
|
|||
in Unreal and Blender.
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["camera"]
|
||||
category = "geometry"
|
||||
version = (0, 1, 0)
|
||||
label = "Zero Keyframe"
|
||||
actions = [openpype.hosts.blender.api.action.SelectInvalidAction]
|
||||
|
||||
_identity = mathutils.Matrix()
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance) -> List:
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
invalid = []
|
||||
for obj in [obj for obj in instance]:
|
||||
if obj.type == "CAMERA":
|
||||
for obj in instance:
|
||||
if isinstance(obj, bpy.types.Object) and obj.type == "CAMERA":
|
||||
if obj.animation_data and obj.animation_data.action:
|
||||
action = obj.animation_data.action
|
||||
frames_set = set()
|
||||
|
|
@ -45,4 +44,5 @@ class ValidateCameraZeroKeyframe(pyblish.api.InstancePlugin):
|
|||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
f"Object found in instance is not in Object Mode: {invalid}")
|
||||
f"Camera must have a keyframe at frame 0: {invalid}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,13 +3,14 @@ from typing import List
|
|||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.blender.api.action
|
||||
|
||||
|
||||
class ValidateMeshHasUvs(pyblish.api.InstancePlugin):
|
||||
"""Validate that the current mesh has UV's."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
category = "geometry"
|
||||
|
|
@ -25,7 +26,10 @@ class ValidateMeshHasUvs(pyblish.api.InstancePlugin):
|
|||
for uv_layer in obj.data.uv_layers:
|
||||
for polygon in obj.data.polygons:
|
||||
for loop_index in polygon.loop_indices:
|
||||
if not uv_layer.data[loop_index].uv:
|
||||
if (
|
||||
loop_index >= len(uv_layer.data)
|
||||
or not uv_layer.data[loop_index].uv
|
||||
):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
|
@ -33,20 +37,20 @@ class ValidateMeshHasUvs(pyblish.api.InstancePlugin):
|
|||
@classmethod
|
||||
def get_invalid(cls, instance) -> List:
|
||||
invalid = []
|
||||
# TODO (jasper): only check objects in the collection that will be published?
|
||||
for obj in [
|
||||
obj for obj in instance]:
|
||||
try:
|
||||
if obj.type == 'MESH':
|
||||
# Make sure we are in object mode.
|
||||
bpy.ops.object.mode_set(mode='OBJECT')
|
||||
if not cls.has_uvs(obj):
|
||||
invalid.append(obj)
|
||||
except:
|
||||
continue
|
||||
for obj in instance:
|
||||
if isinstance(obj, bpy.types.Object) and obj.type == 'MESH':
|
||||
if obj.mode != "OBJECT":
|
||||
cls.log.warning(
|
||||
f"Mesh object {obj.name} should be in 'OBJECT' mode"
|
||||
" to be properly checked."
|
||||
)
|
||||
if not cls.has_uvs(obj):
|
||||
invalid.append(obj)
|
||||
return invalid
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError(f"Meshes found in instance without valid UV's: {invalid}")
|
||||
raise RuntimeError(
|
||||
f"Meshes found in instance without valid UV's: {invalid}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -3,28 +3,27 @@ from typing import List
|
|||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.blender.api.action
|
||||
|
||||
|
||||
class ValidateMeshNoNegativeScale(pyblish.api.Validator):
|
||||
"""Ensure that meshes don't have a negative scale."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
category = "geometry"
|
||||
label = "Mesh No Negative Scale"
|
||||
actions = [openpype.hosts.blender.api.action.SelectInvalidAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
invalid = []
|
||||
# TODO (jasper): only check objects in the collection that will be published?
|
||||
for obj in [
|
||||
obj for obj in bpy.data.objects if obj.type == 'MESH'
|
||||
]:
|
||||
if any(v < 0 for v in obj.scale):
|
||||
invalid.append(obj)
|
||||
|
||||
for obj in instance:
|
||||
if isinstance(obj, bpy.types.Object) and obj.type == 'MESH':
|
||||
if any(v < 0 for v in obj.scale):
|
||||
invalid.append(obj)
|
||||
return invalid
|
||||
|
||||
def process(self, instance):
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
from typing import List
|
||||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.blender.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateNoColonsInName(pyblish.api.InstancePlugin):
|
||||
|
|
@ -12,20 +16,20 @@ class ValidateNoColonsInName(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model", "rig"]
|
||||
version = (0, 1, 0)
|
||||
label = "No Colons in names"
|
||||
actions = [openpype.hosts.blender.api.action.SelectInvalidAction]
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance) -> List:
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
invalid = []
|
||||
for obj in [obj for obj in instance]:
|
||||
for obj in instance:
|
||||
if ':' in obj.name:
|
||||
invalid.append(obj)
|
||||
if obj.type == 'ARMATURE':
|
||||
if isinstance(obj, bpy.types.Object) and obj.type == 'ARMATURE':
|
||||
for bone in obj.data.bones:
|
||||
if ':' in bone.name:
|
||||
invalid.append(obj)
|
||||
|
|
@ -36,4 +40,5 @@ class ValidateNoColonsInName(pyblish.api.InstancePlugin):
|
|||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
f"Objects found with colon in name: {invalid}")
|
||||
f"Objects found with colon in name: {invalid}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
from typing import List
|
||||
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
import openpype.hosts.blender.api.action
|
||||
|
||||
|
|
@ -10,26 +12,21 @@ class ValidateObjectIsInObjectMode(pyblish.api.InstancePlugin):
|
|||
order = pyblish.api.ValidatorOrder - 0.01
|
||||
hosts = ["blender"]
|
||||
families = ["model", "rig", "layout"]
|
||||
category = "geometry"
|
||||
label = "Validate Object Mode"
|
||||
actions = [openpype.hosts.blender.api.action.SelectInvalidAction]
|
||||
optional = False
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance) -> List:
|
||||
@staticmethod
|
||||
def get_invalid(instance) -> List:
|
||||
invalid = []
|
||||
for obj in [obj for obj in instance]:
|
||||
try:
|
||||
if obj.type == 'MESH' or obj.type == 'ARMATURE':
|
||||
# Check if the object is in object mode.
|
||||
if not obj.mode == 'OBJECT':
|
||||
invalid.append(obj)
|
||||
except Exception:
|
||||
continue
|
||||
for obj in instance:
|
||||
if isinstance(obj, bpy.types.Object) and obj.mode != "OBJECT":
|
||||
invalid.append(obj)
|
||||
return invalid
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
f"Object found in instance is not in Object Mode: {invalid}")
|
||||
f"Object found in instance is not in Object Mode: {invalid}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
from typing import List
|
||||
|
||||
import mathutils
|
||||
import bpy
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.blender.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateTransformZero(pyblish.api.InstancePlugin):
|
||||
|
|
@ -15,10 +18,9 @@ class ValidateTransformZero(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["blender"]
|
||||
families = ["model"]
|
||||
category = "geometry"
|
||||
version = (0, 1, 0)
|
||||
label = "Transform Zero"
|
||||
actions = [openpype.hosts.blender.api.action.SelectInvalidAction]
|
||||
|
|
@ -28,8 +30,11 @@ class ValidateTransformZero(pyblish.api.InstancePlugin):
|
|||
@classmethod
|
||||
def get_invalid(cls, instance) -> List:
|
||||
invalid = []
|
||||
for obj in [obj for obj in instance]:
|
||||
if obj.matrix_basis != cls._identity:
|
||||
for obj in instance:
|
||||
if (
|
||||
isinstance(obj, bpy.types.Object)
|
||||
and obj.matrix_basis != cls._identity
|
||||
):
|
||||
invalid.append(obj)
|
||||
return invalid
|
||||
|
||||
|
|
@ -37,4 +42,6 @@ class ValidateTransformZero(pyblish.api.InstancePlugin):
|
|||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
raise RuntimeError(
|
||||
f"Object found in instance is not in Object Mode: {invalid}")
|
||||
"Object found in instance has not"
|
||||
f" transform to zero: {invalid}"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,22 +1,10 @@
|
|||
import os
|
||||
|
||||
HOST_DIR = os.path.dirname(
|
||||
os.path.abspath(__file__)
|
||||
from .addon import (
|
||||
HOST_DIR,
|
||||
FlameAddon,
|
||||
)
|
||||
|
||||
|
||||
def add_implementation_envs(env, _app):
|
||||
# Add requirements to DL_PYTHON_HOOK_PATH
|
||||
pype_root = os.environ["OPENPYPE_REPOS_ROOT"]
|
||||
|
||||
env["DL_PYTHON_HOOK_PATH"] = os.path.join(
|
||||
pype_root, "openpype", "hosts", "flame", "startup")
|
||||
env.pop("QT_AUTO_SCREEN_SCALE_FACTOR", None)
|
||||
|
||||
# Set default values if are not already set via settings
|
||||
defaults = {
|
||||
"LOGLEVEL": "DEBUG"
|
||||
}
|
||||
for key, value in defaults.items():
|
||||
if not env.get(key):
|
||||
env[key] = value
|
||||
__all__ = (
|
||||
"HOST_DIR",
|
||||
"FlameAddon",
|
||||
)
|
||||
|
|
|
|||
36
openpype/hosts/flame/addon.py
Normal file
36
openpype/hosts/flame/addon.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import os
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype.modules.interfaces import IHostAddon
|
||||
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class FlameAddon(OpenPypeModule, IHostAddon):
|
||||
name = "flame"
|
||||
host_name = "flame"
|
||||
|
||||
def initialize(self, module_settings):
|
||||
self.enabled = True
|
||||
|
||||
def add_implementation_envs(self, env, _app):
|
||||
# Add requirements to DL_PYTHON_HOOK_PATH
|
||||
env["DL_PYTHON_HOOK_PATH"] = os.path.join(HOST_DIR, "startup")
|
||||
env.pop("QT_AUTO_SCREEN_SCALE_FACTOR", None)
|
||||
|
||||
# Set default values if are not already set via settings
|
||||
defaults = {
|
||||
"LOGLEVEL": "DEBUG"
|
||||
}
|
||||
for key, value in defaults.items():
|
||||
if not env.get(key):
|
||||
env[key] = value
|
||||
|
||||
def get_launch_hook_paths(self, app):
|
||||
if app.host_name != self.host_name:
|
||||
return []
|
||||
return [
|
||||
os.path.join(HOST_DIR, "hooks")
|
||||
]
|
||||
|
||||
def get_workfile_extensions(self):
|
||||
return [".otoc"]
|
||||
|
|
@ -17,9 +17,9 @@ class FusionIncrementCurrentFile(pyblish.api.ContextPlugin):
|
|||
def process(self, context):
|
||||
|
||||
from openpype.lib import version_up
|
||||
from openpype.action import get_errored_plugins_from_data
|
||||
from openpype.pipeline.publish import get_errored_plugins_from_context
|
||||
|
||||
errored_plugins = get_errored_plugins_from_data(context)
|
||||
errored_plugins = get_errored_plugins_from_context(context)
|
||||
if any(plugin.__name__ == "FusionSubmitDeadline"
|
||||
for plugin in errored_plugins):
|
||||
raise RuntimeError("Skipping incrementing current file because "
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype import action
|
||||
from openpype.pipeline.publish import RepairAction
|
||||
|
||||
|
||||
class ValidateBackgroundDepth(pyblish.api.InstancePlugin):
|
||||
|
|
@ -8,7 +8,7 @@ class ValidateBackgroundDepth(pyblish.api.InstancePlugin):
|
|||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
label = "Validate Background Depth 32 bit"
|
||||
actions = [action.RepairAction]
|
||||
actions = [RepairAction]
|
||||
hosts = ["fusion"]
|
||||
families = ["render"]
|
||||
optional = True
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype import action
|
||||
from openpype.pipeline.publish import RepairAction
|
||||
|
||||
|
||||
class ValidateCreateFolderChecked(pyblish.api.InstancePlugin):
|
||||
|
|
@ -11,7 +11,7 @@ class ValidateCreateFolderChecked(pyblish.api.InstancePlugin):
|
|||
"""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
actions = [action.RepairAction]
|
||||
actions = [RepairAction]
|
||||
label = "Validate Create Folder Checked"
|
||||
families = ["render"]
|
||||
hosts = ["fusion"]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
from openpype.action import get_errored_plugins_from_data
|
||||
from openpype.pipeline.publish import get_errored_plugins_from_context
|
||||
from openpype.lib import version_up
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
|
||||
|
|
@ -19,7 +19,7 @@ class IncrementWorkfile(pyblish.api.InstancePlugin):
|
|||
optional = True
|
||||
|
||||
def process(self, instance):
|
||||
errored_plugins = get_errored_plugins_from_data(instance.context)
|
||||
errored_plugins = get_errored_plugins_from_context(instance.context)
|
||||
if errored_plugins:
|
||||
raise RuntimeError(
|
||||
"Skipping incrementing current file because publishing failed."
|
||||
|
|
|
|||
|
|
@ -1,9 +1,12 @@
|
|||
import os
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline import PublishXmlValidationError
|
||||
|
||||
import openpype.hosts.harmony.api as harmony
|
||||
from openpype.pipeline.publish import (
|
||||
ValidateContentsOrder,
|
||||
PublishXmlValidationError,
|
||||
)
|
||||
|
||||
|
||||
class ValidateInstanceRepair(pyblish.api.Action):
|
||||
|
|
@ -37,7 +40,7 @@ class ValidateInstance(pyblish.api.InstancePlugin):
|
|||
label = "Validate Instance"
|
||||
hosts = ["harmony"]
|
||||
actions = [ValidateInstanceRepair]
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
|
||||
def process(self, instance):
|
||||
instance_asset = instance.data["asset"]
|
||||
|
|
|
|||
|
|
@ -1,38 +1,10 @@
|
|||
import os
|
||||
from .addon import (
|
||||
HoudiniAddon,
|
||||
HOUDINI_HOST_DIR,
|
||||
)
|
||||
|
||||
|
||||
def add_implementation_envs(env, _app):
|
||||
# Add requirements to HOUDINI_PATH and HOUDINI_MENU_PATH
|
||||
pype_root = os.environ["OPENPYPE_REPOS_ROOT"]
|
||||
|
||||
startup_path = os.path.join(
|
||||
pype_root, "openpype", "hosts", "houdini", "startup"
|
||||
)
|
||||
new_houdini_path = [startup_path]
|
||||
new_houdini_menu_path = [startup_path]
|
||||
|
||||
old_houdini_path = env.get("HOUDINI_PATH") or ""
|
||||
old_houdini_menu_path = env.get("HOUDINI_MENU_PATH") or ""
|
||||
|
||||
for path in old_houdini_path.split(os.pathsep):
|
||||
if not path:
|
||||
continue
|
||||
|
||||
norm_path = os.path.normpath(path)
|
||||
if norm_path not in new_houdini_path:
|
||||
new_houdini_path.append(norm_path)
|
||||
|
||||
for path in old_houdini_menu_path.split(os.pathsep):
|
||||
if not path:
|
||||
continue
|
||||
|
||||
norm_path = os.path.normpath(path)
|
||||
if norm_path not in new_houdini_menu_path:
|
||||
new_houdini_menu_path.append(norm_path)
|
||||
|
||||
# Add ampersand for unknown reason (Maybe is needed in Houdini?)
|
||||
new_houdini_path.append("&")
|
||||
new_houdini_menu_path.append("&")
|
||||
|
||||
env["HOUDINI_PATH"] = os.pathsep.join(new_houdini_path)
|
||||
env["HOUDINI_MENU_PATH"] = os.pathsep.join(new_houdini_menu_path)
|
||||
__all__ = (
|
||||
"HoudiniAddon",
|
||||
"HOUDINI_HOST_DIR",
|
||||
)
|
||||
|
|
|
|||
55
openpype/hosts/houdini/addon.py
Normal file
55
openpype/hosts/houdini/addon.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import os
|
||||
from openpype.modules import OpenPypeModule
|
||||
from openpype.modules.interfaces import IHostAddon
|
||||
|
||||
HOUDINI_HOST_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
class HoudiniAddon(OpenPypeModule, IHostAddon):
|
||||
name = "houdini"
|
||||
host_name = "houdini"
|
||||
|
||||
def initialize(self, module_settings):
|
||||
self.enabled = True
|
||||
|
||||
def add_implementation_envs(self, env, _app):
|
||||
# Add requirements to HOUDINI_PATH and HOUDINI_MENU_PATH
|
||||
startup_path = os.path.join(HOUDINI_HOST_DIR, "startup")
|
||||
new_houdini_path = [startup_path]
|
||||
new_houdini_menu_path = [startup_path]
|
||||
|
||||
old_houdini_path = env.get("HOUDINI_PATH") or ""
|
||||
old_houdini_menu_path = env.get("HOUDINI_MENU_PATH") or ""
|
||||
|
||||
for path in old_houdini_path.split(os.pathsep):
|
||||
if not path:
|
||||
continue
|
||||
|
||||
norm_path = os.path.normpath(path)
|
||||
if norm_path not in new_houdini_path:
|
||||
new_houdini_path.append(norm_path)
|
||||
|
||||
for path in old_houdini_menu_path.split(os.pathsep):
|
||||
if not path:
|
||||
continue
|
||||
|
||||
norm_path = os.path.normpath(path)
|
||||
if norm_path not in new_houdini_menu_path:
|
||||
new_houdini_menu_path.append(norm_path)
|
||||
|
||||
# Add ampersand for unknown reason (Maybe is needed in Houdini?)
|
||||
new_houdini_path.append("&")
|
||||
new_houdini_menu_path.append("&")
|
||||
|
||||
env["HOUDINI_PATH"] = os.pathsep.join(new_houdini_path)
|
||||
env["HOUDINI_MENU_PATH"] = os.pathsep.join(new_houdini_menu_path)
|
||||
|
||||
def get_launch_hook_paths(self, app):
|
||||
if app.host_name != self.host_name:
|
||||
return []
|
||||
return [
|
||||
os.path.join(HOUDINI_HOST_DIR, "hooks")
|
||||
]
|
||||
|
||||
def get_workfile_extensions(self):
|
||||
return [".hip", ".hiplc", ".hipnc"]
|
||||
|
|
@ -13,7 +13,7 @@ from openpype.pipeline import (
|
|||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from openpype.pipeline.load import any_outdated_containers
|
||||
import openpype.hosts.houdini
|
||||
from openpype.hosts.houdini import HOUDINI_HOST_DIR
|
||||
from openpype.hosts.houdini.api import lib
|
||||
|
||||
from openpype.lib import (
|
||||
|
|
@ -28,8 +28,7 @@ log = logging.getLogger("openpype.hosts.houdini")
|
|||
AVALON_CONTAINERS = "/obj/AVALON_CONTAINERS"
|
||||
IS_HEADLESS = not hasattr(hou, "ui")
|
||||
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.houdini.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PLUGINS_DIR = os.path.join(HOUDINI_HOST_DIR, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
|
|
@ -66,7 +65,7 @@ def install():
|
|||
|
||||
self._has_been_setup = True
|
||||
# add houdini vendor packages
|
||||
hou_pythonpath = os.path.join(os.path.dirname(HOST_DIR), "vendor")
|
||||
hou_pythonpath = os.path.join(HOUDINI_HOST_DIR, "vendor")
|
||||
|
||||
sys.path.append(hou_pythonpath)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,11 +2,10 @@
|
|||
import os
|
||||
|
||||
import hou
|
||||
from openpype.pipeline import HOST_WORKFILE_EXTENSIONS
|
||||
|
||||
|
||||
def file_extensions():
|
||||
return HOST_WORKFILE_EXTENSIONS["houdini"]
|
||||
return [".hip", ".hiplc", ".hipnc"]
|
||||
|
||||
|
||||
def has_unsaved_changes():
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
from openpype.lib import PreLaunchHook
|
||||
import os
|
||||
|
||||
|
||||
class SetPath(PreLaunchHook):
|
||||
|
|
@ -15,4 +14,4 @@ class SetPath(PreLaunchHook):
|
|||
self.log.warning("BUG: Workdir is not filled.")
|
||||
return
|
||||
|
||||
os.chdir(workdir)
|
||||
self.launch_context.kwargs["cwd"] = workdir
|
||||
|
|
|
|||
|
|
@ -1,27 +1,28 @@
|
|||
import os
|
||||
import hou
|
||||
|
||||
from openpype.pipeline import legacy_io
|
||||
import pyblish.api
|
||||
|
||||
|
||||
class CollectHoudiniCurrentFile(pyblish.api.ContextPlugin):
|
||||
"""Inject the current working file into context"""
|
||||
|
||||
order = pyblish.api.CollectorOrder - 0.5
|
||||
order = pyblish.api.CollectorOrder - 0.01
|
||||
label = "Houdini Current File"
|
||||
hosts = ["houdini"]
|
||||
|
||||
def process(self, context):
|
||||
"""Inject the current working file"""
|
||||
|
||||
filepath = hou.hipFile.path()
|
||||
if not os.path.exists(filepath):
|
||||
current_file = hou.hipFile.path()
|
||||
if not os.path.exists(current_file):
|
||||
# By default Houdini will even point a new scene to a path.
|
||||
# However if the file is not saved at all and does not exist,
|
||||
# we assume the user never set it.
|
||||
filepath = ""
|
||||
|
||||
elif os.path.basename(filepath) == "untitled.hip":
|
||||
elif os.path.basename(current_file) == "untitled.hip":
|
||||
# Due to even a new file being called 'untitled.hip' we are unable
|
||||
# to confirm the current scene was ever saved because the file
|
||||
# could have existed already. We will allow it if the file exists,
|
||||
|
|
@ -33,4 +34,43 @@ class CollectHoudiniCurrentFile(pyblish.api.ContextPlugin):
|
|||
"saved correctly."
|
||||
)
|
||||
|
||||
context.data["currentFile"] = filepath
|
||||
context.data["currentFile"] = current_file
|
||||
|
||||
folder, file = os.path.split(current_file)
|
||||
filename, ext = os.path.splitext(file)
|
||||
|
||||
task = legacy_io.Session["AVALON_TASK"]
|
||||
|
||||
data = {}
|
||||
|
||||
# create instance
|
||||
instance = context.create_instance(name=filename)
|
||||
subset = 'workfile' + task.capitalize()
|
||||
|
||||
data.update({
|
||||
"subset": subset,
|
||||
"asset": os.getenv("AVALON_ASSET", None),
|
||||
"label": subset,
|
||||
"publish": True,
|
||||
"family": 'workfile',
|
||||
"families": ['workfile'],
|
||||
"setMembers": [current_file],
|
||||
"frameStart": context.data['frameStart'],
|
||||
"frameEnd": context.data['frameEnd'],
|
||||
"handleStart": context.data['handleStart'],
|
||||
"handleEnd": context.data['handleEnd']
|
||||
})
|
||||
|
||||
data['representations'] = [{
|
||||
'name': ext.lstrip("."),
|
||||
'ext': ext.lstrip("."),
|
||||
'files': file,
|
||||
"stagingDir": folder,
|
||||
}]
|
||||
|
||||
instance.data.update(data)
|
||||
|
||||
self.log.info('Collected instance: {}'.format(file))
|
||||
self.log.info('Scene path: {}'.format(current_file))
|
||||
self.log.info('staging Dir: {}'.format(folder))
|
||||
self.log.info('subset: {}'.format(subset))
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import pyblish.api
|
||||
|
||||
from openpype.api import version_up
|
||||
from openpype.action import get_errored_plugins_from_data
|
||||
from openpype.lib import version_up
|
||||
from openpype.pipeline import registered_host
|
||||
from openpype.pipeline.publish import get_errored_plugins_from_context
|
||||
|
||||
|
||||
class IncrementCurrentFile(pyblish.api.InstancePlugin):
|
||||
|
|
@ -30,7 +30,7 @@ class IncrementCurrentFile(pyblish.api.InstancePlugin):
|
|||
context.data[key] = True
|
||||
|
||||
context = instance.context
|
||||
errored_plugins = get_errored_plugins_from_data(context)
|
||||
errored_plugins = get_errored_plugins_from_context(context)
|
||||
if any(
|
||||
plugin.__name__ == "HoudiniSubmitPublishDeadline"
|
||||
for plugin in errored_plugins
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import pyblish.api
|
||||
|
||||
import hou
|
||||
from openpype.api import version_up
|
||||
from openpype.action import get_errored_plugins_from_data
|
||||
from openpype.lib import version_up
|
||||
from openpype.pipeline.publish import get_errored_plugins_from_context
|
||||
|
||||
|
||||
class IncrementCurrentFileDeadline(pyblish.api.ContextPlugin):
|
||||
|
|
@ -19,7 +19,7 @@ class IncrementCurrentFileDeadline(pyblish.api.ContextPlugin):
|
|||
|
||||
def process(self, context):
|
||||
|
||||
errored_plugins = get_errored_plugins_from_data(context)
|
||||
errored_plugins = get_errored_plugins_from_context(context)
|
||||
if any(
|
||||
plugin.__name__ == "HoudiniSubmitPublishDeadline"
|
||||
for plugin in errored_plugins
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateVDBInputNode(pyblish.api.InstancePlugin):
|
||||
|
|
@ -16,7 +16,7 @@ class ValidateVDBInputNode(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.1
|
||||
order = ValidateContentsOrder + 0.1
|
||||
families = ["vdbcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Input Node (VDB)"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateAbcPrimitiveToDetail(pyblish.api.InstancePlugin):
|
||||
"""Validate Alembic ROP Primitive to Detail attribute is consistent.
|
||||
|
|
@ -15,7 +16,7 @@ class ValidateAbcPrimitiveToDetail(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.1
|
||||
order = ValidateContentsOrder + 0.1
|
||||
families = ["pointcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Primitive to Detail (Abc)"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateAlembicROPFaceSets(pyblish.api.InstancePlugin):
|
||||
|
|
@ -17,7 +18,7 @@ class ValidateAlembicROPFaceSets(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.1
|
||||
order = ValidateContentsOrder + 0.1
|
||||
families = ["pointcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Alembic ROP Face Sets"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import pyblish.api
|
||||
import colorbleed.api
|
||||
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateAlembicInputNode(pyblish.api.InstancePlugin):
|
||||
|
|
@ -11,7 +12,7 @@ class ValidateAlembicInputNode(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = colorbleed.api.ValidateContentsOrder + 0.1
|
||||
order = ValidateContentsOrder + 0.1
|
||||
families = ["pointcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Input Node (Abc)"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateBypassed(pyblish.api.InstancePlugin):
|
||||
|
|
@ -11,7 +11,7 @@ class ValidateBypassed(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder - 0.1
|
||||
order = ValidateContentsOrder - 0.1
|
||||
families = ["*"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate ROP Bypass"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateCameraROP(pyblish.api.InstancePlugin):
|
||||
"""Validate Camera ROP settings."""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ["camera"]
|
||||
hosts = ["houdini"]
|
||||
label = "Camera ROP"
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateIntermediateDirectoriesChecked(pyblish.api.InstancePlugin):
|
||||
"""Validate Create Intermediate Directories is enabled on ROP node."""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ["pointcache", "camera", "vdbcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Create Intermediate Directories Checked"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import hou
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
def cook_in_range(node, start, end):
|
||||
|
|
@ -28,7 +28,7 @@ def get_errors(node):
|
|||
class ValidateNoErrors(pyblish.api.InstancePlugin):
|
||||
"""Validate the Instance has no current cooking errors."""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["houdini"]
|
||||
label = "Validate no errors"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidatePrimitiveHierarchyPaths(pyblish.api.InstancePlugin):
|
||||
|
|
@ -11,7 +11,7 @@ class ValidatePrimitiveHierarchyPaths(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.1
|
||||
order = ValidateContentsOrder + 0.1
|
||||
families = ["pointcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Prims Hierarchy Path"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
from openpype.hosts.houdini.api import lib
|
||||
from openpype.pipeline.publish import RepairContextAction
|
||||
|
||||
import hou
|
||||
|
||||
|
|
@ -14,7 +14,7 @@ class ValidateRemotePublishOutNode(pyblish.api.ContextPlugin):
|
|||
hosts = ["houdini"]
|
||||
targets = ["deadline"]
|
||||
label = "Remote Publish ROP node"
|
||||
actions = [openpype.api.RepairContextAction]
|
||||
actions = [RepairContextAction]
|
||||
|
||||
def process(self, context):
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
import hou
|
||||
from openpype.pipeline.publish import RepairContextAction
|
||||
|
||||
|
||||
class ValidateRemotePublishEnabled(pyblish.api.ContextPlugin):
|
||||
|
|
@ -12,7 +12,7 @@ class ValidateRemotePublishEnabled(pyblish.api.ContextPlugin):
|
|||
hosts = ["houdini"]
|
||||
targets = ["deadline"]
|
||||
label = "Remote Publish ROP enabled"
|
||||
actions = [openpype.api.RepairContextAction]
|
||||
actions = [RepairContextAction]
|
||||
|
||||
def process(self, context):
|
||||
|
||||
|
|
|
|||
|
|
@ -3,14 +3,14 @@ import re
|
|||
import pyblish.api
|
||||
|
||||
from openpype.client import get_subset_by_name
|
||||
import openpype.api
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateUSDShadeModelExists(pyblish.api.InstancePlugin):
|
||||
"""Validate the Instance has no current cooking errors."""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["houdini"]
|
||||
families = ["usdShade"]
|
||||
label = "USD Shade model exists"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
import hou
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ class ValidateUsdShadeWorkspace(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["houdini"]
|
||||
families = ["usdShade"]
|
||||
label = "USD Shade Workspace"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateVDBInputNode(pyblish.api.InstancePlugin):
|
||||
|
|
@ -16,7 +16,7 @@ class ValidateVDBInputNode(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.1
|
||||
order = ValidateContentsOrder + 0.1
|
||||
families = ["vdbcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Input Node (VDB)"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import hou
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateVDBOutputNode(pyblish.api.InstancePlugin):
|
||||
|
|
@ -17,7 +17,7 @@ class ValidateVDBOutputNode(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.1
|
||||
order = ValidateContentsOrder + 0.1
|
||||
families = ["vdbcache"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Output Node (VDB)"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
import openpype.api
|
||||
import pyblish.api
|
||||
import hou
|
||||
|
||||
|
||||
class ValidateWorkfilePaths(pyblish.api.InstancePlugin):
|
||||
"""Validate workfile paths so they are absolute."""
|
||||
|
||||
order = pyblish.api.ValidatorOrder
|
||||
families = ["workfile"]
|
||||
hosts = ["houdini"]
|
||||
label = "Validate Workfile Paths"
|
||||
actions = [openpype.api.RepairAction]
|
||||
optional = True
|
||||
|
||||
node_types = ["file", "alembic"]
|
||||
prohibited_vars = ["$HIP", "$JOB"]
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid()
|
||||
self.log.info(
|
||||
"node types to check: {}".format(", ".join(self.node_types)))
|
||||
self.log.info(
|
||||
"prohibited vars: {}".format(", ".join(self.prohibited_vars))
|
||||
)
|
||||
if invalid:
|
||||
for param in invalid:
|
||||
self.log.error(
|
||||
"{}: {}".format(param.path(), param.unexpandedString()))
|
||||
|
||||
raise RuntimeError("Invalid paths found")
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls):
|
||||
invalid = []
|
||||
for param, _ in hou.fileReferences():
|
||||
# skip nodes we are not interested in
|
||||
if param.node().type().name() not in cls.node_types:
|
||||
continue
|
||||
|
||||
if any(
|
||||
v for v in cls.prohibited_vars
|
||||
if v in param.unexpandedString()):
|
||||
invalid.append(param)
|
||||
|
||||
return invalid
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
invalid = cls.get_invalid()
|
||||
for param in invalid:
|
||||
cls.log.info("processing: {}".format(param.path()))
|
||||
cls.log.info("Replacing {} for {}".format(
|
||||
param.unexpandedString(),
|
||||
hou.text.expandString(param.unexpandedString())))
|
||||
param.set(hou.text.expandString(param.unexpandedString()))
|
||||
10
openpype/hosts/houdini/startup/python3.9libs/pythonrc.py
Normal file
10
openpype/hosts/houdini/startup/python3.9libs/pythonrc.py
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
from openpype.pipeline import install_host
|
||||
from openpype.hosts.houdini import api
|
||||
|
||||
|
||||
def main():
|
||||
print("Installing OpenPype ...")
|
||||
install_host(api)
|
||||
|
||||
|
||||
main()
|
||||
|
|
@ -1,6 +1,10 @@
|
|||
from .addon import MayaAddon
|
||||
from .addon import (
|
||||
MayaAddon,
|
||||
MAYA_ROOT_DIR,
|
||||
)
|
||||
|
||||
|
||||
__all__ = (
|
||||
"MayaAddon",
|
||||
"MAYA_ROOT_DIR",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import pyblish.api
|
|||
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.api import get_errored_instances_from_context
|
||||
from openpype.pipeline.publish import get_errored_instances_from_context
|
||||
|
||||
|
||||
class GenerateUUIDsOnInvalidAction(pyblish.api.Action):
|
||||
|
|
|
|||
|
|
@ -9,14 +9,17 @@ import maya.api.OpenMaya as om
|
|||
import pyblish.api
|
||||
|
||||
from openpype.settings import get_project_settings
|
||||
from openpype.host import HostBase, IWorkfileHost, ILoadHost
|
||||
import openpype.hosts.maya
|
||||
from openpype.host import (
|
||||
HostBase,
|
||||
IWorkfileHost,
|
||||
ILoadHost,
|
||||
HostDirmap,
|
||||
)
|
||||
from openpype.tools.utils import host_tools
|
||||
from openpype.lib import (
|
||||
register_event_callback,
|
||||
emit_event
|
||||
)
|
||||
from openpype.lib.path_tools import HostDirmap
|
||||
from openpype.pipeline import (
|
||||
legacy_io,
|
||||
register_loader_plugin_path,
|
||||
|
|
@ -28,7 +31,9 @@ from openpype.pipeline import (
|
|||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from openpype.pipeline.load import any_outdated_containers
|
||||
from openpype.hosts.maya import MAYA_ROOT_DIR
|
||||
from openpype.hosts.maya.lib import copy_workspace_mel
|
||||
|
||||
from . import menu, lib
|
||||
from .workio import (
|
||||
open_file,
|
||||
|
|
@ -41,8 +46,7 @@ from .workio import (
|
|||
|
||||
log = logging.getLogger("openpype.hosts.maya")
|
||||
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.maya.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
PLUGINS_DIR = os.path.join(MAYA_ROOT_DIR, "plugins")
|
||||
PUBLISH_PATH = os.path.join(PLUGINS_DIR, "publish")
|
||||
LOAD_PATH = os.path.join(PLUGINS_DIR, "load")
|
||||
CREATE_PATH = os.path.join(PLUGINS_DIR, "create")
|
||||
|
|
@ -59,9 +63,10 @@ class MayaHost(HostBase, IWorkfileHost, ILoadHost):
|
|||
self._op_events = {}
|
||||
|
||||
def install(self):
|
||||
project_settings = get_project_settings(os.getenv("AVALON_PROJECT"))
|
||||
project_name = os.getenv("AVALON_PROJECT")
|
||||
project_settings = get_project_settings(project_name)
|
||||
# process path mapping
|
||||
dirmap_processor = MayaDirmap("maya", project_settings)
|
||||
dirmap_processor = MayaDirmap("maya", project_name, project_settings)
|
||||
dirmap_processor.process_dirmap()
|
||||
|
||||
pyblish.api.register_plugin_path(PUBLISH_PATH)
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ class ExtractPlayblast(openpype.api.Extractor):
|
|||
# Update preset with current panel setting
|
||||
# if override_viewport_options is turned off
|
||||
if not override_viewport_options:
|
||||
panel = cmds.getPanel(with_focus=True)
|
||||
panel = cmds.getPanel(withFocus=True)
|
||||
panel_preset = capture.parse_active_view()
|
||||
preset.update(panel_preset)
|
||||
cmds.setFocus(panel)
|
||||
|
|
|
|||
|
|
@ -100,9 +100,9 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
# camera.
|
||||
if preset.pop("isolate_view", False) and instance.data.get("isolate"):
|
||||
preset["isolate"] = instance.data["setMembers"]
|
||||
|
||||
|
||||
# Show or Hide Image Plane
|
||||
image_plane = instance.data.get("imagePlane", True)
|
||||
image_plane = instance.data.get("imagePlane", True)
|
||||
if "viewport_options" in preset:
|
||||
preset["viewport_options"]["imagePlane"] = image_plane
|
||||
else:
|
||||
|
|
@ -117,7 +117,7 @@ class ExtractThumbnail(openpype.api.Extractor):
|
|||
# Update preset with current panel setting
|
||||
# if override_viewport_options is turned off
|
||||
if not override_viewport_options:
|
||||
panel = cmds.getPanel(with_focus=True)
|
||||
panel = cmds.getPanel(withFocus=True)
|
||||
panel_preset = capture.parse_active_view()
|
||||
preset.update(panel_preset)
|
||||
cmds.setFocus(panel)
|
||||
|
|
|
|||
|
|
@ -16,12 +16,11 @@ class IncrementCurrentFileDeadline(pyblish.api.ContextPlugin):
|
|||
|
||||
def process(self, context):
|
||||
|
||||
import os
|
||||
from maya import cmds
|
||||
from openpype.lib import version_up
|
||||
from openpype.action import get_errored_plugins_from_data
|
||||
from openpype.pipeline.publish import get_errored_plugins_from_context
|
||||
|
||||
errored_plugins = get_errored_plugins_from_data(context)
|
||||
errored_plugins = get_errored_plugins_from_context(context)
|
||||
if any(plugin.__name__ == "MayaSubmitDeadline"
|
||||
for plugin in errored_plugins):
|
||||
raise RuntimeError("Skipping incrementing current file because "
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateAnimationContent(pyblish.api.InstancePlugin):
|
||||
|
|
@ -11,7 +12,7 @@ class ValidateAnimationContent(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["maya"]
|
||||
families = ["animation"]
|
||||
label = "Animation Content"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api import lib
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin):
|
||||
|
|
@ -16,13 +20,13 @@ class ValidateOutRelatedNodeIds(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['animation', "pointcache"]
|
||||
hosts = ['maya']
|
||||
label = 'Animation Out Set Related Node Ids'
|
||||
actions = [
|
||||
openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction
|
||||
RepairAction
|
||||
]
|
||||
|
||||
def process(self, instance):
|
||||
|
|
|
|||
|
|
@ -4,18 +4,20 @@ import types
|
|||
import maya.cmds as cmds
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateAssRelativePaths(pyblish.api.InstancePlugin):
|
||||
"""Ensure exporting ass file has set relative texture paths"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ['maya']
|
||||
families = ['ass']
|
||||
label = "ASS has relative texture paths"
|
||||
actions = [openpype.api.RepairAction]
|
||||
actions = [RepairAction]
|
||||
|
||||
def process(self, instance):
|
||||
# we cannot ask this until user open render settings as
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import openpype.api
|
|||
from maya import cmds
|
||||
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import RepairAction
|
||||
|
||||
|
||||
class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin):
|
||||
|
|
@ -29,7 +30,7 @@ class ValidateAssemblyModelTransforms(pyblish.api.InstancePlugin):
|
|||
label = "Assembly Model Transforms"
|
||||
families = ["assembly"]
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
|
||||
prompt_message = ("You are about to reset the matrix to the default values."
|
||||
" This can alter the look of your scene. "
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import pymel.core as pm
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import (
|
||||
RepairContextAction,
|
||||
ValidateContentsOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateAttributes(pyblish.api.ContextPlugin):
|
||||
|
|
@ -16,10 +19,10 @@ class ValidateAttributes(pyblish.api.ContextPlugin):
|
|||
}
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
label = "Attributes"
|
||||
hosts = ["maya"]
|
||||
actions = [openpype.api.RepairContextAction]
|
||||
actions = [RepairContextAction]
|
||||
optional = True
|
||||
|
||||
attributes = None
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateCameraAttributes(pyblish.api.InstancePlugin):
|
||||
|
|
@ -14,7 +15,7 @@ class ValidateCameraAttributes(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['camera']
|
||||
hosts = ['maya']
|
||||
label = 'Camera Attributes'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateCameraContents(pyblish.api.InstancePlugin):
|
||||
|
|
@ -15,7 +16,7 @@ class ValidateCameraContents(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['camera']
|
||||
hosts = ['maya']
|
||||
label = 'Camera Contents'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateColorSets(pyblish.api.Validator):
|
||||
|
|
@ -13,13 +17,13 @@ class ValidateColorSets(pyblish.api.Validator):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
category = 'geometry'
|
||||
label = 'Mesh ColorSets'
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
optional = True
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import pyblish.api
|
||||
|
||||
from maya import cmds
|
||||
from openpype.plugin import contextplugin_should_run
|
||||
from openpype.pipeline.publish import context_plugin_should_run
|
||||
|
||||
|
||||
class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin):
|
||||
|
|
@ -24,7 +24,7 @@ class ValidateCurrentRenderLayerIsRenderable(pyblish.api.ContextPlugin):
|
|||
def process(self, context):
|
||||
|
||||
# Workaround bug pyblish-base#250
|
||||
if not contextplugin_should_run(self, context):
|
||||
if not context_plugin_should_run(self, context):
|
||||
return
|
||||
|
||||
layer = cmds.editRenderLayerGlobals(query=True, currentRenderLayer=True)
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api.lib import maintained_selection
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateCycleError(pyblish.api.InstancePlugin):
|
||||
"""Validate nodes produce no cycle errors."""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.05
|
||||
order = ValidateContentsOrder + 0.05
|
||||
label = "Cycle Errors"
|
||||
hosts = ["maya"]
|
||||
families = ["rig"]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
from maya import cmds
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateFrameRange(pyblish.api.InstancePlugin):
|
||||
|
|
@ -18,7 +21,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
|
|||
"""
|
||||
|
||||
label = "Validate Frame Range"
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ["animation",
|
||||
"pointcache",
|
||||
"camera",
|
||||
|
|
@ -26,7 +29,7 @@ class ValidateFrameRange(pyblish.api.InstancePlugin):
|
|||
"review",
|
||||
"yeticache"]
|
||||
optional = True
|
||||
actions = [openpype.api.RepairAction]
|
||||
actions = [RepairAction]
|
||||
exclude_families = []
|
||||
|
||||
def process(self, instance):
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateInstanceHasMembers(pyblish.api.InstancePlugin):
|
||||
"""Validates instance objectSet has *any* members."""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["maya"]
|
||||
label = 'Instance has members'
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction]
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
from __future__ import absolute_import
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
from maya import cmds
|
||||
|
||||
|
|
@ -98,7 +98,7 @@ class ValidateInstanceInContext(pyblish.api.InstancePlugin):
|
|||
Action on this validator will select invalid instances in Outliner.
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
label = "Instance in same Context"
|
||||
optional = True
|
||||
hosts = ["maya"]
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import string
|
||||
|
||||
import six
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
# Allow only characters, numbers and underscore
|
||||
allowed = set(string.ascii_lowercase +
|
||||
|
|
@ -18,7 +18,7 @@ def validate_name(subset):
|
|||
class ValidateSubsetName(pyblish.api.InstancePlugin):
|
||||
"""Validates subset name has only valid characters"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ["*"]
|
||||
label = "Subset Name"
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
import maya.cmds as cmds
|
||||
import openpype.api
|
||||
import os
|
||||
|
||||
from openpype.pipeline.publish import RepairContextAction
|
||||
|
||||
|
||||
class ValidateLoadedPlugin(pyblish.api.ContextPlugin):
|
||||
|
|
@ -10,7 +11,7 @@ class ValidateLoadedPlugin(pyblish.api.ContextPlugin):
|
|||
label = "Loaded Plugin"
|
||||
order = pyblish.api.ValidatorOrder
|
||||
host = ["maya"]
|
||||
actions = [openpype.api.RepairContextAction]
|
||||
actions = [RepairContextAction]
|
||||
|
||||
@classmethod
|
||||
def get_invalid(cls, context):
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateLookContents(pyblish.api.InstancePlugin):
|
||||
|
|
@ -17,7 +18,7 @@ class ValidateLookContents(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['look']
|
||||
hosts = ['maya']
|
||||
label = 'Look Data Contents'
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
from maya import cmds
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateLookDefaultShadersConnections(pyblish.api.InstancePlugin):
|
||||
|
|
@ -16,7 +16,7 @@ class ValidateLookDefaultShadersConnections(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['look']
|
||||
hosts = ['maya']
|
||||
label = 'Look Default Shader Connections'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin):
|
||||
|
|
@ -16,12 +20,12 @@ class ValidateLookIdReferenceEdits(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['look']
|
||||
hosts = ['maya']
|
||||
label = 'Look Id Reference Edits'
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
|
||||
def process(self, instance):
|
||||
invalid = self.get_invalid(instance)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from collections import defaultdict
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidatePipelineOrder
|
||||
|
||||
|
||||
class ValidateUniqueRelationshipMembers(pyblish.api.InstancePlugin):
|
||||
|
|
@ -20,7 +21,7 @@ class ValidateUniqueRelationshipMembers(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidatePipelineOrder
|
||||
order = ValidatePipelineOrder
|
||||
label = 'Look members unique'
|
||||
hosts = ['maya']
|
||||
families = ['look']
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin):
|
||||
|
|
@ -23,7 +24,7 @@ class ValidateLookNoDefaultShaders(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder + 0.01
|
||||
order = ValidateContentsOrder + 0.01
|
||||
families = ['look']
|
||||
hosts = ['maya']
|
||||
label = 'Look No Default Shaders'
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api import lib
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api import lib
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateLookSets(pyblish.api.InstancePlugin):
|
||||
|
|
@ -38,7 +38,7 @@ class ValidateLookSets(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['look']
|
||||
hosts = ['maya']
|
||||
label = 'Look Sets'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateShadingEngine(pyblish.api.InstancePlugin):
|
||||
|
|
@ -11,12 +15,12 @@ class ValidateShadingEngine(pyblish.api.InstancePlugin):
|
|||
Shading engines should be named "{surface_shader}SG"
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ["look"]
|
||||
hosts = ["maya"]
|
||||
label = "Look Shading Engine Naming"
|
||||
actions = [
|
||||
openpype.hosts.maya.api.action.SelectInvalidAction, openpype.api.RepairAction
|
||||
openpype.hosts.maya.api.action.SelectInvalidAction, RepairAction
|
||||
]
|
||||
|
||||
# The default connections to check
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateSingleShader(pyblish.api.InstancePlugin):
|
||||
|
|
@ -12,7 +13,7 @@ class ValidateSingleShader(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['look']
|
||||
hosts = ['maya']
|
||||
label = 'Look Single Shader Per Shape'
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import maya.cmds as cmds
|
||||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
|
||||
import openpype.hosts.maya.api.lib as mayalib
|
||||
from openpype.pipeline.context_tools import get_current_project_asset
|
||||
from math import ceil
|
||||
from openpype.pipeline.publish import (
|
||||
RepairContextAction,
|
||||
ValidateSceneOrder,
|
||||
)
|
||||
|
||||
|
||||
def float_round(num, places=0, direction=ceil):
|
||||
|
|
@ -14,10 +18,10 @@ def float_round(num, places=0, direction=ceil):
|
|||
class ValidateMayaUnits(pyblish.api.ContextPlugin):
|
||||
"""Check if the Maya units are set correct"""
|
||||
|
||||
order = openpype.api.ValidateSceneOrder
|
||||
order = ValidateSceneOrder
|
||||
label = "Maya Units"
|
||||
hosts = ['maya']
|
||||
actions = [openpype.api.RepairContextAction]
|
||||
actions = [RepairContextAction]
|
||||
|
||||
validate_linear_units = True
|
||||
linear_units = "cm"
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api.lib import maintained_selection
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateMeshArnoldAttributes(pyblish.api.InstancePlugin):
|
||||
|
|
@ -13,14 +17,14 @@ class ValidateMeshArnoldAttributes(pyblish.api.InstancePlugin):
|
|||
later published looks can discover non-default Arnold attributes.
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ["maya"]
|
||||
families = ["model"]
|
||||
category = "geometry"
|
||||
label = "Mesh Arnold Attributes"
|
||||
actions = [
|
||||
openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction
|
||||
RepairAction
|
||||
]
|
||||
optional = True
|
||||
if cmds.getAttr(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateMeshOrder
|
||||
|
||||
|
||||
def len_flattened(components):
|
||||
|
|
@ -45,7 +46,7 @@ class ValidateMeshHasUVs(pyblish.api.InstancePlugin):
|
|||
UVs for every face.
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
category = 'geometry'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateMeshOrder
|
||||
|
||||
|
||||
class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin):
|
||||
|
|
@ -12,7 +13,7 @@ class ValidateMeshLaminaFaces(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
category = 'geometry'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api import lib
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateMeshNgons(pyblish.api.Validator):
|
||||
|
|
@ -16,7 +17,7 @@ class ValidateMeshNgons(pyblish.api.Validator):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["maya"]
|
||||
families = ["model"]
|
||||
label = "Mesh ngons"
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateMeshOrder
|
||||
|
||||
|
||||
class ValidateMeshNoNegativeScale(pyblish.api.Validator):
|
||||
|
|
@ -17,7 +18,7 @@ class ValidateMeshNoNegativeScale(pyblish.api.Validator):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
label = 'Mesh No Negative Scale'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateMeshOrder
|
||||
|
||||
|
||||
class ValidateMeshNonManifold(pyblish.api.Validator):
|
||||
|
|
@ -13,7 +14,7 @@ class ValidateMeshNonManifold(pyblish.api.Validator):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
label = 'Mesh Non-Manifold Vertices/Edges'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api import lib
|
||||
from openpype.pipeline.publish import ValidateMeshOrder
|
||||
|
||||
|
||||
class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin):
|
||||
|
|
@ -16,7 +17,7 @@ class ValidateMeshNonZeroEdgeLength(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
families = ['model']
|
||||
hosts = ['maya']
|
||||
category = 'geometry'
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import maya.api.OpenMaya as om2
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateMeshNormalsUnlocked(pyblish.api.Validator):
|
||||
|
|
@ -14,14 +18,14 @@ class ValidateMeshNormalsUnlocked(pyblish.api.Validator):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
category = 'geometry'
|
||||
version = (0, 1, 0)
|
||||
label = 'Mesh Normals Unlocked'
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
optional = True
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import maya.api.OpenMaya as om
|
|||
import pymel.core as pm
|
||||
|
||||
from six.moves import xrange
|
||||
from openpype.pipeline.publish import ValidateMeshOrder
|
||||
|
||||
|
||||
class GetOverlappingUVs(object):
|
||||
|
|
@ -232,7 +233,7 @@ class ValidateMeshHasOverlappingUVs(pyblish.api.InstancePlugin):
|
|||
It is optional to warn publisher about it.
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
category = 'geometry'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
def pairs(iterable):
|
||||
|
|
@ -86,12 +90,12 @@ class ValidateMeshShaderConnections(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
label = "Mesh Shader Connections"
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
|
||||
def process(self, instance):
|
||||
"""Process all the nodes in the instance 'objectSet'"""
|
||||
|
|
|
|||
|
|
@ -4,6 +4,10 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api import lib
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin):
|
||||
|
|
@ -15,7 +19,7 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model', 'pointcache']
|
||||
category = 'uv'
|
||||
|
|
@ -23,7 +27,7 @@ class ValidateMeshSingleUVSet(pyblish.api.InstancePlugin):
|
|||
version = (0, 1, 0)
|
||||
label = "Mesh Single UV Set"
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance):
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin):
|
||||
|
|
@ -15,13 +19,13 @@ class ValidateMeshUVSetMap1(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
optional = True
|
||||
label = "Mesh has map1 UV Set"
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance):
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateMeshOrder,
|
||||
)
|
||||
|
||||
|
||||
def len_flattened(components):
|
||||
|
|
@ -57,13 +61,13 @@ class ValidateMeshVerticesHaveEdges(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateMeshOrder
|
||||
order = ValidateMeshOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
category = 'geometry'
|
||||
label = 'Mesh Vertices Have Edges'
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
|
||||
@classmethod
|
||||
def repair(cls, instance):
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import pyblish.api
|
|||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api import lib
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateModelContent(pyblish.api.InstancePlugin):
|
||||
|
|
@ -14,7 +15,7 @@ class ValidateModelContent(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["maya"]
|
||||
families = ["model"]
|
||||
label = "Model Content"
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import pyblish.api
|
|||
|
||||
import openpype.api
|
||||
from openpype.pipeline import legacy_io
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.hosts.maya.api.shader_definition_editor import (
|
||||
DEFINITION_FILENAME)
|
||||
|
|
@ -23,7 +24,7 @@ class ValidateModelName(pyblish.api.InstancePlugin):
|
|||
|
||||
"""
|
||||
optional = True
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ["maya"]
|
||||
families = ["model"]
|
||||
label = "Model Name"
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ import appdirs
|
|||
|
||||
import pyblish.api
|
||||
from openpype.lib import requests_get
|
||||
from openpype.plugin import contextplugin_should_run
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import (
|
||||
context_plugin_should_run,
|
||||
RepairAction,
|
||||
)
|
||||
|
||||
|
||||
class ValidateMusterConnection(pyblish.api.ContextPlugin):
|
||||
|
|
@ -21,12 +23,12 @@ class ValidateMusterConnection(pyblish.api.ContextPlugin):
|
|||
token = None
|
||||
if not os.environ.get("MUSTER_REST_URL"):
|
||||
active = False
|
||||
actions = [openpype.api.RepairAction]
|
||||
actions = [RepairAction]
|
||||
|
||||
def process(self, context):
|
||||
|
||||
# Workaround bug pyblish-base#250
|
||||
if not contextplugin_should_run(self, context):
|
||||
if not context_plugin_should_run(self, context):
|
||||
return
|
||||
|
||||
# test if we have environment set (redundant as this plugin shouldn'
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
import os
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
import os
|
||||
|
||||
COLOUR_SPACES = ['sRGB', 'linear', 'auto']
|
||||
MIPMAP_EXTENSIONS = ['tdl']
|
||||
|
||||
|
||||
class ValidateMvLookContents(pyblish.api.InstancePlugin):
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
families = ['mvLook']
|
||||
hosts = ['maya']
|
||||
label = 'Validate mvLook Data'
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateNoAnimation(pyblish.api.Validator):
|
||||
|
|
@ -14,7 +15,7 @@ class ValidateNoAnimation(pyblish.api.Validator):
|
|||
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
label = "No Animation"
|
||||
hosts = ["maya"]
|
||||
families = ["model"]
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ from maya import cmds
|
|||
import pyblish.api
|
||||
import openpype.api
|
||||
import openpype.hosts.maya.api.action
|
||||
from openpype.pipeline.publish import ValidateContentsOrder
|
||||
|
||||
|
||||
class ValidateNoDefaultCameras(pyblish.api.InstancePlugin):
|
||||
|
|
@ -13,7 +14,7 @@ class ValidateNoDefaultCameras(pyblish.api.InstancePlugin):
|
|||
settings when being loaded and sometimes being skipped.
|
||||
"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ['maya']
|
||||
families = ['camera']
|
||||
version = (0, 1, 0)
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@ import maya.cmds as cmds
|
|||
|
||||
import pyblish.api
|
||||
import openpype.api
|
||||
from openpype.pipeline.publish import (
|
||||
RepairAction,
|
||||
ValidateContentsOrder,
|
||||
)
|
||||
|
||||
import openpype.hosts.maya.api.action
|
||||
|
||||
|
||||
|
|
@ -16,14 +21,14 @@ def get_namespace(node_name):
|
|||
class ValidateNoNamespace(pyblish.api.InstancePlugin):
|
||||
"""Ensure the nodes don't have a namespace"""
|
||||
|
||||
order = openpype.api.ValidateContentsOrder
|
||||
order = ValidateContentsOrder
|
||||
hosts = ['maya']
|
||||
families = ['model']
|
||||
category = 'cleanup'
|
||||
version = (0, 1, 0)
|
||||
label = 'No Namespaces'
|
||||
actions = [openpype.hosts.maya.api.action.SelectInvalidAction,
|
||||
openpype.api.RepairAction]
|
||||
RepairAction]
|
||||
|
||||
@staticmethod
|
||||
def get_invalid(instance):
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue