diff --git a/openpype/hosts/maya/api/lib.py b/openpype/hosts/maya/api/lib.py index bd26738040..f6fcab7e40 100644 --- a/openpype/hosts/maya/api/lib.py +++ b/openpype/hosts/maya/api/lib.py @@ -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)) diff --git a/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py b/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py index 74269cc506..7dd66eed6c 100644 --- a/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py +++ b/openpype/hosts/maya/plugins/publish/validate_mesh_overlapping_uvs.py @@ -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): diff --git a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py index 062732c059..19d4f170b6 100644 --- a/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py +++ b/openpype/modules/deadline/plugins/publish/submit_maya_deadline.py @@ -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 diff --git a/openpype/plugins/publish/collect_anatomy_instance_data.py b/openpype/plugins/publish/collect_anatomy_instance_data.py index 48171aa957..4fbb93324b 100644 --- a/openpype/plugins/publish/collect_anatomy_instance_data.py +++ b/openpype/plugins/publish/collect_anatomy_instance_data.py @@ -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. diff --git a/openpype/settings/defaults/project_settings/hiero.json b/openpype/settings/defaults/project_settings/hiero.json index 100c1f5b47..3e613aa1bf 100644 --- a/openpype/settings/defaults/project_settings/hiero.json +++ b/openpype/settings/defaults/project_settings/hiero.json @@ -21,7 +21,8 @@ "floatLut": "linear", "logLut": "Cineon", "viewerLut": "sRGB", - "thumbnailLut": "sRGB" + "thumbnailLut": "sRGB", + "monitorOutLut": "sRGB" }, "regexInputs": { "inputs": [ diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index a8689524db..201dda1c2d 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -1144,6 +1144,10 @@ } ] }, + "include_handles": { + "include_handles_default": false, + "per_task_type": [] + }, "templated_workfile_build": { "profiles": [] }, diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json index f44f92438c..ea05f4ab9b 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_hiero.json @@ -42,10 +42,19 @@ "nuke-default": "nuke-default" }, { - "aces_1.0.3": "aces_1.0.3" + "aces_1.0.3": "aces_1.0.3 (12)" }, { - "aces_1.1": "aces_1.1" + "aces_1.1": "aces_1.1 (12, 13)" + }, + { + "aces_1.2": "aces_1.2 (13, 14)" + }, + { + "studio-config-v1.0.0_aces-v1.3_ocio-v2.1": "studio-config-v1.0.0_aces-v1.3_ocio-v2.1 (14)" + }, + { + "cg-config-v1.0.0_aces-v1.3_ocio-v2.1": "cg-config-v1.0.0_aces-v1.3_ocio-v2.1 (14)" }, { "custom": "custom" @@ -93,6 +102,11 @@ "type": "text", "key": "thumbnailLut", "label": "Thumbnails" + }, + { + "type": "text", + "key": "monitorOutLut", + "label": "Monitor" } ] } diff --git a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json index 47dfb37024..ccc967a260 100644 --- a/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json +++ b/openpype/settings/entities/schemas/projects_schema/schema_project_maya.json @@ -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" diff --git a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json index 1cd6f0e67f..21f6baff9e 100644 --- a/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json +++ b/openpype/settings/entities/schemas/projects_schema/schemas/schema_nuke_imageio.json @@ -74,28 +74,34 @@ "nuke-default": "nuke-default" }, { - "spi-vfx": "spi-vfx" + "spi-vfx": "spi-vfx (11)" }, { - "spi-anim": "spi-anim" + "spi-anim": "spi-anim (11)" }, { - "aces_0.1.1": "aces_0.1.1" + "aces_0.1.1": "aces_0.1.1 (11)" }, { - "aces_0.7.1": "aces_0.7.1" + "aces_0.7.1": "aces_0.7.1 (11)" }, { - "aces_1.0.1": "aces_1.0.1" + "aces_1.0.1": "aces_1.0.1 (11)" }, { - "aces_1.0.3": "aces_1.0.3" + "aces_1.0.3": "aces_1.0.3 (11, 12)" }, { - "aces_1.1": "aces_1.1" + "aces_1.1": "aces_1.1 (12, 13)" }, { - "aces_1.2": "aces_1.2" + "aces_1.2": "aces_1.2 (13, 14)" + }, + { + "studio-config-v1.0.0_aces-v1.3_ocio-v2.1": "studio-config-v1.0.0_aces-v1.3_ocio-v2.1 (14)" + }, + { + "cg-config-v1.0.0_aces-v1.3_ocio-v2.1": "cg-config-v1.0.0_aces-v1.3_ocio-v2.1 (14)" }, { "custom": "custom" @@ -257,4 +263,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/website/docs/admin_hosts_maya.md b/website/docs/admin_hosts_maya.md index 0dd86d6e7c..eea6456c76 100644 --- a/website/docs/admin_hosts_maya.md +++ b/website/docs/admin_hosts_maya.md @@ -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 diff --git a/website/docs/artist_concepts.md b/website/docs/artist_concepts.md index 7582540811..1e55c8139d 100644 --- a/website/docs/artist_concepts.md +++ b/website/docs/artist_concepts.md @@ -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. diff --git a/website/docs/assets/maya-admin_exclude_handles.png b/website/docs/assets/maya-admin_exclude_handles.png new file mode 100644 index 0000000000..9a50f2c287 Binary files /dev/null and b/website/docs/assets/maya-admin_exclude_handles.png differ diff --git a/website/docs/assets/maya-admin_include_handles.png b/website/docs/assets/maya-admin_include_handles.png new file mode 100644 index 0000000000..88d2270ddc Binary files /dev/null and b/website/docs/assets/maya-admin_include_handles.png differ