mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into bugfix/OP-5883_3dsmax-Publishing-Deadline-jobs-from-RedShift
This commit is contained in:
commit
2e40a26beb
32 changed files with 1248 additions and 402 deletions
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
4
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -35,6 +35,8 @@ body:
|
|||
label: Version
|
||||
description: What version are you running? Look to OpenPype Tray
|
||||
options:
|
||||
- 3.15.9
|
||||
- 3.15.9-nightly.2
|
||||
- 3.15.9-nightly.1
|
||||
- 3.15.8
|
||||
- 3.15.8-nightly.3
|
||||
|
|
@ -133,8 +135,6 @@ body:
|
|||
- 3.14.3-nightly.1
|
||||
- 3.14.2
|
||||
- 3.14.2-nightly.5
|
||||
- 3.14.2-nightly.4
|
||||
- 3.14.2-nightly.3
|
||||
validations:
|
||||
required: true
|
||||
- type: dropdown
|
||||
|
|
|
|||
335
CHANGELOG.md
335
CHANGELOG.md
|
|
@ -1,6 +1,341 @@
|
|||
# Changelog
|
||||
|
||||
|
||||
## [3.15.9](https://github.com/ynput/OpenPype/tree/3.15.9)
|
||||
|
||||
|
||||
[Full Changelog](https://github.com/ynput/OpenPype/compare/3.15.8...3.15.9)
|
||||
|
||||
### **🆕 New features**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Blender: Implemented Loading of Alembic Camera <a href="https://github.com/ynput/OpenPype/pull/4990">#4990</a></summary>
|
||||
|
||||
Implemented loading of Alembic cameras in Blender.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Unreal: Implemented Creator, Loader and Extractor for Levels <a href="https://github.com/ynput/OpenPype/pull/5008">#5008</a></summary>
|
||||
|
||||
Creator, Loader and Extractor for Unreal Levels have been implemented.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🚀 Enhancements**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Blender: Added setting for base unit scale <a href="https://github.com/ynput/OpenPype/pull/4987">#4987</a></summary>
|
||||
|
||||
A setting for the base unit scale has been added for Blender.The unit scale is automatically applied when opening a file or creating a new one.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Unreal: Changed naming and path of Camera Levels <a href="https://github.com/ynput/OpenPype/pull/5010">#5010</a></summary>
|
||||
|
||||
The levels created for the camera in Unreal now include `_camera` in the name, to be better identifiable, and are placed in the camera folder.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Settings: Added option to nest settings templates <a href="https://github.com/ynput/OpenPype/pull/5022">#5022</a></summary>
|
||||
|
||||
It is possible to nest settings templates in another templates.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Enhancement/publisher: Remove "hit play to continue" label on continue <a href="https://github.com/ynput/OpenPype/pull/5029">#5029</a></summary>
|
||||
|
||||
Remove "hit play to continue" message on continue so that it doesn't show anymore when play was clicked.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Ftrack: Limit number of ftrack events to query at once <a href="https://github.com/ynput/OpenPype/pull/5033">#5033</a></summary>
|
||||
|
||||
Limit the amount of ftrack events received from mongo at once to 100.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>General: Small code cleanups <a href="https://github.com/ynput/OpenPype/pull/5034">#5034</a></summary>
|
||||
|
||||
Small code cleanup and updates.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Global: collect frames to fix with settings <a href="https://github.com/ynput/OpenPype/pull/5036">#5036</a></summary>
|
||||
|
||||
Settings for `Collect Frames to Fix` will allow disable per project the plugin. Also `Rewriting latest version` attribute is hiddable from settings.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>General: Publish plugin apply settings can expect only project settings <a href="https://github.com/ynput/OpenPype/pull/5037">#5037</a></summary>
|
||||
|
||||
Only project settings are passed to optional `apply_settings` method, if the method expects only one argument.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **🐛 Bug fixes**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Maya: Load Assembly fix invalid imports <a href="https://github.com/ynput/OpenPype/pull/4859">#4859</a></summary>
|
||||
|
||||
Refactors imports so they are now correct.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Maya: Skipping rendersetup for members. <a href="https://github.com/ynput/OpenPype/pull/4973">#4973</a></summary>
|
||||
|
||||
When publishing a `rendersetup`, the objectset is and should be empty.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Maya: Validate Rig Output IDs <a href="https://github.com/ynput/OpenPype/pull/5016">#5016</a></summary>
|
||||
|
||||
Absolute names of node were not used, so plugin did not fetch the nodes properly.Also missed pymel command.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Deadline: escape rootless path in publish job <a href="https://github.com/ynput/OpenPype/pull/4910">#4910</a></summary>
|
||||
|
||||
If the publish path on Deadline job contains spaces or other characters, command was failing because the path wasn't properly escaped. This is fixing it.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>General: Company name and URL changed <a href="https://github.com/ynput/OpenPype/pull/4974">#4974</a></summary>
|
||||
|
||||
The current records were obsolete in inno_setup, changed to the up-to-date.
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Unreal: Fix usage of 'get_full_path' function <a href="https://github.com/ynput/OpenPype/pull/5014">#5014</a></summary>
|
||||
|
||||
This PR changes all the occurrences of `get_full_path` functions to alternatives to get the path of the objects.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Unreal: Fix sequence frames validator to use correct data <a href="https://github.com/ynput/OpenPype/pull/5021">#5021</a></summary>
|
||||
|
||||
Fix sequence frames validator to use clipIn and clipOut data instead of frameStart and frameEnd.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Unreal: Fix render instances collection to use correct data <a href="https://github.com/ynput/OpenPype/pull/5023">#5023</a></summary>
|
||||
|
||||
Fix render instances collection to use `frameStart` and `frameEnd` from the Project Manager, instead of the sequence's ones.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Resolve: loader is opening even if no timeline in project <a href="https://github.com/ynput/OpenPype/pull/5025">#5025</a></summary>
|
||||
|
||||
Loader is opening now even no timeline is available in a project.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>nuke: callback for dirmapping is on demand <a href="https://github.com/ynput/OpenPype/pull/5030">#5030</a></summary>
|
||||
|
||||
Nuke was slowed down on processing due this callback. Since it is disabled by default it made sense to add it only on demand.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Publisher: UI works with instances without label <a href="https://github.com/ynput/OpenPype/pull/5032">#5032</a></summary>
|
||||
|
||||
Publisher UI does not crash if instance don't have filled 'label' key in instance data.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Publisher: Call explicitly prepared tab methods <a href="https://github.com/ynput/OpenPype/pull/5044">#5044</a></summary>
|
||||
|
||||
It is not possible to go to Create tab during publishing from OpenPype menu.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Ftrack: Role names are not case sensitive in ftrack event server status action <a href="https://github.com/ynput/OpenPype/pull/5058">#5058</a></summary>
|
||||
|
||||
Event server status action is not case sensitive for role names of user.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Publisher: Fix border widget <a href="https://github.com/ynput/OpenPype/pull/5063">#5063</a></summary>
|
||||
|
||||
Fixed border lines in Publisher UI to be painted correctly with correct indentation and size.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Unreal: Fix Commandlet Project and Permissions <a href="https://github.com/ynput/OpenPype/pull/5066">#5066</a></summary>
|
||||
|
||||
Fix problem when creating an Unreal Project when Commandlet Project is in a protected location.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Unreal: Added verification for Unreal app name format <a href="https://github.com/ynput/OpenPype/pull/5070">#5070</a></summary>
|
||||
|
||||
The Unreal app name is used to determine the Unreal version folder, so it is necessary that if follows the format `x-x`, where `x` is any integer. This PR adds a verification that the app name follows that format.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **📃 Documentation**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Docs: Display wrong image in ExtractOIIOTranscode <a href="https://github.com/ynput/OpenPype/pull/5045">#5045</a></summary>
|
||||
|
||||
Wrong image display in `https://openpype.io/docs/project_settings/settings_project_global#extract-oiio-transcode`.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
### **Merged pull requests**
|
||||
|
||||
|
||||
<details>
|
||||
<summary>Drop-down menu to list all families in create placeholder <a href="https://github.com/ynput/OpenPype/pull/4928">#4928</a></summary>
|
||||
|
||||
Currently in the create placeholder window, we need to write the family manually. This replace the text field by an enum field with all families for the current software.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>add sync to specific projects or listen only <a href="https://github.com/ynput/OpenPype/pull/4919">#4919</a></summary>
|
||||
|
||||
Extend kitsu sync service with additional arguments to sync specific projects.
|
||||
|
||||
|
||||
___
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
|
||||
|
||||
## [3.15.8](https://github.com/ynput/OpenPype/tree/3.15.8)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ AppId={{B9E9DF6A-5BDA-42DD-9F35-C09D564C4D93}
|
|||
AppName={#MyAppName}
|
||||
AppVersion={#AppVer}
|
||||
AppVerName={#MyAppName} version {#AppVer}
|
||||
AppPublisher=Orbi Tools s.r.o
|
||||
AppPublisherURL=http://pype.club
|
||||
AppSupportURL=http://pype.club
|
||||
AppUpdatesURL=http://pype.club
|
||||
AppPublisher=Ynput s.r.o
|
||||
AppPublisherURL=https://ynput.io
|
||||
AppSupportURL=https://ynput.io
|
||||
AppUpdatesURL=https://ynput.io
|
||||
DefaultDirName={autopf}\{#MyAppName}\{#AppVer}
|
||||
UsePreviousAppDir=no
|
||||
DisableProgramGroupPage=yes
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ from openpype.lib import (
|
|||
emit_event
|
||||
)
|
||||
import openpype.hosts.blender
|
||||
from openpype.settings import get_project_settings
|
||||
|
||||
|
||||
HOST_DIR = os.path.dirname(os.path.abspath(openpype.hosts.blender.__file__))
|
||||
PLUGINS_DIR = os.path.join(HOST_DIR, "plugins")
|
||||
|
|
@ -83,6 +85,31 @@ def uninstall():
|
|||
ops.unregister()
|
||||
|
||||
|
||||
def show_message(title, message):
|
||||
from openpype.widgets.message_window import Window
|
||||
from .ops import BlenderApplication
|
||||
|
||||
BlenderApplication.get_app()
|
||||
|
||||
Window(
|
||||
parent=None,
|
||||
title=title,
|
||||
message=message,
|
||||
level="warning")
|
||||
|
||||
|
||||
def message_window(title, message):
|
||||
from .ops import (
|
||||
MainThreadItem,
|
||||
execute_in_main_thread,
|
||||
_process_app_events
|
||||
)
|
||||
|
||||
mti = MainThreadItem(show_message, title, message)
|
||||
execute_in_main_thread(mti)
|
||||
_process_app_events()
|
||||
|
||||
|
||||
def set_start_end_frames():
|
||||
project_name = legacy_io.active_project()
|
||||
asset_name = legacy_io.Session["AVALON_ASSET"]
|
||||
|
|
@ -125,10 +152,36 @@ def set_start_end_frames():
|
|||
def on_new():
|
||||
set_start_end_frames()
|
||||
|
||||
project = os.environ.get("AVALON_PROJECT")
|
||||
settings = get_project_settings(project)
|
||||
|
||||
unit_scale_settings = settings.get("blender").get("unit_scale_settings")
|
||||
unit_scale_enabled = unit_scale_settings.get("enabled")
|
||||
if unit_scale_enabled:
|
||||
unit_scale = unit_scale_settings.get("base_file_unit_scale")
|
||||
bpy.context.scene.unit_settings.scale_length = unit_scale
|
||||
|
||||
|
||||
def on_open():
|
||||
set_start_end_frames()
|
||||
|
||||
project = os.environ.get("AVALON_PROJECT")
|
||||
settings = get_project_settings(project)
|
||||
|
||||
unit_scale_settings = settings.get("blender").get("unit_scale_settings")
|
||||
unit_scale_enabled = unit_scale_settings.get("enabled")
|
||||
apply_on_opening = unit_scale_settings.get("apply_on_opening")
|
||||
if unit_scale_enabled and apply_on_opening:
|
||||
unit_scale = unit_scale_settings.get("base_file_unit_scale")
|
||||
prev_unit_scale = bpy.context.scene.unit_settings.scale_length
|
||||
|
||||
if unit_scale != prev_unit_scale:
|
||||
bpy.context.scene.unit_settings.scale_length = unit_scale
|
||||
|
||||
message_window(
|
||||
"Base file unit scale changed",
|
||||
"Base file unit scale changed to match the project settings.")
|
||||
|
||||
|
||||
@bpy.app.handlers.persistent
|
||||
def _on_save_pre(*args):
|
||||
|
|
|
|||
209
openpype/hosts/blender/plugins/load/load_camera_abc.py
Normal file
209
openpype/hosts/blender/plugins/load/load_camera_abc.py
Normal file
|
|
@ -0,0 +1,209 @@
|
|||
"""Load an asset in Blender from an Alembic file."""
|
||||
|
||||
from pathlib import Path
|
||||
from pprint import pformat
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import bpy
|
||||
|
||||
from openpype.pipeline import (
|
||||
get_representation_path,
|
||||
AVALON_CONTAINER_ID,
|
||||
)
|
||||
from openpype.hosts.blender.api import plugin, lib
|
||||
from openpype.hosts.blender.api.pipeline import (
|
||||
AVALON_CONTAINERS,
|
||||
AVALON_PROPERTY,
|
||||
)
|
||||
|
||||
|
||||
class AbcCameraLoader(plugin.AssetLoader):
|
||||
"""Load a camera from Alembic file.
|
||||
|
||||
Stores the imported asset in an empty named after the asset.
|
||||
"""
|
||||
|
||||
families = ["camera"]
|
||||
representations = ["abc"]
|
||||
|
||||
label = "Load Camera (ABC)"
|
||||
icon = "code-fork"
|
||||
color = "orange"
|
||||
|
||||
def _remove(self, asset_group):
|
||||
objects = list(asset_group.children)
|
||||
|
||||
for obj in objects:
|
||||
if obj.type == "CAMERA":
|
||||
bpy.data.cameras.remove(obj.data)
|
||||
elif obj.type == "EMPTY":
|
||||
objects.extend(obj.children)
|
||||
bpy.data.objects.remove(obj)
|
||||
|
||||
def _process(self, libpath, asset_group, group_name):
|
||||
plugin.deselect_all()
|
||||
|
||||
bpy.ops.wm.alembic_import(filepath=libpath)
|
||||
|
||||
objects = lib.get_selection()
|
||||
|
||||
for obj in objects:
|
||||
obj.parent = asset_group
|
||||
|
||||
for obj in objects:
|
||||
name = obj.name
|
||||
obj.name = f"{group_name}:{name}"
|
||||
if obj.type != "EMPTY":
|
||||
name_data = obj.data.name
|
||||
obj.data.name = f"{group_name}:{name_data}"
|
||||
|
||||
if not obj.get(AVALON_PROPERTY):
|
||||
obj[AVALON_PROPERTY] = dict()
|
||||
|
||||
avalon_info = obj[AVALON_PROPERTY]
|
||||
avalon_info.update({"container_name": group_name})
|
||||
|
||||
plugin.deselect_all()
|
||||
|
||||
return objects
|
||||
|
||||
def process_asset(
|
||||
self,
|
||||
context: dict,
|
||||
name: str,
|
||||
namespace: Optional[str] = None,
|
||||
options: Optional[Dict] = None,
|
||||
) -> Optional[List]:
|
||||
"""
|
||||
Arguments:
|
||||
name: Use pre-defined name
|
||||
namespace: Use pre-defined namespace
|
||||
context: Full parenthood of representation to load
|
||||
options: Additional settings dictionary
|
||||
"""
|
||||
libpath = self.fname
|
||||
asset = context["asset"]["name"]
|
||||
subset = context["subset"]["name"]
|
||||
|
||||
asset_name = plugin.asset_name(asset, subset)
|
||||
unique_number = plugin.get_unique_number(asset, subset)
|
||||
group_name = plugin.asset_name(asset, subset, unique_number)
|
||||
namespace = namespace or f"{asset}_{unique_number}"
|
||||
|
||||
avalon_container = bpy.data.collections.get(AVALON_CONTAINERS)
|
||||
if not avalon_container:
|
||||
avalon_container = bpy.data.collections.new(name=AVALON_CONTAINERS)
|
||||
bpy.context.scene.collection.children.link(avalon_container)
|
||||
|
||||
asset_group = bpy.data.objects.new(group_name, object_data=None)
|
||||
avalon_container.objects.link(asset_group)
|
||||
|
||||
objects = self._process(libpath, asset_group, group_name)
|
||||
|
||||
objects = []
|
||||
nodes = list(asset_group.children)
|
||||
|
||||
for obj in nodes:
|
||||
objects.append(obj)
|
||||
nodes.extend(list(obj.children))
|
||||
|
||||
bpy.context.scene.collection.objects.link(asset_group)
|
||||
|
||||
asset_group[AVALON_PROPERTY] = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": AVALON_CONTAINER_ID,
|
||||
"name": name,
|
||||
"namespace": namespace or "",
|
||||
"loader": str(self.__class__.__name__),
|
||||
"representation": str(context["representation"]["_id"]),
|
||||
"libpath": libpath,
|
||||
"asset_name": asset_name,
|
||||
"parent": str(context["representation"]["parent"]),
|
||||
"family": context["representation"]["context"]["family"],
|
||||
"objectName": group_name,
|
||||
}
|
||||
|
||||
self[:] = objects
|
||||
return objects
|
||||
|
||||
def exec_update(self, container: Dict, representation: Dict):
|
||||
"""Update the loaded asset.
|
||||
|
||||
This will remove all objects of the current collection, load the new
|
||||
ones and add them to the collection.
|
||||
If the objects of the collection are used in another collection they
|
||||
will not be removed, only unlinked. Normally this should not be the
|
||||
case though.
|
||||
|
||||
Warning:
|
||||
No nested collections are supported at the moment!
|
||||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
libpath = Path(get_representation_path(representation))
|
||||
extension = libpath.suffix.lower()
|
||||
|
||||
self.log.info(
|
||||
"Container: %s\nRepresentation: %s",
|
||||
pformat(container, indent=2),
|
||||
pformat(representation, indent=2),
|
||||
)
|
||||
|
||||
assert asset_group, (
|
||||
f"The asset is not loaded: {container['objectName']}")
|
||||
assert libpath, (
|
||||
f"No existing library file found for {container['objectName']}")
|
||||
assert libpath.is_file(), f"The file doesn't exist: {libpath}"
|
||||
assert extension in plugin.VALID_EXTENSIONS, (
|
||||
f"Unsupported file: {libpath}")
|
||||
|
||||
metadata = asset_group.get(AVALON_PROPERTY)
|
||||
group_libpath = metadata["libpath"]
|
||||
|
||||
normalized_group_libpath = str(
|
||||
Path(bpy.path.abspath(group_libpath)).resolve())
|
||||
normalized_libpath = str(
|
||||
Path(bpy.path.abspath(str(libpath))).resolve())
|
||||
self.log.debug(
|
||||
"normalized_group_libpath:\n %s\nnormalized_libpath:\n %s",
|
||||
normalized_group_libpath,
|
||||
normalized_libpath,
|
||||
)
|
||||
if normalized_group_libpath == normalized_libpath:
|
||||
self.log.info("Library already loaded, not updating...")
|
||||
return
|
||||
|
||||
mat = asset_group.matrix_basis.copy()
|
||||
|
||||
self._remove(asset_group)
|
||||
self._process(str(libpath), asset_group, object_name)
|
||||
|
||||
asset_group.matrix_basis = mat
|
||||
|
||||
metadata["libpath"] = str(libpath)
|
||||
metadata["representation"] = str(representation["_id"])
|
||||
|
||||
def exec_remove(self, container: Dict) -> bool:
|
||||
"""Remove an existing container from a Blender scene.
|
||||
|
||||
Arguments:
|
||||
container (openpype:container-1.0): Container to remove,
|
||||
from `host.ls()`.
|
||||
|
||||
Returns:
|
||||
bool: Whether the container was deleted.
|
||||
|
||||
Warning:
|
||||
No nested collections are supported at the moment!
|
||||
"""
|
||||
object_name = container["objectName"]
|
||||
asset_group = bpy.data.objects.get(object_name)
|
||||
|
||||
if not asset_group:
|
||||
return False
|
||||
|
||||
self._remove(asset_group)
|
||||
|
||||
bpy.data.objects.remove(asset_group)
|
||||
|
||||
return True
|
||||
|
|
@ -41,8 +41,8 @@ class LoadClip(phiero.SequenceLoader):
|
|||
|
||||
clip_name_template = "{asset}_{subset}_{representation}"
|
||||
|
||||
@classmethod
|
||||
def apply_settings(cls, project_settings, system_settings):
|
||||
|
||||
plugin_type_settings = (
|
||||
project_settings
|
||||
.get("hiero", {})
|
||||
|
|
|
|||
|
|
@ -28,7 +28,9 @@ from openpype.pipeline import (
|
|||
)
|
||||
from openpype.hosts.maya.api.lib import (
|
||||
matrix_equals,
|
||||
unique_namespace
|
||||
unique_namespace,
|
||||
get_container_transforms,
|
||||
DEFAULT_MATRIX
|
||||
)
|
||||
|
||||
log = logging.getLogger("PackageLoader")
|
||||
|
|
@ -183,8 +185,6 @@ def _add(instance, representation_id, loaders, namespace, root="|"):
|
|||
|
||||
"""
|
||||
|
||||
from openpype.hosts.maya.lib import get_container_transforms
|
||||
|
||||
# Process within the namespace
|
||||
with namespaced(namespace, new=False) as namespace:
|
||||
|
||||
|
|
@ -379,8 +379,6 @@ def update_scene(set_container, containers, current_data, new_data, new_file):
|
|||
|
||||
"""
|
||||
|
||||
from openpype.hosts.maya.lib import DEFAULT_MATRIX, get_container_transforms
|
||||
|
||||
set_namespace = set_container['namespace']
|
||||
project_name = legacy_io.active_project()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
import maya.cmds as cmds
|
||||
|
||||
from openpype.pipeline import (
|
||||
load,
|
||||
remove_container
|
||||
)
|
||||
|
||||
from openpype.hosts.maya.api.pipeline import containerise
|
||||
from openpype.hosts.maya.api.lib import unique_namespace
|
||||
from openpype.hosts.maya.api import setdress
|
||||
|
||||
|
||||
class AssemblyLoader(load.LoaderPlugin):
|
||||
|
||||
|
|
@ -16,9 +22,6 @@ class AssemblyLoader(load.LoaderPlugin):
|
|||
|
||||
def load(self, context, name, namespace, data):
|
||||
|
||||
from openpype.hosts.maya.api.pipeline import containerise
|
||||
from openpype.hosts.maya.api.lib import unique_namespace
|
||||
|
||||
asset = context['asset']['name']
|
||||
namespace = namespace or unique_namespace(
|
||||
asset + "_",
|
||||
|
|
@ -26,8 +29,6 @@ class AssemblyLoader(load.LoaderPlugin):
|
|||
suffix="_",
|
||||
)
|
||||
|
||||
from openpype.hosts.maya.api import setdress
|
||||
|
||||
containers = setdress.load_package(
|
||||
filepath=self.fname,
|
||||
name=name,
|
||||
|
|
@ -50,15 +51,11 @@ class AssemblyLoader(load.LoaderPlugin):
|
|||
|
||||
def update(self, container, representation):
|
||||
|
||||
from openpype import setdress
|
||||
return setdress.update_package(container, representation)
|
||||
|
||||
def remove(self, container):
|
||||
"""Remove all sub containers"""
|
||||
|
||||
from openpype import setdress
|
||||
import maya.cmds as cmds
|
||||
|
||||
# Remove all members
|
||||
member_containers = setdress.get_contained_containers(container)
|
||||
for member_container in member_containers:
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ class ValidateInstanceHasMembers(pyblish.api.InstancePlugin):
|
|||
|
||||
@classmethod
|
||||
def get_invalid(cls, instance):
|
||||
|
||||
invalid = list()
|
||||
if not instance.data["setMembers"]:
|
||||
objectset_name = instance.data['name']
|
||||
|
|
@ -22,6 +21,10 @@ class ValidateInstanceHasMembers(pyblish.api.InstancePlugin):
|
|||
return invalid
|
||||
|
||||
def process(self, instance):
|
||||
# Allow renderlayer and workfile to be empty
|
||||
skip_families = ["workfile", "renderlayer", "rendersetup"]
|
||||
if instance.data.get("family") in skip_families:
|
||||
return
|
||||
|
||||
invalid = self.get_invalid(instance)
|
||||
if invalid:
|
||||
|
|
|
|||
|
|
@ -55,7 +55,8 @@ class ValidateRigOutputIds(pyblish.api.InstancePlugin):
|
|||
if shapes:
|
||||
instance_nodes.extend(shapes)
|
||||
|
||||
scene_nodes = cmds.ls(type="transform") + cmds.ls(type="mesh")
|
||||
scene_nodes = cmds.ls(type="transform", long=True)
|
||||
scene_nodes += cmds.ls(type="mesh", long=True)
|
||||
scene_nodes = set(scene_nodes) - set(instance_nodes)
|
||||
|
||||
scene_nodes_by_basename = defaultdict(list)
|
||||
|
|
@ -76,7 +77,7 @@ class ValidateRigOutputIds(pyblish.api.InstancePlugin):
|
|||
if len(ids) > 1:
|
||||
cls.log.error(
|
||||
"\"{}\" id mismatch to: {}".format(
|
||||
instance_node.longName(), matches
|
||||
instance_node, matches
|
||||
)
|
||||
)
|
||||
invalid[instance_node] = matches
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
import os
|
||||
import re
|
||||
from openpype.modules import IHostAddon, OpenPypeModule
|
||||
from openpype.widgets.message_window import Window
|
||||
|
||||
UNREAL_ROOT_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
|
@ -19,6 +21,20 @@ class UnrealAddon(OpenPypeModule, IHostAddon):
|
|||
|
||||
from .lib import get_compatible_integration
|
||||
|
||||
pattern = re.compile(r'^\d+-\d+$')
|
||||
|
||||
if not pattern.match(app.name):
|
||||
msg = (
|
||||
"Unreal application key in the settings must be in format"
|
||||
"'5-0' or '5-1'"
|
||||
)
|
||||
Window(
|
||||
parent=None,
|
||||
title="Unreal application name format",
|
||||
message=msg,
|
||||
level="critical")
|
||||
raise ValueError(msg)
|
||||
|
||||
ue_version = app.name.replace("-", ".")
|
||||
unreal_plugin_path = os.path.join(
|
||||
UNREAL_ROOT_DIR, "integration", "UE_{}".format(ue_version), "Ayon"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ from .pipeline import (
|
|||
show_tools_popup,
|
||||
instantiate,
|
||||
UnrealHost,
|
||||
set_sequence_hierarchy,
|
||||
generate_sequence,
|
||||
maintained_selection
|
||||
)
|
||||
|
||||
|
|
@ -41,5 +43,7 @@ __all__ = [
|
|||
"show_tools_popup",
|
||||
"instantiate",
|
||||
"UnrealHost",
|
||||
"set_sequence_hierarchy",
|
||||
"generate_sequence",
|
||||
"maintained_selection"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -9,12 +9,14 @@ import time
|
|||
|
||||
import pyblish.api
|
||||
|
||||
from openpype.client import get_asset_by_name, get_assets
|
||||
from openpype.pipeline import (
|
||||
register_loader_plugin_path,
|
||||
register_creator_plugin_path,
|
||||
deregister_loader_plugin_path,
|
||||
deregister_creator_plugin_path,
|
||||
AYON_CONTAINER_ID,
|
||||
legacy_io,
|
||||
)
|
||||
from openpype.tools.utils import host_tools
|
||||
import openpype.hosts.unreal
|
||||
|
|
@ -512,6 +514,141 @@ def get_subsequences(sequence: unreal.LevelSequence):
|
|||
return []
|
||||
|
||||
|
||||
def set_sequence_hierarchy(
|
||||
seq_i, seq_j, max_frame_i, min_frame_j, max_frame_j, map_paths
|
||||
):
|
||||
# Get existing sequencer tracks or create them if they don't exist
|
||||
tracks = seq_i.get_master_tracks()
|
||||
subscene_track = None
|
||||
visibility_track = None
|
||||
for t in tracks:
|
||||
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
|
||||
subscene_track = t
|
||||
if (t.get_class() ==
|
||||
unreal.MovieSceneLevelVisibilityTrack.static_class()):
|
||||
visibility_track = t
|
||||
if not subscene_track:
|
||||
subscene_track = seq_i.add_master_track(unreal.MovieSceneSubTrack)
|
||||
if not visibility_track:
|
||||
visibility_track = seq_i.add_master_track(
|
||||
unreal.MovieSceneLevelVisibilityTrack)
|
||||
|
||||
# Create the sub-scene section
|
||||
subscenes = subscene_track.get_sections()
|
||||
subscene = None
|
||||
for s in subscenes:
|
||||
if s.get_editor_property('sub_sequence') == seq_j:
|
||||
subscene = s
|
||||
break
|
||||
if not subscene:
|
||||
subscene = subscene_track.add_section()
|
||||
subscene.set_row_index(len(subscene_track.get_sections()))
|
||||
subscene.set_editor_property('sub_sequence', seq_j)
|
||||
subscene.set_range(
|
||||
min_frame_j,
|
||||
max_frame_j + 1)
|
||||
|
||||
# Create the visibility section
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
maps = []
|
||||
for m in map_paths:
|
||||
# Unreal requires to load the level to get the map name
|
||||
unreal.EditorLevelLibrary.save_all_dirty_levels()
|
||||
unreal.EditorLevelLibrary.load_level(m)
|
||||
maps.append(str(ar.get_asset_by_object_path(m).asset_name))
|
||||
|
||||
vis_section = visibility_track.add_section()
|
||||
index = len(visibility_track.get_sections())
|
||||
|
||||
vis_section.set_range(
|
||||
min_frame_j,
|
||||
max_frame_j + 1)
|
||||
vis_section.set_visibility(unreal.LevelVisibility.VISIBLE)
|
||||
vis_section.set_row_index(index)
|
||||
vis_section.set_level_names(maps)
|
||||
|
||||
if min_frame_j > 1:
|
||||
hid_section = visibility_track.add_section()
|
||||
hid_section.set_range(
|
||||
1,
|
||||
min_frame_j)
|
||||
hid_section.set_visibility(unreal.LevelVisibility.HIDDEN)
|
||||
hid_section.set_row_index(index)
|
||||
hid_section.set_level_names(maps)
|
||||
if max_frame_j < max_frame_i:
|
||||
hid_section = visibility_track.add_section()
|
||||
hid_section.set_range(
|
||||
max_frame_j + 1,
|
||||
max_frame_i + 1)
|
||||
hid_section.set_visibility(unreal.LevelVisibility.HIDDEN)
|
||||
hid_section.set_row_index(index)
|
||||
hid_section.set_level_names(maps)
|
||||
|
||||
|
||||
def generate_sequence(h, h_dir):
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
|
||||
sequence = tools.create_asset(
|
||||
asset_name=h,
|
||||
package_path=h_dir,
|
||||
asset_class=unreal.LevelSequence,
|
||||
factory=unreal.LevelSequenceFactoryNew()
|
||||
)
|
||||
|
||||
project_name = legacy_io.active_project()
|
||||
asset_data = get_asset_by_name(
|
||||
project_name,
|
||||
h_dir.split('/')[-1],
|
||||
fields=["_id", "data.fps"]
|
||||
)
|
||||
|
||||
start_frames = []
|
||||
end_frames = []
|
||||
|
||||
elements = list(get_assets(
|
||||
project_name,
|
||||
parent_ids=[asset_data["_id"]],
|
||||
fields=["_id", "data.clipIn", "data.clipOut"]
|
||||
))
|
||||
for e in elements:
|
||||
start_frames.append(e.get('data').get('clipIn'))
|
||||
end_frames.append(e.get('data').get('clipOut'))
|
||||
|
||||
elements.extend(get_assets(
|
||||
project_name,
|
||||
parent_ids=[e["_id"]],
|
||||
fields=["_id", "data.clipIn", "data.clipOut"]
|
||||
))
|
||||
|
||||
min_frame = min(start_frames)
|
||||
max_frame = max(end_frames)
|
||||
|
||||
fps = asset_data.get('data').get("fps")
|
||||
|
||||
sequence.set_display_rate(
|
||||
unreal.FrameRate(fps, 1.0))
|
||||
sequence.set_playback_start(min_frame)
|
||||
sequence.set_playback_end(max_frame)
|
||||
|
||||
sequence.set_work_range_start(min_frame / fps)
|
||||
sequence.set_work_range_end(max_frame / fps)
|
||||
sequence.set_view_range_start(min_frame / fps)
|
||||
sequence.set_view_range_end(max_frame / fps)
|
||||
|
||||
tracks = sequence.get_master_tracks()
|
||||
track = None
|
||||
for t in tracks:
|
||||
if (t.get_class() ==
|
||||
unreal.MovieSceneCameraCutTrack.static_class()):
|
||||
track = t
|
||||
break
|
||||
if not track:
|
||||
track = sequence.add_master_track(
|
||||
unreal.MovieSceneCameraCutTrack)
|
||||
|
||||
return sequence, (min_frame, max_frame)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def maintained_selection():
|
||||
"""Stub to be either implemented or replaced.
|
||||
|
|
|
|||
|
|
@ -3,16 +3,24 @@
|
|||
from pathlib import Path
|
||||
|
||||
import unreal
|
||||
from unreal import EditorAssetLibrary
|
||||
from unreal import EditorLevelLibrary
|
||||
from unreal import EditorLevelUtils
|
||||
from openpype.client import get_assets, get_asset_by_name
|
||||
from unreal import (
|
||||
EditorAssetLibrary,
|
||||
EditorLevelLibrary,
|
||||
EditorLevelUtils,
|
||||
LevelSequenceEditorBlueprintLibrary as LevelSequenceLib,
|
||||
)
|
||||
from openpype.client import get_asset_by_name
|
||||
from openpype.pipeline import (
|
||||
AYON_CONTAINER_ID,
|
||||
legacy_io,
|
||||
)
|
||||
from openpype.hosts.unreal.api import plugin
|
||||
from openpype.hosts.unreal.api import pipeline as unreal_pipeline
|
||||
from openpype.hosts.unreal.api.pipeline import (
|
||||
generate_sequence,
|
||||
set_sequence_hierarchy,
|
||||
create_container,
|
||||
imprint,
|
||||
)
|
||||
|
||||
|
||||
class CameraLoader(plugin.Loader):
|
||||
|
|
@ -24,32 +32,6 @@ class CameraLoader(plugin.Loader):
|
|||
icon = "cube"
|
||||
color = "orange"
|
||||
|
||||
def _set_sequence_hierarchy(
|
||||
self, seq_i, seq_j, min_frame_j, max_frame_j
|
||||
):
|
||||
tracks = seq_i.get_master_tracks()
|
||||
track = None
|
||||
for t in tracks:
|
||||
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
|
||||
track = t
|
||||
break
|
||||
if not track:
|
||||
track = seq_i.add_master_track(unreal.MovieSceneSubTrack)
|
||||
|
||||
subscenes = track.get_sections()
|
||||
subscene = None
|
||||
for s in subscenes:
|
||||
if s.get_editor_property('sub_sequence') == seq_j:
|
||||
subscene = s
|
||||
break
|
||||
if not subscene:
|
||||
subscene = track.add_section()
|
||||
subscene.set_row_index(len(track.get_sections()))
|
||||
subscene.set_editor_property('sub_sequence', seq_j)
|
||||
subscene.set_range(
|
||||
min_frame_j,
|
||||
max_frame_j + 1)
|
||||
|
||||
def _import_camera(
|
||||
self, world, sequence, bindings, import_fbx_settings, import_filename
|
||||
):
|
||||
|
|
@ -110,10 +92,7 @@ class CameraLoader(plugin.Loader):
|
|||
hierarchy_dir_list.append(hierarchy_dir)
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
if asset:
|
||||
asset_name = "{}_{}".format(asset, name)
|
||||
else:
|
||||
asset_name = "{}".format(name)
|
||||
asset_name = f"{asset}_{name}" if asset else f"{name}"
|
||||
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
|
||||
|
|
@ -127,23 +106,15 @@ class CameraLoader(plugin.Loader):
|
|||
# Get highest number to make a unique name
|
||||
folders = [a for a in asset_content
|
||||
if a[-1] == "/" and f"{name}_" in a]
|
||||
f_numbers = []
|
||||
for f in folders:
|
||||
# Get number from folder name. Splits the string by "_" and
|
||||
# removes the last element (which is a "/").
|
||||
f_numbers.append(int(f.split("_")[-1][:-1]))
|
||||
# Get number from folder name. Splits the string by "_" and
|
||||
# removes the last element (which is a "/").
|
||||
f_numbers = [int(f.split("_")[-1][:-1]) for f in folders]
|
||||
f_numbers.sort()
|
||||
if not f_numbers:
|
||||
unique_number = 1
|
||||
else:
|
||||
unique_number = f_numbers[-1] + 1
|
||||
unique_number = f_numbers[-1] + 1 if f_numbers else 1
|
||||
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
f"{hierarchy_dir}/{asset}/{name}_{unique_number:02d}", suffix="")
|
||||
|
||||
asset_path = Path(asset_dir)
|
||||
asset_path_parent = str(asset_path.parent.as_posix())
|
||||
|
||||
container_name += suffix
|
||||
|
||||
EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
|
@ -156,9 +127,9 @@ class CameraLoader(plugin.Loader):
|
|||
if not EditorAssetLibrary.does_asset_exist(master_level):
|
||||
EditorLevelLibrary.new_level(f"{h_dir}/{h_asset}_map")
|
||||
|
||||
level = f"{asset_path_parent}/{asset}_map.{asset}_map"
|
||||
level = f"{asset_dir}/{asset}_map_camera.{asset}_map_camera"
|
||||
if not EditorAssetLibrary.does_asset_exist(level):
|
||||
EditorLevelLibrary.new_level(f"{asset_path_parent}/{asset}_map")
|
||||
EditorLevelLibrary.new_level(f"{asset_dir}/{asset}_map_camera")
|
||||
|
||||
EditorLevelLibrary.load_level(master_level)
|
||||
EditorLevelUtils.add_level_to_world(
|
||||
|
|
@ -169,27 +140,13 @@ class CameraLoader(plugin.Loader):
|
|||
EditorLevelLibrary.save_all_dirty_levels()
|
||||
EditorLevelLibrary.load_level(level)
|
||||
|
||||
project_name = legacy_io.active_project()
|
||||
# TODO refactor
|
||||
# - Creating of hierarchy should be a function in unreal integration
|
||||
# - it's used in multiple loaders but must not be loader's logic
|
||||
# - hard to say what is purpose of the loop
|
||||
# - variables does not match their meaning
|
||||
# - why scene is stored to sequences?
|
||||
# - asset documents vs. elements
|
||||
# - cleanup variable names in whole function
|
||||
# - e.g. 'asset', 'asset_name', 'asset_data', 'asset_doc'
|
||||
# - really inefficient queries of asset documents
|
||||
# - existing asset in scene is considered as "with correct values"
|
||||
# - variable 'elements' is modified during it's loop
|
||||
# Get all the sequences in the hierarchy. It will create them, if
|
||||
# they don't exist.
|
||||
sequences = []
|
||||
frame_ranges = []
|
||||
i = 0
|
||||
for h in hierarchy_dir_list:
|
||||
sequences = []
|
||||
for (h_dir, h) in zip(hierarchy_dir_list, hierarchy):
|
||||
root_content = EditorAssetLibrary.list_assets(
|
||||
h, recursive=False, include_folder=False)
|
||||
h_dir, recursive=False, include_folder=False)
|
||||
|
||||
existing_sequences = [
|
||||
EditorAssetLibrary.find_asset_data(asset)
|
||||
|
|
@ -198,57 +155,17 @@ class CameraLoader(plugin.Loader):
|
|||
asset).get_class().get_name() == 'LevelSequence'
|
||||
]
|
||||
|
||||
if not existing_sequences:
|
||||
scene = tools.create_asset(
|
||||
asset_name=hierarchy[i],
|
||||
package_path=h,
|
||||
asset_class=unreal.LevelSequence,
|
||||
factory=unreal.LevelSequenceFactoryNew()
|
||||
)
|
||||
|
||||
asset_data = get_asset_by_name(
|
||||
project_name,
|
||||
h.split('/')[-1],
|
||||
fields=["_id", "data.fps"]
|
||||
)
|
||||
|
||||
start_frames = []
|
||||
end_frames = []
|
||||
|
||||
elements = list(get_assets(
|
||||
project_name,
|
||||
parent_ids=[asset_data["_id"]],
|
||||
fields=["_id", "data.clipIn", "data.clipOut"]
|
||||
))
|
||||
|
||||
for e in elements:
|
||||
start_frames.append(e.get('data').get('clipIn'))
|
||||
end_frames.append(e.get('data').get('clipOut'))
|
||||
|
||||
elements.extend(get_assets(
|
||||
project_name,
|
||||
parent_ids=[e["_id"]],
|
||||
fields=["_id", "data.clipIn", "data.clipOut"]
|
||||
))
|
||||
|
||||
min_frame = min(start_frames)
|
||||
max_frame = max(end_frames)
|
||||
|
||||
scene.set_display_rate(
|
||||
unreal.FrameRate(asset_data.get('data').get("fps"), 1.0))
|
||||
scene.set_playback_start(min_frame)
|
||||
scene.set_playback_end(max_frame)
|
||||
|
||||
sequences.append(scene)
|
||||
frame_ranges.append((min_frame, max_frame))
|
||||
else:
|
||||
for e in existing_sequences:
|
||||
sequences.append(e.get_asset())
|
||||
if existing_sequences:
|
||||
for seq in existing_sequences:
|
||||
sequences.append(seq.get_asset())
|
||||
frame_ranges.append((
|
||||
e.get_asset().get_playback_start(),
|
||||
e.get_asset().get_playback_end()))
|
||||
seq.get_asset().get_playback_start(),
|
||||
seq.get_asset().get_playback_end()))
|
||||
else:
|
||||
sequence, frame_range = generate_sequence(h, h_dir)
|
||||
|
||||
i += 1
|
||||
sequences.append(sequence)
|
||||
frame_ranges.append(frame_range)
|
||||
|
||||
EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
|
|
@ -260,19 +177,24 @@ class CameraLoader(plugin.Loader):
|
|||
)
|
||||
|
||||
# Add sequences data to hierarchy
|
||||
for i in range(0, len(sequences) - 1):
|
||||
self._set_sequence_hierarchy(
|
||||
for i in range(len(sequences) - 1):
|
||||
set_sequence_hierarchy(
|
||||
sequences[i], sequences[i + 1],
|
||||
frame_ranges[i + 1][0], frame_ranges[i + 1][1])
|
||||
frame_ranges[i][1],
|
||||
frame_ranges[i + 1][0], frame_ranges[i + 1][1],
|
||||
[level])
|
||||
|
||||
project_name = legacy_io.active_project()
|
||||
data = get_asset_by_name(project_name, asset)["data"]
|
||||
cam_seq.set_display_rate(
|
||||
unreal.FrameRate(data.get("fps"), 1.0))
|
||||
cam_seq.set_playback_start(data.get('clipIn'))
|
||||
cam_seq.set_playback_end(data.get('clipOut') + 1)
|
||||
self._set_sequence_hierarchy(
|
||||
set_sequence_hierarchy(
|
||||
sequences[-1], cam_seq,
|
||||
data.get('clipIn'), data.get('clipOut'))
|
||||
frame_ranges[-1][1],
|
||||
data.get('clipIn'), data.get('clipOut'),
|
||||
[level])
|
||||
|
||||
settings = unreal.MovieSceneUserImportFBXSettings()
|
||||
settings.set_editor_property('reduce_keys', False)
|
||||
|
|
@ -307,7 +229,7 @@ class CameraLoader(plugin.Loader):
|
|||
key.set_time(unreal.FrameNumber(value=new_time))
|
||||
|
||||
# Create Asset Container
|
||||
unreal_pipeline.create_container(
|
||||
create_container(
|
||||
container=container_name, path=asset_dir)
|
||||
|
||||
data = {
|
||||
|
|
@ -322,14 +244,14 @@ class CameraLoader(plugin.Loader):
|
|||
"parent": context["representation"]["parent"],
|
||||
"family": context["representation"]["context"]["family"]
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
"{}/{}".format(asset_dir, container_name), data)
|
||||
imprint(f"{asset_dir}/{container_name}", data)
|
||||
|
||||
EditorLevelLibrary.save_all_dirty_levels()
|
||||
EditorLevelLibrary.load_level(master_level)
|
||||
|
||||
# Save all assets in the hierarchy
|
||||
asset_content = EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=True
|
||||
hierarchy_dir_list[0], recursive=True, include_folder=False
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
|
|
@ -340,29 +262,27 @@ class CameraLoader(plugin.Loader):
|
|||
def update(self, container, representation):
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
|
||||
root = "/Game/ayon"
|
||||
curr_level_sequence = LevelSequenceLib.get_current_level_sequence()
|
||||
curr_time = LevelSequenceLib.get_current_time()
|
||||
is_cam_lock = LevelSequenceLib.is_camera_cut_locked_to_viewport()
|
||||
|
||||
editor_subsystem = unreal.UnrealEditorSubsystem()
|
||||
vp_loc, vp_rot = editor_subsystem.get_level_viewport_camera_info()
|
||||
|
||||
asset_dir = container.get('namespace')
|
||||
|
||||
context = representation.get("context")
|
||||
|
||||
hierarchy = context.get('hierarchy').split("/")
|
||||
h_dir = f"{root}/{hierarchy[0]}"
|
||||
h_asset = hierarchy[0]
|
||||
master_level = f"{h_dir}/{h_asset}_map.{h_asset}_map"
|
||||
|
||||
EditorLevelLibrary.save_current_level()
|
||||
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["LevelSequence"],
|
||||
package_paths=[asset_dir],
|
||||
recursive_paths=False)
|
||||
sequences = ar.get_assets(filter)
|
||||
filter = unreal.ARFilter(
|
||||
sequences = ar.get_assets(_filter)
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[str(Path(asset_dir).parent.as_posix())],
|
||||
package_paths=[asset_dir],
|
||||
recursive_paths=True)
|
||||
maps = ar.get_assets(filter)
|
||||
maps = ar.get_assets(_filter)
|
||||
|
||||
# There should be only one map in the list
|
||||
EditorLevelLibrary.load_level(maps[0].get_asset().get_path_name())
|
||||
|
|
@ -401,12 +321,18 @@ class CameraLoader(plugin.Loader):
|
|||
root = "/Game/Ayon"
|
||||
namespace = container.get('namespace').replace(f"{root}/", "")
|
||||
ms_asset = namespace.split('/')[0]
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["LevelSequence"],
|
||||
package_paths=[f"{root}/{ms_asset}"],
|
||||
recursive_paths=False)
|
||||
sequences = ar.get_assets(filter)
|
||||
sequences = ar.get_assets(_filter)
|
||||
master_sequence = sequences[0].get_asset()
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{root}/{ms_asset}"],
|
||||
recursive_paths=False)
|
||||
levels = ar.get_assets(_filter)
|
||||
master_level = levels[0].get_asset().get_path_name()
|
||||
|
||||
sequences = [master_sequence]
|
||||
|
||||
|
|
@ -418,26 +344,20 @@ class CameraLoader(plugin.Loader):
|
|||
for t in tracks:
|
||||
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
|
||||
subscene_track = t
|
||||
break
|
||||
if subscene_track:
|
||||
sections = subscene_track.get_sections()
|
||||
for ss in sections:
|
||||
if ss.get_sequence().get_name() == sequence_name:
|
||||
parent = s
|
||||
sub_scene = ss
|
||||
# subscene_track.remove_section(ss)
|
||||
break
|
||||
sequences.append(ss.get_sequence())
|
||||
# Update subscenes indexes.
|
||||
i = 0
|
||||
for ss in sections:
|
||||
for i, ss in enumerate(sections):
|
||||
ss.set_row_index(i)
|
||||
i += 1
|
||||
|
||||
if parent:
|
||||
break
|
||||
|
||||
assert parent, "Could not find the parent sequence"
|
||||
assert parent, "Could not find the parent sequence"
|
||||
|
||||
EditorAssetLibrary.delete_asset(level_sequence.get_path_name())
|
||||
|
||||
|
|
@ -466,33 +386,63 @@ class CameraLoader(plugin.Loader):
|
|||
str(representation["data"]["path"])
|
||||
)
|
||||
|
||||
# Set range of all sections
|
||||
# Changing the range of the section is not enough. We need to change
|
||||
# the frame of all the keys in the section.
|
||||
project_name = legacy_io.active_project()
|
||||
asset = container.get('asset')
|
||||
data = get_asset_by_name(project_name, asset)["data"]
|
||||
|
||||
for possessable in new_sequence.get_possessables():
|
||||
for tracks in possessable.get_tracks():
|
||||
for section in tracks.get_sections():
|
||||
section.set_range(
|
||||
data.get('clipIn'),
|
||||
data.get('clipOut') + 1)
|
||||
for channel in section.get_all_channels():
|
||||
for key in channel.get_keys():
|
||||
old_time = key.get_time().get_editor_property(
|
||||
'frame_number')
|
||||
old_time_value = old_time.get_editor_property(
|
||||
'value')
|
||||
new_time = old_time_value + (
|
||||
data.get('clipIn') - data.get('frameStart')
|
||||
)
|
||||
key.set_time(unreal.FrameNumber(value=new_time))
|
||||
|
||||
data = {
|
||||
"representation": str(representation["_id"]),
|
||||
"parent": str(representation["parent"])
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
"{}/{}".format(asset_dir, container.get('container_name')), data)
|
||||
imprint(f"{asset_dir}/{container.get('container_name')}", data)
|
||||
|
||||
EditorLevelLibrary.save_current_level()
|
||||
|
||||
asset_content = EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=False)
|
||||
f"{root}/{ms_asset}", recursive=True, include_folder=False)
|
||||
|
||||
for a in asset_content:
|
||||
EditorAssetLibrary.save_asset(a)
|
||||
|
||||
EditorLevelLibrary.load_level(master_level)
|
||||
|
||||
if curr_level_sequence:
|
||||
LevelSequenceLib.open_level_sequence(curr_level_sequence)
|
||||
LevelSequenceLib.set_current_time(curr_time)
|
||||
LevelSequenceLib.set_lock_camera_cut_to_viewport(is_cam_lock)
|
||||
|
||||
editor_subsystem.set_level_viewport_camera_info(vp_loc, vp_rot)
|
||||
|
||||
def remove(self, container):
|
||||
path = Path(container.get("namespace"))
|
||||
parent_path = str(path.parent.as_posix())
|
||||
asset_dir = container.get('namespace')
|
||||
path = Path(asset_dir)
|
||||
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["LevelSequence"],
|
||||
package_paths=[f"{str(path.as_posix())}"],
|
||||
package_paths=[asset_dir],
|
||||
recursive_paths=False)
|
||||
sequences = ar.get_assets(filter)
|
||||
sequences = ar.get_assets(_filter)
|
||||
|
||||
if not sequences:
|
||||
raise Exception("Could not find sequence.")
|
||||
|
|
@ -500,11 +450,11 @@ class CameraLoader(plugin.Loader):
|
|||
world = ar.get_asset_by_object_path(
|
||||
EditorLevelLibrary.get_editor_world().get_path_name())
|
||||
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{parent_path}"],
|
||||
package_paths=[asset_dir],
|
||||
recursive_paths=True)
|
||||
maps = ar.get_assets(filter)
|
||||
maps = ar.get_assets(_filter)
|
||||
|
||||
# There should be only one map in the list
|
||||
if not maps:
|
||||
|
|
@ -534,12 +484,18 @@ class CameraLoader(plugin.Loader):
|
|||
root = "/Game/Ayon"
|
||||
namespace = container.get('namespace').replace(f"{root}/", "")
|
||||
ms_asset = namespace.split('/')[0]
|
||||
filter = unreal.ARFilter(
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["LevelSequence"],
|
||||
package_paths=[f"{root}/{ms_asset}"],
|
||||
recursive_paths=False)
|
||||
sequences = ar.get_assets(filter)
|
||||
sequences = ar.get_assets(_filter)
|
||||
master_sequence = sequences[0].get_asset()
|
||||
_filter = unreal.ARFilter(
|
||||
class_names=["World"],
|
||||
package_paths=[f"{root}/{ms_asset}"],
|
||||
recursive_paths=False)
|
||||
levels = ar.get_assets(_filter)
|
||||
master_level = levels[0].get_full_name()
|
||||
|
||||
sequences = [master_sequence]
|
||||
|
||||
|
|
@ -547,10 +503,13 @@ class CameraLoader(plugin.Loader):
|
|||
for s in sequences:
|
||||
tracks = s.get_master_tracks()
|
||||
subscene_track = None
|
||||
visibility_track = None
|
||||
for t in tracks:
|
||||
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
|
||||
subscene_track = t
|
||||
break
|
||||
if (t.get_class() ==
|
||||
unreal.MovieSceneLevelVisibilityTrack.static_class()):
|
||||
visibility_track = t
|
||||
if subscene_track:
|
||||
sections = subscene_track.get_sections()
|
||||
for ss in sections:
|
||||
|
|
@ -560,23 +519,48 @@ class CameraLoader(plugin.Loader):
|
|||
break
|
||||
sequences.append(ss.get_sequence())
|
||||
# Update subscenes indexes.
|
||||
i = 0
|
||||
for ss in sections:
|
||||
for i, ss in enumerate(sections):
|
||||
ss.set_row_index(i)
|
||||
i += 1
|
||||
|
||||
if visibility_track:
|
||||
sections = visibility_track.get_sections()
|
||||
for ss in sections:
|
||||
if (unreal.Name(f"{container.get('asset')}_map_camera")
|
||||
in ss.get_level_names()):
|
||||
visibility_track.remove_section(ss)
|
||||
# Update visibility sections indexes.
|
||||
i = -1
|
||||
prev_name = []
|
||||
for ss in sections:
|
||||
if prev_name != ss.get_level_names():
|
||||
i += 1
|
||||
ss.set_row_index(i)
|
||||
prev_name = ss.get_level_names()
|
||||
if parent:
|
||||
break
|
||||
|
||||
assert parent, "Could not find the parent sequence"
|
||||
|
||||
EditorAssetLibrary.delete_directory(str(path.as_posix()))
|
||||
# Create a temporary level to delete the layout level.
|
||||
EditorLevelLibrary.save_all_dirty_levels()
|
||||
EditorAssetLibrary.make_directory(f"{root}/tmp")
|
||||
tmp_level = f"{root}/tmp/temp_map"
|
||||
if not EditorAssetLibrary.does_asset_exist(f"{tmp_level}.temp_map"):
|
||||
EditorLevelLibrary.new_level(tmp_level)
|
||||
else:
|
||||
EditorLevelLibrary.load_level(tmp_level)
|
||||
|
||||
# Delete the layout directory.
|
||||
EditorAssetLibrary.delete_directory(asset_dir)
|
||||
|
||||
EditorLevelLibrary.load_level(master_level)
|
||||
EditorAssetLibrary.delete_directory(f"{root}/tmp")
|
||||
|
||||
# Check if there isn't any more assets in the parent folder, and
|
||||
# delete it if not.
|
||||
asset_content = EditorAssetLibrary.list_assets(
|
||||
parent_path, recursive=False, include_folder=True
|
||||
path.parent.as_posix(), recursive=False, include_folder=True
|
||||
)
|
||||
|
||||
if len(asset_content) == 0:
|
||||
EditorAssetLibrary.delete_directory(parent_path)
|
||||
EditorAssetLibrary.delete_directory(path.parent.as_posix())
|
||||
|
|
|
|||
|
|
@ -5,15 +5,18 @@ import collections
|
|||
from pathlib import Path
|
||||
|
||||
import unreal
|
||||
from unreal import EditorAssetLibrary
|
||||
from unreal import EditorLevelLibrary
|
||||
from unreal import EditorLevelUtils
|
||||
from unreal import AssetToolsHelpers
|
||||
from unreal import FBXImportType
|
||||
from unreal import MovieSceneLevelVisibilityTrack
|
||||
from unreal import MovieSceneSubTrack
|
||||
from unreal import (
|
||||
EditorAssetLibrary,
|
||||
EditorLevelLibrary,
|
||||
EditorLevelUtils,
|
||||
AssetToolsHelpers,
|
||||
FBXImportType,
|
||||
MovieSceneLevelVisibilityTrack,
|
||||
MovieSceneSubTrack,
|
||||
LevelSequenceEditorBlueprintLibrary as LevelSequenceLib,
|
||||
)
|
||||
|
||||
from openpype.client import get_asset_by_name, get_assets, get_representations
|
||||
from openpype.client import get_asset_by_name, get_representations
|
||||
from openpype.pipeline import (
|
||||
discover_loader_plugins,
|
||||
loaders_from_representation,
|
||||
|
|
@ -25,7 +28,13 @@ from openpype.pipeline import (
|
|||
from openpype.pipeline.context_tools import get_current_project_asset
|
||||
from openpype.settings import get_current_project_settings
|
||||
from openpype.hosts.unreal.api import plugin
|
||||
from openpype.hosts.unreal.api import pipeline as unreal_pipeline
|
||||
from openpype.hosts.unreal.api.pipeline import (
|
||||
generate_sequence,
|
||||
set_sequence_hierarchy,
|
||||
create_container,
|
||||
imprint,
|
||||
ls,
|
||||
)
|
||||
|
||||
|
||||
class LayoutLoader(plugin.Loader):
|
||||
|
|
@ -91,77 +100,6 @@ class LayoutLoader(plugin.Loader):
|
|||
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _set_sequence_hierarchy(
|
||||
seq_i, seq_j, max_frame_i, min_frame_j, max_frame_j, map_paths
|
||||
):
|
||||
# Get existing sequencer tracks or create them if they don't exist
|
||||
tracks = seq_i.get_master_tracks()
|
||||
subscene_track = None
|
||||
visibility_track = None
|
||||
for t in tracks:
|
||||
if t.get_class() == unreal.MovieSceneSubTrack.static_class():
|
||||
subscene_track = t
|
||||
if (t.get_class() ==
|
||||
unreal.MovieSceneLevelVisibilityTrack.static_class()):
|
||||
visibility_track = t
|
||||
if not subscene_track:
|
||||
subscene_track = seq_i.add_master_track(unreal.MovieSceneSubTrack)
|
||||
if not visibility_track:
|
||||
visibility_track = seq_i.add_master_track(
|
||||
unreal.MovieSceneLevelVisibilityTrack)
|
||||
|
||||
# Create the sub-scene section
|
||||
subscenes = subscene_track.get_sections()
|
||||
subscene = None
|
||||
for s in subscenes:
|
||||
if s.get_editor_property('sub_sequence') == seq_j:
|
||||
subscene = s
|
||||
break
|
||||
if not subscene:
|
||||
subscene = subscene_track.add_section()
|
||||
subscene.set_row_index(len(subscene_track.get_sections()))
|
||||
subscene.set_editor_property('sub_sequence', seq_j)
|
||||
subscene.set_range(
|
||||
min_frame_j,
|
||||
max_frame_j + 1)
|
||||
|
||||
# Create the visibility section
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
maps = []
|
||||
for m in map_paths:
|
||||
# Unreal requires to load the level to get the map name
|
||||
EditorLevelLibrary.save_all_dirty_levels()
|
||||
EditorLevelLibrary.load_level(m)
|
||||
maps.append(str(ar.get_asset_by_object_path(m).asset_name))
|
||||
|
||||
vis_section = visibility_track.add_section()
|
||||
index = len(visibility_track.get_sections())
|
||||
|
||||
vis_section.set_range(
|
||||
min_frame_j,
|
||||
max_frame_j + 1)
|
||||
vis_section.set_visibility(unreal.LevelVisibility.VISIBLE)
|
||||
vis_section.set_row_index(index)
|
||||
vis_section.set_level_names(maps)
|
||||
|
||||
if min_frame_j > 1:
|
||||
hid_section = visibility_track.add_section()
|
||||
hid_section.set_range(
|
||||
1,
|
||||
min_frame_j)
|
||||
hid_section.set_visibility(unreal.LevelVisibility.HIDDEN)
|
||||
hid_section.set_row_index(index)
|
||||
hid_section.set_level_names(maps)
|
||||
if max_frame_j < max_frame_i:
|
||||
hid_section = visibility_track.add_section()
|
||||
hid_section.set_range(
|
||||
max_frame_j + 1,
|
||||
max_frame_i + 1)
|
||||
hid_section.set_visibility(unreal.LevelVisibility.HIDDEN)
|
||||
hid_section.set_row_index(index)
|
||||
hid_section.set_level_names(maps)
|
||||
|
||||
def _transform_from_basis(self, transform, basis):
|
||||
"""Transform a transform from a basis to a new basis."""
|
||||
# Get the basis matrix
|
||||
|
|
@ -352,63 +290,6 @@ class LayoutLoader(plugin.Loader):
|
|||
sec_params = section.get_editor_property('params')
|
||||
sec_params.set_editor_property('animation', animation)
|
||||
|
||||
@staticmethod
|
||||
def _generate_sequence(h, h_dir):
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
|
||||
sequence = tools.create_asset(
|
||||
asset_name=h,
|
||||
package_path=h_dir,
|
||||
asset_class=unreal.LevelSequence,
|
||||
factory=unreal.LevelSequenceFactoryNew()
|
||||
)
|
||||
|
||||
project_name = legacy_io.active_project()
|
||||
asset_data = get_asset_by_name(
|
||||
project_name,
|
||||
h_dir.split('/')[-1],
|
||||
fields=["_id", "data.fps"]
|
||||
)
|
||||
|
||||
start_frames = []
|
||||
end_frames = []
|
||||
|
||||
elements = list(get_assets(
|
||||
project_name,
|
||||
parent_ids=[asset_data["_id"]],
|
||||
fields=["_id", "data.clipIn", "data.clipOut"]
|
||||
))
|
||||
for e in elements:
|
||||
start_frames.append(e.get('data').get('clipIn'))
|
||||
end_frames.append(e.get('data').get('clipOut'))
|
||||
|
||||
elements.extend(get_assets(
|
||||
project_name,
|
||||
parent_ids=[e["_id"]],
|
||||
fields=["_id", "data.clipIn", "data.clipOut"]
|
||||
))
|
||||
|
||||
min_frame = min(start_frames)
|
||||
max_frame = max(end_frames)
|
||||
|
||||
sequence.set_display_rate(
|
||||
unreal.FrameRate(asset_data.get('data').get("fps"), 1.0))
|
||||
sequence.set_playback_start(min_frame)
|
||||
sequence.set_playback_end(max_frame)
|
||||
|
||||
tracks = sequence.get_master_tracks()
|
||||
track = None
|
||||
for t in tracks:
|
||||
if (t.get_class() ==
|
||||
unreal.MovieSceneCameraCutTrack.static_class()):
|
||||
track = t
|
||||
break
|
||||
if not track:
|
||||
track = sequence.add_master_track(
|
||||
unreal.MovieSceneCameraCutTrack)
|
||||
|
||||
return sequence, (min_frame, max_frame)
|
||||
|
||||
def _get_repre_docs_by_version_id(self, data):
|
||||
version_ids = {
|
||||
element.get("version")
|
||||
|
|
@ -696,7 +577,7 @@ class LayoutLoader(plugin.Loader):
|
|||
]
|
||||
|
||||
if not existing_sequences:
|
||||
sequence, frame_range = self._generate_sequence(h, h_dir)
|
||||
sequence, frame_range = generate_sequence(h, h_dir)
|
||||
|
||||
sequences.append(sequence)
|
||||
frame_ranges.append(frame_range)
|
||||
|
|
@ -716,7 +597,7 @@ class LayoutLoader(plugin.Loader):
|
|||
|
||||
# sequences and frame_ranges have the same length
|
||||
for i in range(0, len(sequences) - 1):
|
||||
self._set_sequence_hierarchy(
|
||||
set_sequence_hierarchy(
|
||||
sequences[i], sequences[i + 1],
|
||||
frame_ranges[i][1],
|
||||
frame_ranges[i + 1][0], frame_ranges[i + 1][1],
|
||||
|
|
@ -729,7 +610,7 @@ class LayoutLoader(plugin.Loader):
|
|||
shot.set_playback_start(0)
|
||||
shot.set_playback_end(data.get('clipOut') - data.get('clipIn') + 1)
|
||||
if sequences:
|
||||
self._set_sequence_hierarchy(
|
||||
set_sequence_hierarchy(
|
||||
sequences[-1], shot,
|
||||
frame_ranges[-1][1],
|
||||
data.get('clipIn'), data.get('clipOut'),
|
||||
|
|
@ -745,7 +626,7 @@ class LayoutLoader(plugin.Loader):
|
|||
EditorLevelLibrary.save_current_level()
|
||||
|
||||
# Create Asset Container
|
||||
unreal_pipeline.create_container(
|
||||
create_container(
|
||||
container=container_name, path=asset_dir)
|
||||
|
||||
data = {
|
||||
|
|
@ -761,11 +642,13 @@ class LayoutLoader(plugin.Loader):
|
|||
"family": context["representation"]["context"]["family"],
|
||||
"loaded_assets": loaded_assets
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
imprint(
|
||||
"{}/{}".format(asset_dir, container_name), data)
|
||||
|
||||
save_dir = hierarchy_dir_list[0] if create_sequences else asset_dir
|
||||
|
||||
asset_content = EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=False)
|
||||
save_dir, recursive=True, include_folder=False)
|
||||
|
||||
for a in asset_content:
|
||||
EditorAssetLibrary.save_asset(a)
|
||||
|
|
@ -781,16 +664,24 @@ class LayoutLoader(plugin.Loader):
|
|||
|
||||
ar = unreal.AssetRegistryHelpers.get_asset_registry()
|
||||
|
||||
curr_level_sequence = LevelSequenceLib.get_current_level_sequence()
|
||||
curr_time = LevelSequenceLib.get_current_time()
|
||||
is_cam_lock = LevelSequenceLib.is_camera_cut_locked_to_viewport()
|
||||
|
||||
editor_subsystem = unreal.UnrealEditorSubsystem()
|
||||
vp_loc, vp_rot = editor_subsystem.get_level_viewport_camera_info()
|
||||
|
||||
root = "/Game/Ayon"
|
||||
|
||||
asset_dir = container.get('namespace')
|
||||
context = representation.get("context")
|
||||
|
||||
hierarchy = context.get('hierarchy').split("/")
|
||||
|
||||
sequence = None
|
||||
master_level = None
|
||||
|
||||
if create_sequences:
|
||||
hierarchy = context.get('hierarchy').split("/")
|
||||
h_dir = f"{root}/{hierarchy[0]}"
|
||||
h_asset = hierarchy[0]
|
||||
master_level = f"{h_dir}/{h_asset}_map.{h_asset}_map"
|
||||
|
|
@ -843,13 +734,15 @@ class LayoutLoader(plugin.Loader):
|
|||
"parent": str(representation["parent"]),
|
||||
"loaded_assets": loaded_assets
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
imprint(
|
||||
"{}/{}".format(asset_dir, container.get('container_name')), data)
|
||||
|
||||
EditorLevelLibrary.save_current_level()
|
||||
|
||||
save_dir = f"{root}/{hierarchy[0]}" if create_sequences else asset_dir
|
||||
|
||||
asset_content = EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=False)
|
||||
save_dir, recursive=True, include_folder=False)
|
||||
|
||||
for a in asset_content:
|
||||
EditorAssetLibrary.save_asset(a)
|
||||
|
|
@ -859,6 +752,13 @@ class LayoutLoader(plugin.Loader):
|
|||
elif prev_level:
|
||||
EditorLevelLibrary.load_level(prev_level)
|
||||
|
||||
if curr_level_sequence:
|
||||
LevelSequenceLib.open_level_sequence(curr_level_sequence)
|
||||
LevelSequenceLib.set_current_time(curr_time)
|
||||
LevelSequenceLib.set_lock_camera_cut_to_viewport(is_cam_lock)
|
||||
|
||||
editor_subsystem.set_level_viewport_camera_info(vp_loc, vp_rot)
|
||||
|
||||
def remove(self, container):
|
||||
"""
|
||||
Delete the layout. First, check if the assets loaded with the layout
|
||||
|
|
@ -870,7 +770,7 @@ class LayoutLoader(plugin.Loader):
|
|||
root = "/Game/Ayon"
|
||||
path = Path(container.get("namespace"))
|
||||
|
||||
containers = unreal_pipeline.ls()
|
||||
containers = ls()
|
||||
layout_containers = [
|
||||
c for c in containers
|
||||
if (c.get('asset_name') != container.get('asset_name') and
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@ import subprocess
|
|||
from distutils import dir_util
|
||||
from pathlib import Path
|
||||
from typing import List, Union
|
||||
import tempfile
|
||||
from distutils.dir_util import copy_tree
|
||||
|
||||
import openpype.hosts.unreal.lib as ue_lib
|
||||
|
||||
|
|
@ -90,9 +92,20 @@ class UEProjectGenerationWorker(QtCore.QObject):
|
|||
("Generating a new UE project ... 1 out of "
|
||||
f"{stage_count}"))
|
||||
|
||||
# Need to copy the commandlet project to a temporary folder where
|
||||
# users don't need admin rights to write to.
|
||||
cmdlet_tmp = tempfile.TemporaryDirectory()
|
||||
cmdlet_filename = cmdlet_project.name
|
||||
cmdlet_dir = cmdlet_project.parent.as_posix()
|
||||
cmdlet_tmp_name = Path(cmdlet_tmp.name)
|
||||
cmdlet_tmp_file = cmdlet_tmp_name.joinpath(cmdlet_filename)
|
||||
copy_tree(
|
||||
cmdlet_dir,
|
||||
cmdlet_tmp_name.as_posix())
|
||||
|
||||
commandlet_cmd = [
|
||||
f"{ue_editor_exe.as_posix()}",
|
||||
f"{cmdlet_project.as_posix()}",
|
||||
f"{cmdlet_tmp_file.as_posix()}",
|
||||
"-run=AyonGenerateProject",
|
||||
f"{project_file.resolve().as_posix()}",
|
||||
]
|
||||
|
|
@ -111,6 +124,8 @@ class UEProjectGenerationWorker(QtCore.QObject):
|
|||
gen_process.stdout.close()
|
||||
return_code = gen_process.wait()
|
||||
|
||||
cmdlet_tmp.cleanup()
|
||||
|
||||
if return_code and return_code != 0:
|
||||
msg = (
|
||||
f"Failed to generate {self.project_name} "
|
||||
|
|
|
|||
|
|
@ -275,7 +275,7 @@ class ProcessSubmittedJobOnFarm(pyblish.api.InstancePlugin):
|
|||
args = [
|
||||
"--headless",
|
||||
'publish',
|
||||
rootless_metadata_path,
|
||||
'"{}"'.format(rootless_metadata_path),
|
||||
"--targets", "deadline",
|
||||
"--targets", "farm"
|
||||
]
|
||||
|
|
|
|||
|
|
@ -234,6 +234,10 @@ class BaseAction(BaseHandler):
|
|||
if not settings_roles:
|
||||
return default
|
||||
|
||||
user_roles = {
|
||||
role_name.lower()
|
||||
for role_name in user_roles
|
||||
}
|
||||
for role_name in settings_roles:
|
||||
if role_name.lower() in user_roles:
|
||||
return True
|
||||
|
|
@ -264,8 +268,15 @@ class BaseAction(BaseHandler):
|
|||
return user_entity
|
||||
|
||||
@classmethod
|
||||
def get_user_roles_from_event(cls, session, event):
|
||||
"""Query user entity from event."""
|
||||
def get_user_roles_from_event(cls, session, event, lower=True):
|
||||
"""Get user roles based on data in event.
|
||||
|
||||
Args:
|
||||
session (ftrack_api.Session): Prepared ftrack session.
|
||||
event (ftrack_api.event.Event): Event which is processed.
|
||||
lower (Optional[bool]): Lower the role names. Default 'True'.
|
||||
"""
|
||||
|
||||
not_set = object()
|
||||
|
||||
user_roles = event["data"].get("user_roles", not_set)
|
||||
|
|
@ -273,7 +284,10 @@ class BaseAction(BaseHandler):
|
|||
user_roles = []
|
||||
user_entity = cls.get_user_entity_from_event(session, event)
|
||||
for role in user_entity["user_security_roles"]:
|
||||
user_roles.append(role["security_role"]["name"].lower())
|
||||
role_name = role["security_role"]["name"]
|
||||
if lower:
|
||||
role_name = role_name.lower()
|
||||
user_roles.append(role_name)
|
||||
event["data"]["user_roles"] = user_roles
|
||||
return user_roles
|
||||
|
||||
|
|
@ -322,7 +336,8 @@ class BaseAction(BaseHandler):
|
|||
if not settings.get(self.settings_enabled_key, True):
|
||||
return False
|
||||
|
||||
user_role_list = self.get_user_roles_from_event(session, event)
|
||||
user_role_list = self.get_user_roles_from_event(
|
||||
session, event, lower=False)
|
||||
if not self.roles_check(settings.get("role_list"), user_role_list):
|
||||
return False
|
||||
return True
|
||||
|
|
|
|||
|
|
@ -296,9 +296,9 @@ def server_activity_validate_user(event):
|
|||
if not user_ent:
|
||||
return False
|
||||
|
||||
role_list = ["Pypeclub", "Administrator"]
|
||||
role_list = {"pypeclub", "administrator"}
|
||||
for role in user_ent["user_security_roles"]:
|
||||
if role["security_role"]["name"] in role_list:
|
||||
if role["security_role"]["name"].lower() in role_list:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ class KitsuModule(OpenPypeModule, IPluginPaths, ITrayAction):
|
|||
|
||||
return {
|
||||
"publish": [os.path.join(current_dir, "plugins", "publish")],
|
||||
"actions": [os.path.join(current_dir, "actions")]
|
||||
"actions": [os.path.join(current_dir, "actions")],
|
||||
}
|
||||
|
||||
def cli(self, click_group):
|
||||
|
|
@ -128,15 +128,35 @@ def push_to_zou(login, password):
|
|||
@click.option(
|
||||
"-p", "--password", envvar="KITSU_PWD", help="Password for kitsu username"
|
||||
)
|
||||
def sync_service(login, password):
|
||||
@click.option(
|
||||
"-prj",
|
||||
"--project",
|
||||
"projects",
|
||||
multiple=True,
|
||||
default=[],
|
||||
help="Sync specific kitsu projects",
|
||||
)
|
||||
@click.option(
|
||||
"-lo",
|
||||
"--listen-only",
|
||||
"listen_only",
|
||||
is_flag=True,
|
||||
default=False,
|
||||
help="Listen to events only without any syncing",
|
||||
)
|
||||
def sync_service(login, password, projects, listen_only):
|
||||
"""Synchronize openpype database from Zou sever database.
|
||||
|
||||
Args:
|
||||
login (str): Kitsu user login
|
||||
password (str): Kitsu user password
|
||||
projects (tuple): specific kitsu projects
|
||||
listen_only (bool): run listen only without any syncing
|
||||
"""
|
||||
from .utils.update_op_with_zou import sync_all_projects
|
||||
from .utils.sync_service import start_listeners
|
||||
|
||||
sync_all_projects(login, password)
|
||||
if not listen_only:
|
||||
sync_all_projects(login, password, filter_projects=projects)
|
||||
|
||||
start_listeners(login, password)
|
||||
|
|
|
|||
|
|
@ -94,9 +94,7 @@ def update_op_assets(
|
|||
if not item_doc: # Create asset
|
||||
op_asset = create_op_asset(item)
|
||||
insert_result = dbcon.insert_one(op_asset)
|
||||
item_doc = get_asset_by_id(
|
||||
project_name, insert_result.inserted_id
|
||||
)
|
||||
item_doc = get_asset_by_id(project_name, insert_result.inserted_id)
|
||||
|
||||
# Update asset
|
||||
item_data = deepcopy(item_doc["data"])
|
||||
|
|
@ -329,7 +327,7 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne:
|
|||
"code": project_code,
|
||||
"fps": float(project["fps"]),
|
||||
"zou_id": project["id"],
|
||||
"active": project['project_status_name'] != "Closed",
|
||||
"active": project["project_status_name"] != "Closed",
|
||||
}
|
||||
)
|
||||
|
||||
|
|
@ -359,7 +357,10 @@ def write_project_to_op(project: dict, dbcon: AvalonMongoDB) -> UpdateOne:
|
|||
|
||||
|
||||
def sync_all_projects(
|
||||
login: str, password: str, ignore_projects: list = None
|
||||
login: str,
|
||||
password: str,
|
||||
ignore_projects: list = None,
|
||||
filter_projects: tuple = None,
|
||||
):
|
||||
"""Update all OP projects in DB with Zou data.
|
||||
|
||||
|
|
@ -367,6 +368,7 @@ def sync_all_projects(
|
|||
login (str): Kitsu user login
|
||||
password (str): Kitsu user password
|
||||
ignore_projects (list): List of unsynced project names
|
||||
filter_projects (tuple): Tuple of filter project names to sync with
|
||||
Raises:
|
||||
gazu.exception.AuthFailedException: Wrong user login and/or password
|
||||
"""
|
||||
|
|
@ -381,7 +383,24 @@ def sync_all_projects(
|
|||
dbcon = AvalonMongoDB()
|
||||
dbcon.install()
|
||||
all_projects = gazu.project.all_projects()
|
||||
for project in all_projects:
|
||||
|
||||
project_to_sync = []
|
||||
|
||||
if filter_projects:
|
||||
all_kitsu_projects = {p["name"]: p for p in all_projects}
|
||||
for proj_name in filter_projects:
|
||||
if proj_name in all_kitsu_projects:
|
||||
project_to_sync.append(all_kitsu_projects[proj_name])
|
||||
else:
|
||||
log.info(
|
||||
f"`{proj_name}` project does not exist in Kitsu."
|
||||
f" Please make sure the project is spelled correctly."
|
||||
)
|
||||
else:
|
||||
# all project
|
||||
project_to_sync = all_projects
|
||||
|
||||
for project in project_to_sync:
|
||||
if ignore_projects and project["name"] in ignore_projects:
|
||||
continue
|
||||
sync_project_from_kitsu(dbcon, project)
|
||||
|
|
@ -408,14 +427,13 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict):
|
|||
# Get all statuses for projects from Kitsu
|
||||
all_status = gazu.project.all_project_status()
|
||||
for status in all_status:
|
||||
if project['project_status_id'] == status['id']:
|
||||
project['project_status_name'] = status['name']
|
||||
if project["project_status_id"] == status["id"]:
|
||||
project["project_status_name"] = status["name"]
|
||||
break
|
||||
|
||||
# Do not sync closed kitsu project that is not found in openpype
|
||||
if (
|
||||
project['project_status_name'] == "Closed"
|
||||
and not get_project(project['name'])
|
||||
if project["project_status_name"] == "Closed" and not get_project(
|
||||
project["name"]
|
||||
):
|
||||
return
|
||||
|
||||
|
|
@ -444,7 +462,7 @@ def sync_project_from_kitsu(dbcon: AvalonMongoDB, project: dict):
|
|||
log.info("Project created: {}".format(project_name))
|
||||
bulk_writes.append(write_project_to_op(project, dbcon))
|
||||
|
||||
if project['project_status_name'] == "Closed":
|
||||
if project["project_status_name"] == "Closed":
|
||||
return
|
||||
|
||||
# Try to find project document
|
||||
|
|
|
|||
|
|
@ -26,11 +26,13 @@ class CollectFramesFixDef(
|
|||
targets = ["local"]
|
||||
hosts = ["nuke"]
|
||||
families = ["render", "prerender"]
|
||||
enabled = True
|
||||
|
||||
rewrite_version_enable = False
|
||||
|
||||
def process(self, instance):
|
||||
attribute_values = self.get_attr_values_from_data(instance.data)
|
||||
frames_to_fix = attribute_values.get("frames_to_fix")
|
||||
|
||||
rewrite_version = attribute_values.get("rewrite_version")
|
||||
|
||||
if frames_to_fix:
|
||||
|
|
@ -71,10 +73,19 @@ class CollectFramesFixDef(
|
|||
|
||||
@classmethod
|
||||
def get_attribute_defs(cls):
|
||||
return [
|
||||
attributes = [
|
||||
TextDef("frames_to_fix", label="Frames to fix",
|
||||
placeholder="5,10-15",
|
||||
regex="[0-9,-]+"),
|
||||
BoolDef("rewrite_version", label="Rewrite latest version",
|
||||
default=False),
|
||||
regex="[0-9,-]+")
|
||||
]
|
||||
|
||||
if cls.rewrite_version_enable:
|
||||
attributes.append(
|
||||
BoolDef(
|
||||
"rewrite_version",
|
||||
label="Rewrite latest version",
|
||||
default=False
|
||||
)
|
||||
)
|
||||
|
||||
return attributes
|
||||
|
|
|
|||
|
|
@ -1,4 +1,9 @@
|
|||
{
|
||||
"unit_scale_settings": {
|
||||
"enabled": true,
|
||||
"apply_on_opening": false,
|
||||
"base_file_unit_scale": 0.01
|
||||
},
|
||||
"imageio": {
|
||||
"ocio_config": {
|
||||
"enabled": false,
|
||||
|
|
|
|||
|
|
@ -46,6 +46,10 @@
|
|||
"enabled": false,
|
||||
"families": []
|
||||
},
|
||||
"CollectFramesFixDef": {
|
||||
"enabled": true,
|
||||
"rewrite_version_enable": true
|
||||
},
|
||||
"ValidateEditorialAssetName": {
|
||||
"enabled": true,
|
||||
"optional": false
|
||||
|
|
@ -252,7 +256,9 @@
|
|||
}
|
||||
},
|
||||
{
|
||||
"families": ["review"],
|
||||
"families": [
|
||||
"review"
|
||||
],
|
||||
"hosts": [
|
||||
"maya",
|
||||
"houdini"
|
||||
|
|
|
|||
|
|
@ -15,6 +15,6 @@
|
|||
"preroll_frames": 0,
|
||||
"render_format": "png",
|
||||
"project_setup": {
|
||||
"dev_mode": true
|
||||
"dev_mode": false
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,32 @@
|
|||
"label": "Blender",
|
||||
"is_file": true,
|
||||
"children": [
|
||||
{
|
||||
"key": "unit_scale_settings",
|
||||
"type": "dict",
|
||||
"label": "Set Unit Scale",
|
||||
"collapsible": true,
|
||||
"is_group": true,
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"key": "apply_on_opening",
|
||||
"type": "boolean",
|
||||
"label": "Apply on Opening Existing Files"
|
||||
},
|
||||
{
|
||||
"key": "base_file_unit_scale",
|
||||
"type": "number",
|
||||
"label": "Base File Unit Scale",
|
||||
"decimal": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "imageio",
|
||||
"type": "dict",
|
||||
|
|
|
|||
|
|
@ -81,6 +81,26 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
"checkbox_key": "enabled",
|
||||
"key": "CollectFramesFixDef",
|
||||
"label": "Collect Frames to Fix",
|
||||
"is_group": true,
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled"
|
||||
},
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "rewrite_version_enable",
|
||||
"label": "Show 'Rewrite latest version' toggle"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "dict",
|
||||
"collapsible": true,
|
||||
|
|
|
|||
|
|
@ -14,32 +14,44 @@ class _VLineWidget(QtWidgets.QWidget):
|
|||
|
||||
It is expected that parent widget will set width.
|
||||
"""
|
||||
def __init__(self, color, left, parent):
|
||||
def __init__(self, color, line_size, left, parent):
|
||||
super(_VLineWidget, self).__init__(parent)
|
||||
self._color = color
|
||||
self._left = left
|
||||
self._line_size = line_size
|
||||
|
||||
def set_line_size(self, line_size):
|
||||
self._line_size = line_size
|
||||
|
||||
def paintEvent(self, event):
|
||||
if not self.isVisible():
|
||||
return
|
||||
|
||||
if self._left:
|
||||
pos_x = 0
|
||||
else:
|
||||
pos_x = self.width()
|
||||
pos_x = self._line_size * 0.5
|
||||
if not self._left:
|
||||
pos_x = self.width() - pos_x
|
||||
|
||||
painter = QtGui.QPainter(self)
|
||||
painter.setRenderHints(
|
||||
QtGui.QPainter.Antialiasing
|
||||
| QtGui.QPainter.SmoothPixmapTransform
|
||||
)
|
||||
|
||||
if self._color:
|
||||
pen = QtGui.QPen(self._color)
|
||||
else:
|
||||
pen = painter.pen()
|
||||
pen.setWidth(1)
|
||||
pen.setWidth(self._line_size)
|
||||
painter.setPen(pen)
|
||||
painter.setBrush(QtCore.Qt.transparent)
|
||||
painter.drawLine(pos_x, 0, pos_x, self.height())
|
||||
painter.drawRect(
|
||||
QtCore.QRectF(
|
||||
pos_x,
|
||||
-self._line_size,
|
||||
pos_x + (self.width() * 2),
|
||||
self.height() + (self._line_size * 2)
|
||||
)
|
||||
)
|
||||
painter.end()
|
||||
|
||||
|
||||
|
|
@ -56,34 +68,46 @@ class _HBottomLineWidget(QtWidgets.QWidget):
|
|||
|
||||
It is expected that parent widget will set height and radius.
|
||||
"""
|
||||
def __init__(self, color, parent):
|
||||
def __init__(self, color, line_size, parent):
|
||||
super(_HBottomLineWidget, self).__init__(parent)
|
||||
self._color = color
|
||||
self._radius = 0
|
||||
self._line_size = line_size
|
||||
|
||||
def set_radius(self, radius):
|
||||
self._radius = radius
|
||||
|
||||
def set_line_size(self, line_size):
|
||||
self._line_size = line_size
|
||||
|
||||
def paintEvent(self, event):
|
||||
if not self.isVisible():
|
||||
return
|
||||
|
||||
rect = QtCore.QRect(
|
||||
0, -self._radius, self.width(), self.height() + self._radius
|
||||
x_offset = self._line_size * 0.5
|
||||
rect = QtCore.QRectF(
|
||||
x_offset,
|
||||
-self._radius,
|
||||
self.width() - (2 * x_offset),
|
||||
(self.height() + self._radius) - x_offset
|
||||
)
|
||||
painter = QtGui.QPainter(self)
|
||||
painter.setRenderHints(
|
||||
QtGui.QPainter.Antialiasing
|
||||
| QtGui.QPainter.SmoothPixmapTransform
|
||||
)
|
||||
|
||||
if self._color:
|
||||
pen = QtGui.QPen(self._color)
|
||||
else:
|
||||
pen = painter.pen()
|
||||
pen.setWidth(1)
|
||||
pen.setWidth(self._line_size)
|
||||
painter.setPen(pen)
|
||||
painter.setBrush(QtCore.Qt.transparent)
|
||||
painter.drawRoundedRect(rect, self._radius, self._radius)
|
||||
if self._radius:
|
||||
painter.drawRoundedRect(rect, self._radius, self._radius)
|
||||
else:
|
||||
painter.drawRect(rect)
|
||||
painter.end()
|
||||
|
||||
|
||||
|
|
@ -102,30 +126,38 @@ class _HTopCornerLineWidget(QtWidgets.QWidget):
|
|||
|
||||
It is expected that parent widget will set height and radius.
|
||||
"""
|
||||
def __init__(self, color, left_side, parent):
|
||||
|
||||
def __init__(self, color, line_size, left_side, parent):
|
||||
super(_HTopCornerLineWidget, self).__init__(parent)
|
||||
self._left_side = left_side
|
||||
self._line_size = line_size
|
||||
self._color = color
|
||||
self._radius = 0
|
||||
|
||||
def set_radius(self, radius):
|
||||
self._radius = radius
|
||||
|
||||
def set_line_size(self, line_size):
|
||||
self._line_size = line_size
|
||||
|
||||
def paintEvent(self, event):
|
||||
if not self.isVisible():
|
||||
return
|
||||
|
||||
pos_y = self.height() / 2
|
||||
|
||||
pos_y = self.height() * 0.5
|
||||
x_offset = self._line_size * 0.5
|
||||
if self._left_side:
|
||||
rect = QtCore.QRect(
|
||||
0, pos_y, self.width() + self._radius, self.height()
|
||||
rect = QtCore.QRectF(
|
||||
x_offset,
|
||||
pos_y,
|
||||
self.width() + self._radius + x_offset,
|
||||
self.height()
|
||||
)
|
||||
else:
|
||||
rect = QtCore.QRect(
|
||||
-self._radius,
|
||||
rect = QtCore.QRectF(
|
||||
(-self._radius),
|
||||
pos_y,
|
||||
self.width() + self._radius,
|
||||
(self.width() + self._radius) - x_offset,
|
||||
self.height()
|
||||
)
|
||||
|
||||
|
|
@ -138,10 +170,13 @@ class _HTopCornerLineWidget(QtWidgets.QWidget):
|
|||
pen = QtGui.QPen(self._color)
|
||||
else:
|
||||
pen = painter.pen()
|
||||
pen.setWidth(1)
|
||||
pen.setWidth(self._line_size)
|
||||
painter.setPen(pen)
|
||||
painter.setBrush(QtCore.Qt.transparent)
|
||||
painter.drawRoundedRect(rect, self._radius, self._radius)
|
||||
if self._radius:
|
||||
painter.drawRoundedRect(rect, self._radius, self._radius)
|
||||
else:
|
||||
painter.drawRect(rect)
|
||||
painter.end()
|
||||
|
||||
|
||||
|
|
@ -163,8 +198,10 @@ class BorderedLabelWidget(QtWidgets.QFrame):
|
|||
if color_value:
|
||||
color = color_value.get_qcolor()
|
||||
|
||||
top_left_w = _HTopCornerLineWidget(color, True, self)
|
||||
top_right_w = _HTopCornerLineWidget(color, False, self)
|
||||
line_size = 1
|
||||
|
||||
top_left_w = _HTopCornerLineWidget(color, line_size, True, self)
|
||||
top_right_w = _HTopCornerLineWidget(color, line_size, False, self)
|
||||
|
||||
label_widget = QtWidgets.QLabel(label, self)
|
||||
|
||||
|
|
@ -175,10 +212,10 @@ class BorderedLabelWidget(QtWidgets.QFrame):
|
|||
top_layout.addWidget(label_widget, 0)
|
||||
top_layout.addWidget(top_right_w, 1)
|
||||
|
||||
left_w = _VLineWidget(color, True, self)
|
||||
right_w = _VLineWidget(color, False, self)
|
||||
left_w = _VLineWidget(color, line_size, True, self)
|
||||
right_w = _VLineWidget(color, line_size, False, self)
|
||||
|
||||
bottom_w = _HBottomLineWidget(color, self)
|
||||
bottom_w = _HBottomLineWidget(color, line_size, self)
|
||||
|
||||
center_layout = QtWidgets.QHBoxLayout()
|
||||
center_layout.setContentsMargins(5, 5, 5, 5)
|
||||
|
|
@ -201,6 +238,7 @@ class BorderedLabelWidget(QtWidgets.QFrame):
|
|||
self._widget = None
|
||||
|
||||
self._radius = 0
|
||||
self._line_size = line_size
|
||||
|
||||
self._top_left_w = top_left_w
|
||||
self._top_right_w = top_right_w
|
||||
|
|
@ -216,14 +254,38 @@ class BorderedLabelWidget(QtWidgets.QFrame):
|
|||
value, value, value, value
|
||||
)
|
||||
|
||||
def set_line_size(self, line_size):
|
||||
if self._line_size == line_size:
|
||||
return
|
||||
self._line_size = line_size
|
||||
for widget in (
|
||||
self._top_left_w,
|
||||
self._top_right_w,
|
||||
self._left_w,
|
||||
self._right_w,
|
||||
self._bottom_w
|
||||
):
|
||||
widget.set_line_size(line_size)
|
||||
self._recalculate_sizes()
|
||||
|
||||
def showEvent(self, event):
|
||||
super(BorderedLabelWidget, self).showEvent(event)
|
||||
self._recalculate_sizes()
|
||||
|
||||
def _recalculate_sizes(self):
|
||||
height = self._label_widget.height()
|
||||
radius = (height + (height % 2)) / 2
|
||||
radius = int((height + (height % 2)) / 2)
|
||||
self._radius = radius
|
||||
|
||||
side_width = 1 + radius
|
||||
radius_size = self._line_size + 1
|
||||
if radius_size < radius:
|
||||
radius_size = radius
|
||||
|
||||
if radius:
|
||||
side_width = self._line_size + radius
|
||||
else:
|
||||
side_width = self._line_size + 1
|
||||
|
||||
# Don't use fixed width/height as that would set also set
|
||||
# the other size (When fixed width is set then is also set
|
||||
# fixed height).
|
||||
|
|
@ -231,8 +293,8 @@ class BorderedLabelWidget(QtWidgets.QFrame):
|
|||
self._left_w.setMaximumWidth(side_width)
|
||||
self._right_w.setMinimumWidth(side_width)
|
||||
self._right_w.setMaximumWidth(side_width)
|
||||
self._bottom_w.setMinimumHeight(radius)
|
||||
self._bottom_w.setMaximumHeight(radius)
|
||||
self._bottom_w.setMinimumHeight(radius_size)
|
||||
self._bottom_w.setMaximumHeight(radius_size)
|
||||
self._bottom_w.set_radius(radius)
|
||||
self._top_right_w.set_radius(radius)
|
||||
self._top_left_w.set_radius(radius)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Package declaring Pype version."""
|
||||
__version__ = "3.15.9-nightly.1"
|
||||
__version__ = "3.15.9"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
[tool.poetry]
|
||||
name = "OpenPype"
|
||||
version = "3.15.8" # OpenPype
|
||||
version = "3.15.9" # OpenPype
|
||||
description = "Open VFX and Animation pipeline with support."
|
||||
authors = ["OpenPype Team <info@openpype.io>"]
|
||||
license = "MIT License"
|
||||
|
|
|
|||
|
|
@ -18,9 +18,20 @@ This setting is available for all the users of the OpenPype instance.
|
|||
## Synchronize
|
||||
Updating OP with Kitsu data is executed running the `sync-service`, which requires to provide your Kitsu credentials with `-l, --login` and `-p, --password` or by setting the environment variables `KITSU_LOGIN` and `KITSU_PWD`. This process will request data from Kitsu and create/delete/update OP assets.
|
||||
Once this sync is done, the thread will automatically start a loop to listen to Kitsu events.
|
||||
- `-prj, --project` This flag accepts multiple project name to sync specific projects, and the default to sync all projects.
|
||||
- `-lo, --listen-only` This flag to run listen to Kitsu events only without any sync.
|
||||
|
||||
Note: You must use one argument of `-pro` or `-lo`, because the listen only flag override syncing flag.
|
||||
|
||||
```bash
|
||||
// sync all projects then run listen
|
||||
openpype_console module kitsu sync-service -l me@domain.ext -p my_password
|
||||
|
||||
// sync specific projects then run listen
|
||||
openpype_console module kitsu sync-service -l me@domain.ext -p my_password -prj project_name01 -prj project_name02
|
||||
|
||||
// start listen only for all projects
|
||||
openpype_console module kitsu sync-service -l me@domain.ext -p my_password -lo
|
||||
```
|
||||
|
||||
### Events listening
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ Example here describes use case for creation of new color coded review of png im
|
|||

|
||||
|
||||
Another use case is to transcode in Maya only `beauty` render layers and use collected `Display` and `View` colorspaces from DCC.
|
||||
n
|
||||

|
||||
|
||||
## Profile filters
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue