mirror of
https://github.com/ynput/ayon-core.git
synced 2025-12-24 21:04:40 +01:00
Merge branch 'develop' into feature/1235-hiero-unify-otio-workflow-from-resolve
This commit is contained in:
commit
36e76837c5
13 changed files with 1228 additions and 504 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import re
|
||||
import subprocess
|
||||
from openpype.lib import PreLaunchHook
|
||||
|
||||
|
|
@ -31,10 +32,46 @@ class InstallPySideToBlender(PreLaunchHook):
|
|||
|
||||
def inner_execute(self):
|
||||
# Get blender's python directory
|
||||
version_regex = re.compile(r"^2\.[0-9]{2}$")
|
||||
|
||||
executable = self.launch_context.executable.executable_path
|
||||
# Blender installation contain subfolder named with it's version where
|
||||
# python binaries are stored.
|
||||
version_subfolder = self.launch_context.app_name.split("_")[1]
|
||||
if os.path.basename(executable).lower() != "blender.exe":
|
||||
self.log.info((
|
||||
"Executable does not lead to blender.exe file. Can't determine"
|
||||
" blender's python to check/install PySide2."
|
||||
))
|
||||
return
|
||||
|
||||
executable_dir = os.path.dirname(executable)
|
||||
version_subfolders = []
|
||||
for name in os.listdir(executable_dir):
|
||||
fullpath = os.path.join(name, executable_dir)
|
||||
if not os.path.isdir(fullpath):
|
||||
continue
|
||||
|
||||
if not version_regex.match(name):
|
||||
continue
|
||||
|
||||
version_subfolders.append(name)
|
||||
|
||||
if not version_subfolders:
|
||||
self.log.info(
|
||||
"Didn't find version subfolder next to Blender executable"
|
||||
)
|
||||
return
|
||||
|
||||
if len(version_subfolders) > 1:
|
||||
self.log.info((
|
||||
"Found more than one version subfolder next"
|
||||
" to blender executable. {}"
|
||||
).format(", ".join([
|
||||
'"./{}"'.format(name)
|
||||
for name in version_subfolders
|
||||
])))
|
||||
return
|
||||
|
||||
version_subfolder = version_subfolders[0]
|
||||
|
||||
pythond_dir = os.path.join(
|
||||
os.path.dirname(executable),
|
||||
version_subfolder,
|
||||
|
|
@ -65,6 +102,7 @@ class InstallPySideToBlender(PreLaunchHook):
|
|||
|
||||
# Check if PySide2 is installed and skip if yes
|
||||
if self.is_pyside_installed(python_executable):
|
||||
self.log.debug("Blender has already installed PySide2.")
|
||||
return
|
||||
|
||||
# Install PySide2 in blender's python
|
||||
|
|
|
|||
162
openpype/hosts/unreal/plugins/load/load_alembic_geometrycache.py
Normal file
162
openpype/hosts/unreal/plugins/load/load_alembic_geometrycache.py
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
import os
|
||||
|
||||
from avalon import api, pipeline
|
||||
from avalon.unreal import lib
|
||||
from avalon.unreal import pipeline as unreal_pipeline
|
||||
import unreal
|
||||
|
||||
|
||||
class PointCacheAlembicLoader(api.Loader):
|
||||
"""Load Point Cache from Alembic"""
|
||||
|
||||
families = ["model", "pointcache"]
|
||||
label = "Import Alembic Point Cache"
|
||||
representations = ["abc"]
|
||||
icon = "cube"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Load and containerise representation into Content Browser.
|
||||
|
||||
This is two step process. First, import FBX to temporary path and
|
||||
then call `containerise()` on it - this moves all content to new
|
||||
directory and then it will create AssetContainer there and imprint it
|
||||
with metadata. This will mark this path as container.
|
||||
|
||||
Args:
|
||||
context (dict): application context
|
||||
name (str): subset name
|
||||
namespace (str): in Unreal this is basically path to container.
|
||||
This is not passed here, so namespace is set
|
||||
by `containerise()` because only then we know
|
||||
real path.
|
||||
data (dict): Those would be data to be imprinted. This is not used
|
||||
now, data are imprinted by `containerise()`.
|
||||
|
||||
Returns:
|
||||
list(str): list of container content
|
||||
"""
|
||||
|
||||
# Create directory for asset and avalon container
|
||||
root = "/Game/Avalon/Assets"
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
if asset:
|
||||
asset_name = "{}_{}".format(asset, name)
|
||||
else:
|
||||
asset_name = "{}".format(name)
|
||||
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
"{}/{}/{}".format(root, asset, name), suffix="")
|
||||
|
||||
container_name += suffix
|
||||
|
||||
unreal.EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
task = unreal.AssetImportTask()
|
||||
|
||||
task.set_editor_property('filename', self.fname)
|
||||
task.set_editor_property('destination_path', asset_dir)
|
||||
task.set_editor_property('destination_name', asset_name)
|
||||
task.set_editor_property('replace_existing', False)
|
||||
task.set_editor_property('automated', True)
|
||||
task.set_editor_property('save', True)
|
||||
|
||||
# set import options here
|
||||
# Unreal 4.24 ignores the settings. It works with Unreal 4.26
|
||||
options = unreal.AbcImportSettings()
|
||||
options.set_editor_property(
|
||||
'import_type', unreal.AlembicImportType.GEOMETRY_CACHE)
|
||||
|
||||
options.geometry_cache_settings.set_editor_property(
|
||||
'flatten_tracks', False)
|
||||
|
||||
task.options = options
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501
|
||||
|
||||
# Create Asset Container
|
||||
lib.create_avalon_container(
|
||||
container=container_name, path=asset_dir)
|
||||
|
||||
data = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": pipeline.AVALON_CONTAINER_ID,
|
||||
"asset": asset,
|
||||
"namespace": asset_dir,
|
||||
"container_name": container_name,
|
||||
"asset_name": asset_name,
|
||||
"loader": str(self.__class__.__name__),
|
||||
"representation": context["representation"]["_id"],
|
||||
"parent": context["representation"]["parent"],
|
||||
"family": context["representation"]["context"]["family"]
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
"{}/{}".format(asset_dir, container_name), data)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=True
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
unreal.EditorAssetLibrary.save_asset(a)
|
||||
|
||||
return asset_content
|
||||
|
||||
def update(self, container, representation):
|
||||
name = container["asset_name"]
|
||||
source_path = api.get_representation_path(representation)
|
||||
destination_path = container["namespace"]
|
||||
|
||||
task = unreal.AssetImportTask()
|
||||
|
||||
task.set_editor_property('filename', source_path)
|
||||
task.set_editor_property('destination_path', destination_path)
|
||||
# strip suffix
|
||||
task.set_editor_property('destination_name', name)
|
||||
task.set_editor_property('replace_existing', True)
|
||||
task.set_editor_property('automated', True)
|
||||
task.set_editor_property('save', True)
|
||||
|
||||
# set import options here
|
||||
# Unreal 4.24 ignores the settings. It works with Unreal 4.26
|
||||
options = unreal.AbcImportSettings()
|
||||
options.set_editor_property(
|
||||
'import_type', unreal.AlembicImportType.GEOMETRY_CACHE)
|
||||
|
||||
options.geometry_cache_settings.set_editor_property(
|
||||
'flatten_tracks', False)
|
||||
|
||||
task.options = options
|
||||
# do import fbx and replace existing data
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
|
||||
container_path = "{}/{}".format(container["namespace"],
|
||||
container["objectName"])
|
||||
# update metadata
|
||||
unreal_pipeline.imprint(
|
||||
container_path,
|
||||
{
|
||||
"representation": str(representation["_id"]),
|
||||
"parent": str(representation["parent"])
|
||||
})
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
destination_path, recursive=True, include_folder=True
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
unreal.EditorAssetLibrary.save_asset(a)
|
||||
|
||||
def remove(self, container):
|
||||
path = container["namespace"]
|
||||
parent_path = os.path.dirname(path)
|
||||
|
||||
unreal.EditorAssetLibrary.delete_directory(path)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
parent_path, recursive=False
|
||||
)
|
||||
|
||||
if len(asset_content) == 0:
|
||||
unreal.EditorAssetLibrary.delete_directory(parent_path)
|
||||
156
openpype/hosts/unreal/plugins/load/load_alembic_skeletalmesh.py
Normal file
156
openpype/hosts/unreal/plugins/load/load_alembic_skeletalmesh.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import os
|
||||
|
||||
from avalon import api, pipeline
|
||||
from avalon.unreal import lib
|
||||
from avalon.unreal import pipeline as unreal_pipeline
|
||||
import unreal
|
||||
|
||||
|
||||
class SkeletalMeshAlembicLoader(api.Loader):
|
||||
"""Load Unreal SkeletalMesh from Alembic"""
|
||||
|
||||
families = ["pointcache"]
|
||||
label = "Import Alembic Skeletal Mesh"
|
||||
representations = ["abc"]
|
||||
icon = "cube"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Load and containerise representation into Content Browser.
|
||||
|
||||
This is two step process. First, import FBX to temporary path and
|
||||
then call `containerise()` on it - this moves all content to new
|
||||
directory and then it will create AssetContainer there and imprint it
|
||||
with metadata. This will mark this path as container.
|
||||
|
||||
Args:
|
||||
context (dict): application context
|
||||
name (str): subset name
|
||||
namespace (str): in Unreal this is basically path to container.
|
||||
This is not passed here, so namespace is set
|
||||
by `containerise()` because only then we know
|
||||
real path.
|
||||
data (dict): Those would be data to be imprinted. This is not used
|
||||
now, data are imprinted by `containerise()`.
|
||||
|
||||
Returns:
|
||||
list(str): list of container content
|
||||
"""
|
||||
|
||||
# Create directory for asset and avalon container
|
||||
root = "/Game/Avalon/Assets"
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
if asset:
|
||||
asset_name = "{}_{}".format(asset, name)
|
||||
else:
|
||||
asset_name = "{}".format(name)
|
||||
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
"{}/{}/{}".format(root, asset, name), suffix="")
|
||||
|
||||
container_name += suffix
|
||||
|
||||
unreal.EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
task = unreal.AssetImportTask()
|
||||
|
||||
task.set_editor_property('filename', self.fname)
|
||||
task.set_editor_property('destination_path', asset_dir)
|
||||
task.set_editor_property('destination_name', asset_name)
|
||||
task.set_editor_property('replace_existing', False)
|
||||
task.set_editor_property('automated', True)
|
||||
task.set_editor_property('save', True)
|
||||
|
||||
# set import options here
|
||||
# Unreal 4.24 ignores the settings. It works with Unreal 4.26
|
||||
options = unreal.AbcImportSettings()
|
||||
options.set_editor_property(
|
||||
'import_type', unreal.AlembicImportType.SKELETAL)
|
||||
|
||||
task.options = options
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501
|
||||
|
||||
# Create Asset Container
|
||||
lib.create_avalon_container(
|
||||
container=container_name, path=asset_dir)
|
||||
|
||||
data = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": pipeline.AVALON_CONTAINER_ID,
|
||||
"asset": asset,
|
||||
"namespace": asset_dir,
|
||||
"container_name": container_name,
|
||||
"asset_name": asset_name,
|
||||
"loader": str(self.__class__.__name__),
|
||||
"representation": context["representation"]["_id"],
|
||||
"parent": context["representation"]["parent"],
|
||||
"family": context["representation"]["context"]["family"]
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
"{}/{}".format(asset_dir, container_name), data)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=True
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
unreal.EditorAssetLibrary.save_asset(a)
|
||||
|
||||
return asset_content
|
||||
|
||||
def update(self, container, representation):
|
||||
name = container["asset_name"]
|
||||
source_path = api.get_representation_path(representation)
|
||||
destination_path = container["namespace"]
|
||||
|
||||
task = unreal.AssetImportTask()
|
||||
|
||||
task.set_editor_property('filename', source_path)
|
||||
task.set_editor_property('destination_path', destination_path)
|
||||
# strip suffix
|
||||
task.set_editor_property('destination_name', name)
|
||||
task.set_editor_property('replace_existing', True)
|
||||
task.set_editor_property('automated', True)
|
||||
task.set_editor_property('save', True)
|
||||
|
||||
# set import options here
|
||||
# Unreal 4.24 ignores the settings. It works with Unreal 4.26
|
||||
options = unreal.AbcImportSettings()
|
||||
options.set_editor_property(
|
||||
'import_type', unreal.AlembicImportType.SKELETAL)
|
||||
|
||||
task.options = options
|
||||
# do import fbx and replace existing data
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
|
||||
container_path = "{}/{}".format(container["namespace"],
|
||||
container["objectName"])
|
||||
# update metadata
|
||||
unreal_pipeline.imprint(
|
||||
container_path,
|
||||
{
|
||||
"representation": str(representation["_id"]),
|
||||
"parent": str(representation["parent"])
|
||||
})
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
destination_path, recursive=True, include_folder=True
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
unreal.EditorAssetLibrary.save_asset(a)
|
||||
|
||||
def remove(self, container):
|
||||
path = container["namespace"]
|
||||
parent_path = os.path.dirname(path)
|
||||
|
||||
unreal.EditorAssetLibrary.delete_directory(path)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
parent_path, recursive=False
|
||||
)
|
||||
|
||||
if len(asset_content) == 0:
|
||||
unreal.EditorAssetLibrary.delete_directory(parent_path)
|
||||
156
openpype/hosts/unreal/plugins/load/load_alembic_staticmesh.py
Normal file
156
openpype/hosts/unreal/plugins/load/load_alembic_staticmesh.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
import os
|
||||
|
||||
from avalon import api, pipeline
|
||||
from avalon.unreal import lib
|
||||
from avalon.unreal import pipeline as unreal_pipeline
|
||||
import unreal
|
||||
|
||||
|
||||
class StaticMeshAlembicLoader(api.Loader):
|
||||
"""Load Unreal StaticMesh from Alembic"""
|
||||
|
||||
families = ["model"]
|
||||
label = "Import Alembic Static Mesh"
|
||||
representations = ["abc"]
|
||||
icon = "cube"
|
||||
color = "orange"
|
||||
|
||||
def load(self, context, name, namespace, data):
|
||||
"""
|
||||
Load and containerise representation into Content Browser.
|
||||
|
||||
This is two step process. First, import FBX to temporary path and
|
||||
then call `containerise()` on it - this moves all content to new
|
||||
directory and then it will create AssetContainer there and imprint it
|
||||
with metadata. This will mark this path as container.
|
||||
|
||||
Args:
|
||||
context (dict): application context
|
||||
name (str): subset name
|
||||
namespace (str): in Unreal this is basically path to container.
|
||||
This is not passed here, so namespace is set
|
||||
by `containerise()` because only then we know
|
||||
real path.
|
||||
data (dict): Those would be data to be imprinted. This is not used
|
||||
now, data are imprinted by `containerise()`.
|
||||
|
||||
Returns:
|
||||
list(str): list of container content
|
||||
"""
|
||||
|
||||
# Create directory for asset and avalon container
|
||||
root = "/Game/Avalon/Assets"
|
||||
asset = context.get('asset').get('name')
|
||||
suffix = "_CON"
|
||||
if asset:
|
||||
asset_name = "{}_{}".format(asset, name)
|
||||
else:
|
||||
asset_name = "{}".format(name)
|
||||
|
||||
tools = unreal.AssetToolsHelpers().get_asset_tools()
|
||||
asset_dir, container_name = tools.create_unique_asset_name(
|
||||
"{}/{}/{}".format(root, asset, name), suffix="")
|
||||
|
||||
container_name += suffix
|
||||
|
||||
unreal.EditorAssetLibrary.make_directory(asset_dir)
|
||||
|
||||
task = unreal.AssetImportTask()
|
||||
|
||||
task.set_editor_property('filename', self.fname)
|
||||
task.set_editor_property('destination_path', asset_dir)
|
||||
task.set_editor_property('destination_name', asset_name)
|
||||
task.set_editor_property('replace_existing', False)
|
||||
task.set_editor_property('automated', True)
|
||||
task.set_editor_property('save', True)
|
||||
|
||||
# set import options here
|
||||
# Unreal 4.24 ignores the settings. It works with Unreal 4.26
|
||||
options = unreal.AbcImportSettings()
|
||||
options.set_editor_property(
|
||||
'import_type', unreal.AlembicImportType.STATIC_MESH)
|
||||
|
||||
task.options = options
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501
|
||||
|
||||
# Create Asset Container
|
||||
lib.create_avalon_container(
|
||||
container=container_name, path=asset_dir)
|
||||
|
||||
data = {
|
||||
"schema": "openpype:container-2.0",
|
||||
"id": pipeline.AVALON_CONTAINER_ID,
|
||||
"asset": asset,
|
||||
"namespace": asset_dir,
|
||||
"container_name": container_name,
|
||||
"asset_name": asset_name,
|
||||
"loader": str(self.__class__.__name__),
|
||||
"representation": context["representation"]["_id"],
|
||||
"parent": context["representation"]["parent"],
|
||||
"family": context["representation"]["context"]["family"]
|
||||
}
|
||||
unreal_pipeline.imprint(
|
||||
"{}/{}".format(asset_dir, container_name), data)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
asset_dir, recursive=True, include_folder=True
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
unreal.EditorAssetLibrary.save_asset(a)
|
||||
|
||||
return asset_content
|
||||
|
||||
def update(self, container, representation):
|
||||
name = container["asset_name"]
|
||||
source_path = api.get_representation_path(representation)
|
||||
destination_path = container["namespace"]
|
||||
|
||||
task = unreal.AssetImportTask()
|
||||
|
||||
task.set_editor_property('filename', source_path)
|
||||
task.set_editor_property('destination_path', destination_path)
|
||||
# strip suffix
|
||||
task.set_editor_property('destination_name', name)
|
||||
task.set_editor_property('replace_existing', True)
|
||||
task.set_editor_property('automated', True)
|
||||
task.set_editor_property('save', True)
|
||||
|
||||
# set import options here
|
||||
# Unreal 4.24 ignores the settings. It works with Unreal 4.26
|
||||
options = unreal.AbcImportSettings()
|
||||
options.set_editor_property(
|
||||
'import_type', unreal.AlembicImportType.STATIC_MESH)
|
||||
|
||||
task.options = options
|
||||
# do import fbx and replace existing data
|
||||
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task])
|
||||
container_path = "{}/{}".format(container["namespace"],
|
||||
container["objectName"])
|
||||
# update metadata
|
||||
unreal_pipeline.imprint(
|
||||
container_path,
|
||||
{
|
||||
"representation": str(representation["_id"]),
|
||||
"parent": str(representation["parent"])
|
||||
})
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
destination_path, recursive=True, include_folder=True
|
||||
)
|
||||
|
||||
for a in asset_content:
|
||||
unreal.EditorAssetLibrary.save_asset(a)
|
||||
|
||||
def remove(self, container):
|
||||
path = container["namespace"]
|
||||
parent_path = os.path.dirname(path)
|
||||
|
||||
unreal.EditorAssetLibrary.delete_directory(path)
|
||||
|
||||
asset_content = unreal.EditorAssetLibrary.list_assets(
|
||||
parent_path, recursive=False
|
||||
)
|
||||
|
||||
if len(asset_content) == 0:
|
||||
unreal.EditorAssetLibrary.delete_directory(parent_path)
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
import os
|
||||
|
||||
from avalon import api, pipeline
|
||||
from avalon import unreal as avalon_unreal
|
||||
from avalon.unreal import lib
|
||||
from avalon.unreal import pipeline as unreal_pipeline
|
||||
import unreal
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from openpype import resources
|
|||
|
||||
from openpype.modules.sync_server.tray.widgets import (
|
||||
SyncProjectListWidget,
|
||||
SyncRepresentationWidget
|
||||
SyncRepresentationSummaryWidget
|
||||
)
|
||||
|
||||
log = PypeLogger().get_logger("SyncServer")
|
||||
|
|
@ -47,7 +47,7 @@ class SyncServerWindow(QtWidgets.QDialog):
|
|||
left_column_layout.addWidget(self.pause_btn)
|
||||
left_column.setLayout(left_column_layout)
|
||||
|
||||
repres = SyncRepresentationWidget(
|
||||
repres = SyncRepresentationSummaryWidget(
|
||||
sync_server,
|
||||
project=self.projects.current_project,
|
||||
parent=self)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ ProgressRole = QtCore.Qt.UserRole + 4
|
|||
DateRole = QtCore.Qt.UserRole + 6
|
||||
FailedRole = QtCore.Qt.UserRole + 8
|
||||
HeaderNameRole = QtCore.Qt.UserRole + 10
|
||||
FullItemRole = QtCore.Qt.UserRole + 12
|
||||
|
||||
|
||||
@six.add_metaclass(abc.ABCMeta)
|
||||
|
|
@ -128,6 +129,7 @@ class FilterDefinition:
|
|||
type = attr.ib()
|
||||
values = attr.ib(factory=list)
|
||||
|
||||
|
||||
def pretty_size(value, suffix='B'):
|
||||
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
|
||||
if abs(value) < 1024.0:
|
||||
|
|
@ -156,3 +158,9 @@ def translate_provider_for_icon(sync_server, project, site):
|
|||
if site == sync_server.DEFAULT_SITE:
|
||||
return sync_server.DEFAULT_SITE
|
||||
return sync_server.get_provider_for_site(project, site)
|
||||
|
||||
|
||||
def get_item_by_id(model, object_id):
|
||||
index = model.get_index(object_id)
|
||||
item = model.data(index, FullItemRole)
|
||||
return item
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ class _SyncRepresentationModel(QtCore.QAbstractTableModel):
|
|||
self.query = self.get_query(load_records)
|
||||
representations = self.dbcon.aggregate(self.query)
|
||||
|
||||
self.add_page_records(self.local_site, self.remote_site,
|
||||
self.add_page_records(self.active_site, self.remote_site,
|
||||
representations)
|
||||
self.endResetModel()
|
||||
self.refresh_finished.emit()
|
||||
|
|
@ -158,7 +158,7 @@ class _SyncRepresentationModel(QtCore.QAbstractTableModel):
|
|||
self._rec_loaded,
|
||||
self._rec_loaded + items_to_fetch - 1)
|
||||
|
||||
self.add_page_records(self.local_site, self.remote_site,
|
||||
self.add_page_records(self.active_site, self.remote_site,
|
||||
representations)
|
||||
|
||||
self.endInsertRows()
|
||||
|
|
@ -283,7 +283,7 @@ class _SyncRepresentationModel(QtCore.QAbstractTableModel):
|
|||
"""
|
||||
self._project = project
|
||||
self.sync_server.set_sync_project_settings()
|
||||
self.local_site = self.sync_server.get_active_site(self.project)
|
||||
self.active_site = self.sync_server.get_active_site(self.project)
|
||||
self.remote_site = self.sync_server.get_remote_site(self.project)
|
||||
self.refresh()
|
||||
|
||||
|
|
@ -410,7 +410,7 @@ class SyncRepresentationSummaryModel(_SyncRepresentationModel):
|
|||
self.sync_server = sync_server
|
||||
# TODO think about admin mode
|
||||
# this is for regular user, always only single local and single remote
|
||||
self.local_site = self.sync_server.get_active_site(self.project)
|
||||
self.active_site = self.sync_server.get_active_site(self.project)
|
||||
self.remote_site = self.sync_server.get_remote_site(self.project)
|
||||
|
||||
self.sort = self.DEFAULT_SORT
|
||||
|
|
@ -428,6 +428,9 @@ class SyncRepresentationSummaryModel(_SyncRepresentationModel):
|
|||
def data(self, index, role):
|
||||
item = self._data[index.row()]
|
||||
|
||||
if role == lib.FullItemRole:
|
||||
return item
|
||||
|
||||
header_value = self._header[index.column()]
|
||||
if role == lib.ProviderRole:
|
||||
if header_value == 'local_site':
|
||||
|
|
@ -585,7 +588,7 @@ class SyncRepresentationSummaryModel(_SyncRepresentationModel):
|
|||
}},
|
||||
'order_local': {
|
||||
'$filter': {'input': '$files.sites', 'as': 'p',
|
||||
'cond': {'$eq': ['$$p.name', self.local_site]}
|
||||
'cond': {'$eq': ['$$p.name', self.active_site]}
|
||||
}}
|
||||
}},
|
||||
{'$addFields': {
|
||||
|
|
@ -714,7 +717,7 @@ class SyncRepresentationSummaryModel(_SyncRepresentationModel):
|
|||
"""
|
||||
base_match = {
|
||||
"type": "representation",
|
||||
'files.sites.name': {'$all': [self.local_site,
|
||||
'files.sites.name': {'$all': [self.active_site,
|
||||
self.remote_site]}
|
||||
}
|
||||
if not self._word_filter:
|
||||
|
|
@ -889,7 +892,7 @@ class SyncRepresentationDetailModel(_SyncRepresentationModel):
|
|||
self.sync_server = sync_server
|
||||
# TODO think about admin mode
|
||||
# this is for regular user, always only single local and single remote
|
||||
self.local_site = self.sync_server.get_active_site(self.project)
|
||||
self.active_site = self.sync_server.get_active_site(self.project)
|
||||
self.remote_site = self.sync_server.get_remote_site(self.project)
|
||||
|
||||
self.sort = self.DEFAULT_SORT
|
||||
|
|
@ -905,6 +908,9 @@ class SyncRepresentationDetailModel(_SyncRepresentationModel):
|
|||
def data(self, index, role):
|
||||
item = self._data[index.row()]
|
||||
|
||||
if role == lib.FullItemRole:
|
||||
return item
|
||||
|
||||
header_value = self._header[index.column()]
|
||||
if role == lib.ProviderRole:
|
||||
if header_value == 'local_site':
|
||||
|
|
@ -1042,7 +1048,7 @@ class SyncRepresentationDetailModel(_SyncRepresentationModel):
|
|||
}},
|
||||
'order_local': {
|
||||
'$filter': {'input': '$files.sites', 'as': 'p',
|
||||
'cond': {'$eq': ['$$p.name', self.local_site]}
|
||||
'cond': {'$eq': ['$$p.name', self.active_site]}
|
||||
}}
|
||||
}},
|
||||
{'$addFields': {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -704,6 +704,105 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
|
||||
return audio_in_args, audio_filters, audio_out_args
|
||||
|
||||
def get_letterbox_filters(
|
||||
self,
|
||||
letter_box_def,
|
||||
input_res_ratio,
|
||||
output_res_ratio,
|
||||
pixel_aspect,
|
||||
scale_factor_by_width,
|
||||
scale_factor_by_height
|
||||
):
|
||||
output = []
|
||||
|
||||
ratio = letter_box_def["ratio"]
|
||||
state = letter_box_def["state"]
|
||||
fill_color = letter_box_def["fill_color"]
|
||||
f_red, f_green, f_blue, f_alpha = fill_color
|
||||
fill_color_hex = "{0:0>2X}{1:0>2X}{2:0>2X}".format(
|
||||
f_red, f_green, f_blue
|
||||
)
|
||||
fill_color_alpha = float(f_alpha) / 255
|
||||
|
||||
line_thickness = letter_box_def["line_thickness"]
|
||||
line_color = letter_box_def["line_color"]
|
||||
l_red, l_green, l_blue, l_alpha = line_color
|
||||
line_color_hex = "{0:0>2X}{1:0>2X}{2:0>2X}".format(
|
||||
l_red, l_green, l_blue
|
||||
)
|
||||
line_color_alpha = float(l_alpha) / 255
|
||||
|
||||
if input_res_ratio == output_res_ratio:
|
||||
ratio /= pixel_aspect
|
||||
elif input_res_ratio < output_res_ratio:
|
||||
ratio /= scale_factor_by_width
|
||||
else:
|
||||
ratio /= scale_factor_by_height
|
||||
|
||||
if state == "letterbox":
|
||||
if fill_color_alpha > 0:
|
||||
top_box = (
|
||||
"drawbox=0:0:iw:round((ih-(iw*(1/{})))/2):t=fill:c={}@{}"
|
||||
).format(ratio, fill_color_hex, fill_color_alpha)
|
||||
|
||||
bottom_box = (
|
||||
"drawbox=0:ih-round((ih-(iw*(1/{0})))/2)"
|
||||
":iw:round((ih-(iw*(1/{0})))/2):t=fill:c={1}@{2}"
|
||||
).format(ratio, fill_color_hex, fill_color_alpha)
|
||||
|
||||
output.extend([top_box, bottom_box])
|
||||
|
||||
if line_color_alpha > 0 and line_thickness > 0:
|
||||
top_line = (
|
||||
"drawbox=0:round((ih-(iw*(1/{0})))/2)-{1}:iw:{1}:"
|
||||
"t=fill:c={2}@{3}"
|
||||
).format(
|
||||
ratio, line_thickness, line_color_hex, line_color_alpha
|
||||
)
|
||||
bottom_line = (
|
||||
"drawbox=0:ih-round((ih-(iw*(1/{})))/2)"
|
||||
":iw:{}:t=fill:c={}@{}"
|
||||
).format(
|
||||
ratio, line_thickness, line_color_hex, line_color_alpha
|
||||
)
|
||||
output.extend([top_line, bottom_line])
|
||||
|
||||
elif state == "pillar":
|
||||
if fill_color_alpha > 0:
|
||||
left_box = (
|
||||
"drawbox=0:0:round((iw-(ih*{}))/2):ih:t=fill:c={}@{}"
|
||||
).format(ratio, fill_color_hex, fill_color_alpha)
|
||||
|
||||
right_box = (
|
||||
"drawbox=iw-round((iw-(ih*{0}))/2))"
|
||||
":0:round((iw-(ih*{0}))/2):ih:t=fill:c={1}@{2}"
|
||||
).format(ratio, fill_color_hex, fill_color_alpha)
|
||||
|
||||
output.extend([left_box, right_box])
|
||||
|
||||
if line_color_alpha > 0 and line_thickness > 0:
|
||||
left_line = (
|
||||
"drawbox=round((iw-(ih*{}))/2):0:{}:ih:t=fill:c={}@{}"
|
||||
).format(
|
||||
ratio, line_thickness, line_color_hex, line_color_alpha
|
||||
)
|
||||
|
||||
right_line = (
|
||||
"drawbox=iw-round((iw-(ih*{}))/2))"
|
||||
":0:{}:ih:t=fill:c={}@{}"
|
||||
).format(
|
||||
ratio, line_thickness, line_color_hex, line_color_alpha
|
||||
)
|
||||
|
||||
output.extend([left_line, right_line])
|
||||
|
||||
else:
|
||||
raise ValueError(
|
||||
"Letterbox state \"{}\" is not recognized".format(state)
|
||||
)
|
||||
|
||||
return output
|
||||
|
||||
def rescaling_filters(self, temp_data, output_def, new_repre):
|
||||
"""Prepare vieo filters based on tags in new representation.
|
||||
|
||||
|
|
@ -715,7 +814,8 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
"""
|
||||
filters = []
|
||||
|
||||
letter_box = output_def.get("letter_box")
|
||||
letter_box_def = output_def["letter_box"]
|
||||
letter_box_enabled = letter_box_def["enabled"]
|
||||
|
||||
# Get instance data
|
||||
pixel_aspect = temp_data["pixel_aspect"]
|
||||
|
|
@ -795,7 +895,7 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
if (
|
||||
output_width == input_width
|
||||
and output_height == input_height
|
||||
and not letter_box
|
||||
and not letter_box_enabled
|
||||
and pixel_aspect == 1
|
||||
):
|
||||
self.log.debug(
|
||||
|
|
@ -834,30 +934,24 @@ class ExtractReview(pyblish.api.InstancePlugin):
|
|||
)
|
||||
|
||||
# letter_box
|
||||
if letter_box:
|
||||
if input_res_ratio == output_res_ratio:
|
||||
letter_box /= pixel_aspect
|
||||
elif input_res_ratio < output_res_ratio:
|
||||
letter_box /= scale_factor_by_width
|
||||
else:
|
||||
letter_box /= scale_factor_by_height
|
||||
|
||||
scale_filter = "scale={}x{}:flags=lanczos".format(
|
||||
output_width, output_height
|
||||
if letter_box_enabled:
|
||||
filters.extend([
|
||||
"scale={}x{}:flags=lanczos".format(
|
||||
output_width, output_height
|
||||
),
|
||||
"setsar=1"
|
||||
])
|
||||
filters.extend(
|
||||
self.get_letterbox_filters(
|
||||
letter_box_def,
|
||||
input_res_ratio,
|
||||
output_res_ratio,
|
||||
pixel_aspect,
|
||||
scale_factor_by_width,
|
||||
scale_factor_by_height
|
||||
)
|
||||
)
|
||||
|
||||
top_box = (
|
||||
"drawbox=0:0:iw:round((ih-(iw*(1/{})))/2):t=fill:c=black"
|
||||
).format(letter_box)
|
||||
|
||||
bottom_box = (
|
||||
"drawbox=0:ih-round((ih-(iw*(1/{0})))/2)"
|
||||
":iw:round((ih-(iw*(1/{0})))/2):t=fill:c=black"
|
||||
).format(letter_box)
|
||||
|
||||
# Add letter box filters
|
||||
filters.extend([scale_filter, "setsar=1", top_box, bottom_box])
|
||||
|
||||
# scaling none square pixels and 1920 width
|
||||
if (
|
||||
input_height != output_height
|
||||
|
|
|
|||
|
|
@ -45,7 +45,25 @@
|
|||
]
|
||||
},
|
||||
"width": 0,
|
||||
"height": 0
|
||||
"height": 0,
|
||||
"letter_box": {
|
||||
"enabled": false,
|
||||
"ratio": 0.0,
|
||||
"state": "letterbox",
|
||||
"fill_color": [
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
255
|
||||
],
|
||||
"line_thickness": 0,
|
||||
"line_color": [
|
||||
255,
|
||||
0,
|
||||
0,
|
||||
255
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,69 @@
|
|||
"default": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 100000
|
||||
},
|
||||
{
|
||||
"key": "letter_box",
|
||||
"label": "Letter box",
|
||||
"type": "dict",
|
||||
"checkbox_key": "enabled",
|
||||
"children": [
|
||||
{
|
||||
"type": "boolean",
|
||||
"key": "enabled",
|
||||
"label": "Enabled",
|
||||
"default": false
|
||||
},
|
||||
{
|
||||
"key": "ratio",
|
||||
"label": "Ratio",
|
||||
"type": "number",
|
||||
"decimal": 4,
|
||||
"default": 0,
|
||||
"minimum": 0,
|
||||
"maximum": 10000
|
||||
},
|
||||
{
|
||||
"key": "state",
|
||||
"label": "Type",
|
||||
"type": "enum",
|
||||
"enum_items": [
|
||||
{
|
||||
"letterbox": "Letterbox"
|
||||
},
|
||||
{
|
||||
"pillar": "Pillar"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_rgba_color",
|
||||
"template_data": [
|
||||
{
|
||||
"label": "Fill Color",
|
||||
"name": "fill_color"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"key": "line_thickness",
|
||||
"label": "Line Thickness",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 1000
|
||||
},
|
||||
{
|
||||
"type": "schema_template",
|
||||
"name": "template_rgba_color",
|
||||
"template_data": [
|
||||
{
|
||||
"label": "Line Color",
|
||||
"name": "line_color"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,33 @@
|
|||
[
|
||||
{
|
||||
"type": "list-strict",
|
||||
"key": "{name}",
|
||||
"label": "{label}",
|
||||
"object_types": [
|
||||
{
|
||||
"label": "R",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
{
|
||||
"label": "G",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
{
|
||||
"label": "B",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
},
|
||||
{
|
||||
"label": "A",
|
||||
"type": "number",
|
||||
"minimum": 0,
|
||||
"maximum": 255
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue