Merge branch 'develop' into bugfix/OP-6952_Resolve-broken-version-management-for-loaded-containers

This commit is contained in:
Jakub Ježek 2023-10-09 15:40:34 +02:00 committed by GitHub
commit 5c010c4ec9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 238 additions and 16 deletions

View file

@ -26,8 +26,7 @@ class CacheModelLoader(plugin.AssetLoader):
Note:
At least for now it only supports Alembic files.
"""
families = ["model", "pointcache"]
families = ["model", "pointcache", "animation"]
representations = ["abc"]
label = "Load Alembic"
@ -53,16 +52,12 @@ class CacheModelLoader(plugin.AssetLoader):
def _process(self, libpath, asset_group, group_name):
plugin.deselect_all()
collection = bpy.context.view_layer.active_layer_collection.collection
relative = bpy.context.preferences.filepaths.use_relative_paths
bpy.ops.wm.alembic_import(
filepath=libpath,
relative_path=relative
)
parent = bpy.context.scene.collection
imported = lib.get_selection()
# Children must be linked before parents,
@ -79,6 +74,10 @@ class CacheModelLoader(plugin.AssetLoader):
objects.reverse()
for obj in objects:
# Unlink the object from all collections
collections = obj.users_collection
for collection in collections:
collection.objects.unlink(obj)
name = obj.name
obj.name = f"{group_name}:{name}"
if obj.type != 'EMPTY':
@ -90,7 +89,7 @@ class CacheModelLoader(plugin.AssetLoader):
material_slot.material.name = f"{group_name}:{name_mat}"
if not obj.get(AVALON_PROPERTY):
obj[AVALON_PROPERTY] = dict()
obj[AVALON_PROPERTY] = {}
avalon_info = obj[AVALON_PROPERTY]
avalon_info.update({"container_name": group_name})
@ -99,6 +98,18 @@ class CacheModelLoader(plugin.AssetLoader):
return objects
def _link_objects(self, objects, collection, containers, asset_group):
# Link the imported objects to any collection where the asset group is
# linked to, except the AVALON_CONTAINERS collection
group_collections = [
collection
for collection in asset_group.users_collection
if collection != containers]
for obj in objects:
for collection in group_collections:
collection.objects.link(obj)
def process_asset(
self, context: dict, name: str, namespace: Optional[str] = None,
options: Optional[Dict] = None
@ -120,18 +131,21 @@ class CacheModelLoader(plugin.AssetLoader):
group_name = plugin.asset_name(asset, subset, unique_number)
namespace = namespace or f"{asset}_{unique_number}"
avalon_containers = bpy.data.collections.get(AVALON_CONTAINERS)
if not avalon_containers:
avalon_containers = bpy.data.collections.new(
name=AVALON_CONTAINERS)
bpy.context.scene.collection.children.link(avalon_containers)
containers = bpy.data.collections.get(AVALON_CONTAINERS)
if not containers:
containers = bpy.data.collections.new(name=AVALON_CONTAINERS)
bpy.context.scene.collection.children.link(containers)
asset_group = bpy.data.objects.new(group_name, object_data=None)
avalon_containers.objects.link(asset_group)
containers.objects.link(asset_group)
objects = self._process(libpath, asset_group, group_name)
bpy.context.scene.collection.objects.link(asset_group)
# Link the asset group to the active collection
collection = bpy.context.view_layer.active_layer_collection.collection
collection.objects.link(asset_group)
self._link_objects(objects, asset_group, containers, asset_group)
asset_group[AVALON_PROPERTY] = {
"schema": "openpype:container-2.0",
@ -207,7 +221,11 @@ class CacheModelLoader(plugin.AssetLoader):
mat = asset_group.matrix_basis.copy()
self._remove(asset_group)
self._process(str(libpath), asset_group, object_name)
objects = self._process(str(libpath), asset_group, object_name)
containers = bpy.data.collections.get(AVALON_CONTAINERS)
self._link_objects(objects, asset_group, containers, asset_group)
asset_group.matrix_basis = mat
metadata["libpath"] = str(libpath)

View file

@ -0,0 +1,117 @@
import pyblish.api
from openpype.pipeline import (
PublishValidationError,
OptionalPyblishPluginMixin
)
from maya import cmds
from openpype.pipeline.publish import RepairAction
from openpype.hosts.maya.api import lib
from openpype.hosts.maya.api.lib import reset_scene_resolution
class ValidateResolution(pyblish.api.InstancePlugin,
OptionalPyblishPluginMixin):
"""Validate the render resolution setting aligned with DB"""
order = pyblish.api.ValidatorOrder
families = ["renderlayer"]
hosts = ["maya"]
label = "Validate Resolution"
actions = [RepairAction]
optional = True
def process(self, instance):
if not self.is_active(instance.data):
return
invalid = self.get_invalid_resolution(instance)
if invalid:
raise PublishValidationError(
"Render resolution is invalid. See log for details.",
description=(
"Wrong render resolution setting. "
"Please use repair button to fix it.\n\n"
"If current renderer is V-Ray, "
"make sure vraySettings node has been created."
)
)
@classmethod
def get_invalid_resolution(cls, instance):
width, height, pixelAspect = cls.get_db_resolution(instance)
current_renderer = instance.data["renderer"]
layer = instance.data["renderlayer"]
invalid = False
if current_renderer == "vray":
vray_node = "vraySettings"
if cmds.objExists(vray_node):
current_width = lib.get_attr_in_layer(
"{}.width".format(vray_node), layer=layer)
current_height = lib.get_attr_in_layer(
"{}.height".format(vray_node), layer=layer)
current_pixelAspect = lib.get_attr_in_layer(
"{}.pixelAspect".format(vray_node), layer=layer
)
else:
cls.log.error(
"Can't detect VRay resolution because there is no node "
"named: `{}`".format(vray_node)
)
return True
else:
current_width = lib.get_attr_in_layer(
"defaultResolution.width", layer=layer)
current_height = lib.get_attr_in_layer(
"defaultResolution.height", layer=layer)
current_pixelAspect = lib.get_attr_in_layer(
"defaultResolution.pixelAspect", layer=layer
)
if current_width != width or current_height != height:
cls.log.error(
"Render resolution {}x{} does not match "
"asset resolution {}x{}".format(
current_width, current_height,
width, height
))
invalid = True
if current_pixelAspect != pixelAspect:
cls.log.error(
"Render pixel aspect {} does not match "
"asset pixel aspect {}".format(
current_pixelAspect, pixelAspect
))
invalid = True
return invalid
@classmethod
def get_db_resolution(cls, instance):
asset_doc = instance.data["assetEntity"]
project_doc = instance.context.data["projectEntity"]
for data in [asset_doc["data"], project_doc["data"]]:
if (
"resolutionWidth" in data and
"resolutionHeight" in data and
"pixelAspect" in data
):
width = data["resolutionWidth"]
height = data["resolutionHeight"]
pixelAspect = data["pixelAspect"]
return int(width), int(height), float(pixelAspect)
# Defaults if not found in asset document or project document
return 1920, 1080, 1.0
@classmethod
def repair(cls, instance):
# Usually without renderlayer overrides the renderlayers
# all share the same resolution value - so fixing the first
# will have fixed all the others too. It's much faster to
# check whether it's invalid first instead of switching
# into all layers individually
if not cls.get_invalid_resolution(instance):
cls.log.debug(
"Nothing to repair on instance: {}".format(instance)
)
return
layer_node = instance.data['setMembers']
with lib.renderlayer(layer_node):
reset_scene_resolution()

View file

@ -1,5 +1,6 @@
import os
import shutil
import filecmp
from openpype.client.entities import get_representations
from openpype.lib.applications import PreLaunchHook, LaunchTypes
@ -194,3 +195,71 @@ class CopyLastPublishedWorkfile(PreLaunchHook):
self.data["last_workfile_path"] = local_workfile_path
# Keep source filepath for further path conformation
self.data["source_filepath"] = last_published_workfile_path
# Get resources directory
resources_dir = os.path.join(
os.path.dirname(local_workfile_path), 'resources'
)
# Make resource directory if it doesn't exist
if not os.path.exists(resources_dir):
os.mkdir(resources_dir)
# Copy resources to the local resources directory
for file in workfile_representation['files']:
# Get resource main path
resource_main_path = file["path"].replace(
"{root[main]}", str(anatomy.roots["main"])
)
# Only copy if the resource file exists, and it's not the workfile
if (
not os.path.exists(resource_main_path)
and not resource_main_path != last_published_workfile_path
):
continue
# Get resource file basename
resource_basename = os.path.basename(resource_main_path)
# Get resource path in workfile folder
resource_work_path = os.path.join(
resources_dir, resource_basename
)
if not os.path.exists(resource_work_path):
continue
# Check if the resource file already exists
# in the workfile resources folder,
# and both files are the same.
if filecmp.cmp(resource_main_path, resource_work_path):
self.log.warning(
'Resource "{}" already exists.'
.format(resource_basename)
)
continue
else:
# Add `.old` to existing resource path
resource_path_old = resource_work_path + '.old'
if os.path.exists(resource_work_path + '.old'):
for i in range(1, 100):
p = resource_path_old + '%02d' % i
if not os.path.exists(p):
# Rename existing resource file to
# `resource_name.old` + 2 digits
shutil.move(resource_work_path, p)
break
else:
self.log.warning(
'There are a hundred old files for '
'resource "{}". '
'Perhaps is it time to clean up your '
'resources folder'
.format(resource_basename)
)
continue
else:
# Rename existing resource file to `resource_name.old`
shutil.move(resource_work_path, resource_path_old)
# Copy resource file to workfile resources folder
shutil.copy(resource_main_path, resources_dir)

View file

@ -829,6 +829,11 @@
"redshift_render_attributes": [],
"renderman_render_attributes": []
},
"ValidateResolution": {
"enabled": true,
"optional": true,
"active": true
},
"ValidateCurrentRenderLayerIsRenderable": {
"enabled": true,
"optional": false,

View file

@ -431,6 +431,10 @@
"type": "schema_template",
"name": "template_publish_plugin",
"template_data": [
{
"key": "ValidateResolution",
"label": "Validate Resolution Settings"
},
{
"key": "ValidateCurrentRenderLayerIsRenderable",
"label": "Validate Current Render Layer Has Renderable Camera"

View file

@ -433,6 +433,10 @@ class PublishersModel(BaseSettingsModel):
default_factory=ValidateRenderSettingsModel,
title="Validate Render Settings"
)
ValidateResolution: BasicValidateModel = Field(
default_factory=BasicValidateModel,
title="Validate Resolution Setting"
)
ValidateCurrentRenderLayerIsRenderable: BasicValidateModel = Field(
default_factory=BasicValidateModel,
title="Validate Current Render Layer Has Renderable Camera"
@ -902,6 +906,11 @@ DEFAULT_PUBLISH_SETTINGS = {
"redshift_render_attributes": [],
"renderman_render_attributes": []
},
"ValidateResolution": {
"enabled": True,
"optional": True,
"active": True
},
"ValidateCurrentRenderLayerIsRenderable": {
"enabled": True,
"optional": False,

View file

@ -1,3 +1,3 @@
# -*- coding: utf-8 -*-
"""Package declaring addon version."""
__version__ = "0.1.4"
__version__ = "0.1.5"