Merge branch 'develop' into bugfix/maya_burnin_focallength_renders

This commit is contained in:
Ondřej Samohel 2023-04-13 12:11:58 +02:00 committed by GitHub
commit 37369011df
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 258 additions and 137 deletions

View file

@ -62,6 +62,7 @@ class CollectRender(pyblish.api.InstancePlugin):
"frameStart": context.data['frameStart'],
"frameEnd": context.data['frameEnd'],
"version": version_int,
"farm": True
}
self.log.info("data: {0}".format(data))
instance.data.update(data)

View file

@ -32,7 +32,12 @@ from openpype.pipeline import (
load_container,
registered_host,
)
from openpype.pipeline.context_tools import get_current_project_asset
from openpype.pipeline.context_tools import (
get_current_asset_name,
get_current_project_asset,
get_current_project_name,
get_current_task_name
)
self = sys.modules[__name__]
@ -292,15 +297,20 @@ def collect_animation_data(fps=False):
"""
# get scene values as defaults
start = cmds.playbackOptions(query=True, animationStartTime=True)
end = cmds.playbackOptions(query=True, animationEndTime=True)
frame_start = cmds.playbackOptions(query=True, minTime=True)
frame_end = cmds.playbackOptions(query=True, maxTime=True)
handle_start = cmds.playbackOptions(query=True, animationStartTime=True)
handle_end = cmds.playbackOptions(query=True, animationEndTime=True)
handle_start = frame_start - handle_start
handle_end = handle_end - frame_end
# build attributes
data = OrderedDict()
data["frameStart"] = start
data["frameEnd"] = end
data["handleStart"] = 0
data["handleEnd"] = 0
data["frameStart"] = frame_start
data["frameEnd"] = frame_end
data["handleStart"] = handle_start
data["handleEnd"] = handle_end
data["step"] = 1.0
if fps:
@ -2134,9 +2144,13 @@ def get_frame_range():
"""Get the current assets frame range and handles."""
# Set frame start/end
project_name = legacy_io.active_project()
asset_name = legacy_io.Session["AVALON_ASSET"]
project_name = get_current_project_name()
task_name = get_current_task_name()
asset_name = get_current_asset_name()
asset = get_asset_by_name(project_name, asset_name)
settings = get_project_settings(project_name)
include_handles_settings = settings["maya"]["include_handles"]
current_task = asset.get("data").get("tasks").get(task_name)
frame_start = asset["data"].get("frameStart")
frame_end = asset["data"].get("frameEnd")
@ -2148,6 +2162,26 @@ def get_frame_range():
handle_start = asset["data"].get("handleStart") or 0
handle_end = asset["data"].get("handleEnd") or 0
animation_start = frame_start
animation_end = frame_end
include_handles = include_handles_settings["include_handles_default"]
for item in include_handles_settings["per_task_type"]:
if current_task["type"] in item["task_type"]:
include_handles = item["include_handles"]
break
if include_handles:
animation_start -= int(handle_start)
animation_end += int(handle_end)
cmds.playbackOptions(
minTime=frame_start,
maxTime=frame_end,
animationStartTime=animation_start,
animationEndTime=animation_end
)
cmds.currentTime(frame_start)
return {
"frameStart": frame_start,
"frameEnd": frame_end,
@ -2166,7 +2200,6 @@ def reset_frame_range(playback=True, render=True, fps=True):
Defaults to True.
fps (bool, Optional): Whether to set scene FPS. Defaults to True.
"""
if fps:
fps = convert_to_maya_fps(
float(legacy_io.Session.get("AVALON_FPS", 25))

View file

@ -255,7 +255,7 @@ class ValidateMeshHasOverlappingUVs(pyblish.api.InstancePlugin):
# Store original uv set
original_current_uv_set = cmds.polyUVSet(mesh,
query=True,
currentUVSet=True)
currentUVSet=True)[0]
overlapping_faces = []
for uv_set in cmds.polyUVSet(mesh, query=True, allUVSets=True):

View file

@ -24,7 +24,7 @@ class UnrealPrelaunchHook(PreLaunchHook):
"""Hook to handle launching Unreal.
This hook will check if current workfile path has Unreal
project inside. IF not, it initialize it and finally it pass
project inside. IF not, it initializes it, and finally it pass
path to the project by environment variable to Unreal launcher
shell script.
@ -141,6 +141,7 @@ class UnrealPrelaunchHook(PreLaunchHook):
def execute(self):
"""Hook entry method."""
workdir = self.launch_context.env["AVALON_WORKDIR"]
executable = str(self.launch_context.executable)
engine_version = self.app_name.split("/")[-1].replace("-", ".")
try:
if int(engine_version.split(".")[0]) < 4 and \
@ -152,7 +153,7 @@ class UnrealPrelaunchHook(PreLaunchHook):
# there can be string in minor version and in that case
# int cast is failing. This probably happens only with
# early access versions and is of no concert for this check
# so lets keep it quite.
# so let's keep it quiet.
...
unreal_project_filename = self._get_work_filename()
@ -183,26 +184,6 @@ class UnrealPrelaunchHook(PreLaunchHook):
f"[ {engine_version} ]"
))
detected = unreal_lib.get_engine_versions(self.launch_context.env)
detected_str = ', '.join(detected.keys()) or 'none'
self.log.info((
f"{self.signature} detected UE versions: "
f"[ {detected_str} ]"
))
if not detected:
raise ApplicationNotFound("No Unreal Engines are found.")
engine_version = ".".join(engine_version.split(".")[:2])
if engine_version not in detected.keys():
raise ApplicationLaunchFailed((
f"{self.signature} requested version not "
f"detected [ {engine_version} ]"
))
ue_path = unreal_lib.get_editor_exe_path(
Path(detected[engine_version]), engine_version)
self.launch_context.launch_args = [ue_path.as_posix()]
project_path.mkdir(parents=True, exist_ok=True)
# Set "OPENPYPE_UNREAL_PLUGIN" to current process environment for
@ -217,7 +198,9 @@ class UnrealPrelaunchHook(PreLaunchHook):
if self.launch_context.env.get(env_key):
os.environ[env_key] = self.launch_context.env[env_key]
engine_path: Path = Path(detected[engine_version])
# engine_path points to the specific Unreal Engine root
# so, we are going up from the executable itself 3 levels.
engine_path: Path = Path(executable).parents[3]
if not unreal_lib.check_plugin_existence(engine_path):
self.exec_plugin_install(engine_path)

View file

@ -23,6 +23,8 @@ def get_engine_versions(env=None):
Location can be overridden by `UNREAL_ENGINE_LOCATION` environment
variable.
.. deprecated:: 3.15.4
Args:
env (dict, optional): Environment to use.
@ -103,6 +105,8 @@ def _win_get_engine_versions():
This file is JSON file listing installed stuff, Unreal engines
are marked with `"AppName" = "UE_X.XX"`` like `UE_4.24`
.. deprecated:: 3.15.4
Returns:
dict: version as a key and path as a value.
@ -122,6 +126,8 @@ def _darwin_get_engine_version() -> dict:
It works the same as on Windows, just JSON file location is different.
.. deprecated:: 3.15.4
Returns:
dict: version as a key and path as a value.
@ -144,6 +150,8 @@ def _darwin_get_engine_version() -> dict:
def _parse_launcher_locations(install_json_path: str) -> dict:
"""This will parse locations from json file.
.. deprecated:: 3.15.4
Args:
install_json_path (str): Path to `LauncherInstalled.dat`.

View file

@ -142,10 +142,8 @@ class MayaSubmitDeadline(abstract_submit_deadline.AbstractSubmitDeadline):
job_info.Pool = instance.data.get("primaryPool")
job_info.SecondaryPool = instance.data.get("secondaryPool")
job_info.ChunkSize = instance.data.get("chunkSize", 10)
job_info.Comment = context.data.get("comment")
job_info.Priority = instance.data.get("priority", self.priority)
job_info.FramesPerTask = instance.data.get("framesPerTask", 1)
if self.group != "none" and self.group:
job_info.Group = self.group

View file

@ -50,7 +50,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
project_name = context.data["projectName"]
self.fill_missing_asset_docs(context, project_name)
self.fill_instance_data_from_asset(context)
self.fill_latest_versions(context, project_name)
self.fill_anatomy_data(context)
@ -115,23 +114,6 @@ class CollectAnatomyInstanceData(pyblish.api.ContextPlugin):
"Not found asset documents with names \"{}\"."
).format(joined_asset_names))
def fill_instance_data_from_asset(self, context):
for instance in context:
asset_doc = instance.data.get("assetEntity")
if not asset_doc:
continue
asset_data = asset_doc["data"]
for key in (
"fps",
"frameStart",
"frameEnd",
"handleStart",
"handleEnd",
):
if key not in instance.data and key in asset_data:
instance.data[key] = asset_data[key]
def fill_latest_versions(self, context, project_name):
"""Try to find latest version for each instance's subset.

View file

@ -1144,6 +1144,10 @@
}
]
},
"include_handles": {
"include_handles_default": false,
"per_task_type": []
},
"templated_workfile_build": {
"profiles": []
},

View file

@ -542,45 +542,10 @@
"create_first_version": false,
"custom_templates": [],
"builder_on_start": false,
"profiles": [
{
"task_types": [],
"tasks": [],
"current_context": [
{
"subset_name_filters": [],
"families": [
"render",
"plate"
],
"repre_names": [
"exr",
"dpx",
"mov",
"mp4",
"h264"
],
"loaders": [
"LoadClip"
]
}
],
"linked_assets": []
}
]
"profiles": []
},
"templated_workfile_build": {
"profiles": [
{
"task_types": [
"Compositing"
],
"task_names": [],
"path": "{project[name]}/templates/comp.nk",
"keep_placeholder": true,
"create_first_version": true
}
]
"profiles": []
},
"filters": {}
}

View file

@ -133,7 +133,9 @@
"linux": []
},
"arguments": {
"windows": ["-U MAXScript {OPENPYPE_ROOT}\\openpype\\hosts\\max\\startup\\startup.ms"],
"windows": [
"-U MAXScript {OPENPYPE_ROOT}\\openpype\\hosts\\max\\startup\\startup.ms"
],
"darwin": [],
"linux": []
},
@ -361,9 +363,15 @@
]
},
"arguments": {
"windows": ["--nukeassist"],
"darwin": ["--nukeassist"],
"linux": ["--nukeassist"]
"windows": [
"--nukeassist"
],
"darwin": [
"--nukeassist"
],
"linux": [
"--nukeassist"
]
},
"environment": {}
},
@ -379,9 +387,15 @@
]
},
"arguments": {
"windows": ["--nukeassist"],
"darwin": ["--nukeassist"],
"linux": ["--nukeassist"]
"windows": [
"--nukeassist"
],
"darwin": [
"--nukeassist"
],
"linux": [
"--nukeassist"
]
},
"environment": {}
},
@ -397,9 +411,15 @@
]
},
"arguments": {
"windows": ["--nukeassist"],
"darwin": ["--nukeassist"],
"linux": ["--nukeassist"]
"windows": [
"--nukeassist"
],
"darwin": [
"--nukeassist"
],
"linux": [
"--nukeassist"
]
},
"environment": {}
},
@ -415,9 +435,15 @@
]
},
"arguments": {
"windows": ["--nukeassist"],
"darwin": ["--nukeassist"],
"linux": ["--nukeassist"]
"windows": [
"--nukeassist"
],
"darwin": [
"--nukeassist"
],
"linux": [
"--nukeassist"
]
},
"environment": {}
},
@ -433,9 +459,15 @@
]
},
"arguments": {
"windows": ["--nukeassist"],
"darwin": ["--nukeassist"],
"linux": ["--nukeassist"]
"windows": [
"--nukeassist"
],
"darwin": [
"--nukeassist"
],
"linux": [
"--nukeassist"
]
},
"environment": {}
},
@ -449,9 +481,15 @@
"linux": []
},
"arguments": {
"windows": ["--nukeassist"],
"darwin": ["--nukeassist"],
"linux": ["--nukeassist"]
"windows": [
"--nukeassist"
],
"darwin": [
"--nukeassist"
],
"linux": [
"--nukeassist"
]
},
"environment": {}
},
@ -1450,21 +1488,45 @@
"label": "Unreal Editor",
"icon": "{}/app_icons/ue4.png",
"host_name": "unreal",
"environment": {},
"environment": {
"UE_PYTHONPATH": "{PYTHONPATH}"
},
"variants": {
"4-27": {
"use_python_2": false,
"environment": {}
},
"5-0": {
"use_python_2": false,
"environment": {
"UE_PYTHONPATH": "{PYTHONPATH}"
}
"executables": {
"windows": [
"C:\\Program Files\\Epic Games\\UE_5.0\\Engine\\Binaries\\Win64\\UnrealEditor.exe"
],
"darwin": [],
"linux": []
},
"arguments": {
"windows": [],
"darwin": [],
"linux": []
},
"environment": {}
},
"5-1": {
"use_python_2": false,
"executables": {
"windows": [
"C:\\Program Files\\Epic Games\\UE_5.1\\Engine\\Binaries\\Win64\\UnrealEditor.exe"
],
"darwin": [],
"linux": []
},
"arguments": {
"windows": [],
"darwin": [],
"linux": []
},
"environment": {}
},
"__dynamic_keys_labels__": {
"4-27": "4.27",
"5-0": "5.0"
"5-1": "Unreal 5.1",
"5-0": "Unreal 5.0"
}
}
},

View file

@ -448,6 +448,8 @@ class TextEntity(InputEntity):
self.multiline = self.schema_data.get("multiline", False)
self.placeholder_text = self.schema_data.get("placeholder")
self.value_hints = self.schema_data.get("value_hints") or []
self.minimum_lines_count = (
self.schema_data.get("minimum_lines_count") or 0)
def schema_validations(self):
if self.multiline and self.value_hints:

View file

@ -380,6 +380,7 @@ How output of the schema could look like on save:
- simple text input
- key `"multiline"` allows to enter multiple lines of text (Default: `False`)
- key `"placeholder"` allows to show text inside input when is empty (Default: `None`)
- key `"minimum_lines_count"` allows to define minimum size hint for UI. Can be 0-n lines.
```
{

View file

@ -151,6 +151,40 @@
}
]
},
{
"type": "dict",
"key": "include_handles",
"collapsible": true,
"label": "Include/Exclude Handles in default playback & render range",
"children": [
{
"key": "include_handles_default",
"label": "Include handles by default",
"type": "boolean"
},
{
"type": "list",
"key": "per_task_type",
"label": "Include/exclude handles by task type",
"use_label_wrap": true,
"object_type": {
"type": "dict",
"children": [
{
"type": "task-types-enum",
"key": "task_type",
"label": "Task types"
},
{
"type": "boolean",
"key": "include_handles",
"label": "Include handles"
}
]
}
}
]
},
{
"type": "schema",
"name": "schema_scriptsmenu"

View file

@ -30,12 +30,7 @@
"children": [
{
"type": "schema_template",
"name": "template_host_variant_items",
"skip_paths": [
"executables",
"separator",
"arguments"
]
"name": "template_host_variant_items"
}
]
}

View file

@ -360,14 +360,16 @@ class TextWidget(InputWidget):
def _add_inputs_to_layout(self):
multiline = self.entity.multiline
if multiline:
self.input_field = SettingsPlainTextEdit(self.content_widget)
input_field = SettingsPlainTextEdit(self.content_widget)
if self.entity.minimum_lines_count:
input_field.set_minimum_lines(self.entity.minimum_lines_count)
else:
self.input_field = SettingsLineEdit(self.content_widget)
input_field = SettingsLineEdit(self.content_widget)
placeholder_text = self.entity.placeholder_text
if placeholder_text:
self.input_field.setPlaceholderText(placeholder_text)
input_field.setPlaceholderText(placeholder_text)
self.input_field = input_field
self.setFocusProxy(self.input_field)
layout_kwargs = {}

View file

@ -310,11 +310,32 @@ class SettingsLineEdit(PlaceholderLineEdit):
class SettingsPlainTextEdit(QtWidgets.QPlainTextEdit):
focused_in = QtCore.Signal()
_min_lines = 0
def focusInEvent(self, event):
super(SettingsPlainTextEdit, self).focusInEvent(event)
self.focused_in.emit()
def set_minimum_lines(self, lines):
self._min_lines = lines
self.update()
def minimumSizeHint(self):
result = super(SettingsPlainTextEdit, self).minimumSizeHint()
if self._min_lines < 1:
return result
document = self.document()
margins = self.contentsMargins()
d_margin = (
((document.documentMargin() + self.frameWidth()) * 2)
+ margins.top() + margins.bottom()
)
font = document.defaultFont()
font_metrics = QtGui.QFontMetrics(font)
result.setHeight(
d_margin + (font_metrics.lineSpacing() * self._min_lines))
return result
class SettingsToolBtn(ImageButton):
_mask_pixmap = None

View file

@ -200,6 +200,16 @@ Most settings to override in the viewport are self explanatory and can be found
These options are set on the camera shape when publishing the review. They correspond to attributes on the Maya camera shape node.
![Extract Playblast Settings](assets/maya-admin_extract_playblast_settings_camera_options.png)
## Include/exclude handles by task type
You can include or exclude handles, globally or by task type.
The "Include handles by default" defines whether by default handles are included. Additionally you can add a per task type override whether you want to include or exclude handles.
For example, in this image you can see that handles are included by default in all task types, except for the 'Lighting' task, where the toggle is disabled.
![Include/exclude handles](assets/maya-admin_exclude_handles.png)
And here you can see that the handles are disabled by default, except in 'Animation' task where it's enabled.
![Custom menu definition](assets/maya-admin_include_handles.png)
## Custom Menu

View file

@ -14,17 +14,29 @@ OpenPype has a limitation regarding duplicated names. Name of assets must be uni
### Subset
Usually, an asset needs to be created in multiple *'flavours'*. A character might have multiple different looks, model needs to be published in different resolutions, a standard animation rig might not be usable in a crowd system and so on. 'Subsets' are here to accommodate all this variety that might be needed within a single asset. A model might have subset: *'main'*, *'proxy'*, *'sculpt'*, while data of *'look'* family could have subsets *'main'*, *'dirty'*, *'damaged'*. Subsets have some recommendations for their names, but ultimately it's up to the artist to use them for separation of publishes when needed.
A published output from an asset results in a subset.
The subset type is referred to as [family](#family), for example a rig, pointcache, look.
A single asset can have many subsets, even of a single family, named [variants](#variant).
By default a subset is named as a combination of family + variant, sometimes prefixed with the task name (like workfile).
### Variant
Usually, an asset needs to be created in multiple *'flavours'*. A character might have multiple different looks, model needs to be published in different resolutions, a standard animation rig might not be usable in a crowd system and so on. 'Variants' are here to accommodate all this variety that might be needed within a single asset. A model might have variant: *'main'*, *'proxy'*, *'sculpt'*, while data of *'look'* family could have subsets *'main'*, *'dirty'*, *'damaged'*. Variants have some recommendations for their names, but ultimately it's up to the artist to use them for separation of publishes when needed.
### Version
A numbered iteration of a given subset. Each version contains at least one [representation][daa74ebf].
A numbered iteration of a given subset. Each version contains at least one [representation](#representation).
[daa74ebf]: #representation "representation"
#### Hero version
A hero version is a version that is always the latest published version. When a new publish is generated its written over the previous hero version replacing it in-place as opposed to regular versions where each new publish is a higher version number.
This is an optional feature. The generation of hero versions can be completely disabled in OpenPype by an admin through the Studio Settings.
### Representation
Each published variant can come out of the software in multiple representations. All of them hold exactly the same data, but in different formats. A model, for example, might be saved as `.OBJ`, Alembic, Maya geometry or as all of them, to be ready for pickup in any other applications supporting these formats.
Each published subset version can come out of the software in multiple representations. All of them hold exactly the same data, but in different formats. A model, for example, might be saved as `.OBJ`, Alembic, Maya geometry or as all of them, to be ready for pickup in any other applications supporting these formats.
#### Naming convention
@ -33,18 +45,22 @@ At this moment names of assets, tasks, subsets or representations can contain on
### Family
Each published [subset][3b89d8e0] can have exactly one family assigned to it. Family determines the type of data that the subset holds. Family doesn't dictate the file type, but can enforce certain technical specifications. For example OpenPype default configuration expects `model` family to only contain geometry without any shaders or joints when it is published.
Each published [subset](#subset) can have exactly one family assigned to it. Family determines the type of data that the subset holds. Family doesn't dictate the file type, but can enforce certain technical specifications. For example OpenPype default configuration expects `model` family to only contain geometry without any shaders or joints when it is published.
### Task
[3b89d8e0]: #subset "subset"
A task defines a work area for an asset where an artist can work in. For example asset *characterA* can have tasks named *modeling* and *rigging*. Tasks also have types. Multiple tasks of the same type may exist on an asset. A task with type `fx` could for example appear twice as *fx_fire* and *fx_cloth*.
Without a task you cannot launch a host application.
### Workfile
The source scene file an artist works in within their task. These are versioned scene files and can be loaded and saved (automatically named) through the [workfiles tool](artist_tools_workfiles.md).
### Host
General term for Software or Application supported by OpenPype and Avalon. These are usually DCC applications like Maya, Houdini or Nuke, but can also be a web based service like Ftrack or Clockify.
### Tool
Small piece of software usually dedicated to a particular purpose. Most of OpenPype and Avalon tools have GUI, but some are command line only.
@ -54,6 +70,10 @@ Small piece of software usually dedicated to a particular purpose. Most of OpenP
Process of exporting data from your work scene to versioned, immutable file that can be used by other artists in the studio.
#### (Publish) Instance
A publish instance is a single entry which defines a publish output. Publish instances persist within the workfile. This way we can expect that a publish from a newer workfile will produce similar consistent versioned outputs.
### Load
Process of importing previously published subsets into your current scene, using any of the OpenPype tools.

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB