diff --git a/openpype/hosts/maya/api/fbx.py b/openpype/hosts/maya/api/fbx.py new file mode 100644 index 0000000000..92683da51b --- /dev/null +++ b/openpype/hosts/maya/api/fbx.py @@ -0,0 +1,202 @@ +# -*- coding: utf-8 -*- +"""Tools to work with FBX.""" +import logging + +from pyblish.api import Instance + +from maya import cmds # noqa +import maya.mel as mel # noqa + + +class FBXExtractor: + """Extract FBX from Maya. + + This extracts reproducible FBX exports ignoring any of the settings set + on the local machine in the FBX export options window. + + All export settings are applied with the `FBXExport*` commands prior + to the `FBXExport` call itself. The options can be overridden with + their + nice names as seen in the "options" property on this class. + + For more information on FBX exports see: + - https://knowledge.autodesk.com/support/maya/learn-explore/caas + /CloudHelp/cloudhelp/2016/ENU/Maya/files/GUID-6CCE943A-2ED4-4CEE-96D4 + -9CB19C28F4E0-htm.html + - http://forums.cgsociety.org/archive/index.php?t-1032853.html + - https://groups.google.com/forum/#!msg/python_inside_maya/cLkaSo361oE + /LKs9hakE28kJ + + """ + @property + def options(self): + """Overridable options for FBX Export + + Given in the following format + - {NAME: EXPECTED TYPE} + + If the overridden option's type does not match, + the option is not included and a warning is logged. + + """ + + return { + "cameras": bool, + "smoothingGroups": bool, + "hardEdges": bool, + "tangents": bool, + "smoothMesh": bool, + "instances": bool, + # "referencedContainersContent": bool, # deprecated in Maya 2016+ + "bakeComplexAnimation": int, + "bakeComplexStart": int, + "bakeComplexEnd": int, + "bakeComplexStep": int, + "bakeResampleAnimation": bool, + "animationOnly": bool, + "useSceneName": bool, + "quaternion": str, # "euler" + "shapes": bool, + "skins": bool, + "constraints": bool, + "lights": bool, + "embeddedTextures": bool, + "inputConnections": bool, + "upAxis": str, # x, y or z, + "triangulate": bool + } + + @property + def default_options(self): + """The default options for FBX extraction. + + This includes shapes, skins, constraints, lights and incoming + connections and exports with the Y-axis as up-axis. + + By default this uses the time sliders start and end time. + + """ + + start_frame = int(cmds.playbackOptions(query=True, + animationStartTime=True)) + end_frame = int(cmds.playbackOptions(query=True, + animationEndTime=True)) + + return { + "cameras": False, + "smoothingGroups": False, + "hardEdges": False, + "tangents": False, + "smoothMesh": False, + "instances": False, + "bakeComplexAnimation": True, + "bakeComplexStart": start_frame, + "bakeComplexEnd": end_frame, + "bakeComplexStep": 1, + "bakeResampleAnimation": True, + "animationOnly": False, + "useSceneName": False, + "quaternion": "euler", + "shapes": True, + "skins": True, + "constraints": False, + "lights": True, + "embeddedTextures": True, + "inputConnections": True, + "upAxis": "y", + "triangulate": False + } + + def __init__(self, log=None): + # Ensure FBX plug-in is loaded + self.log = log or logging.getLogger(self.__class__.__name__) + cmds.loadPlugin("fbxmaya", quiet=True) + + def parse_overrides(self, instance, options): + """Inspect data of instance to determine overridden options + + An instance may supply any of the overridable options + as data, the option is then added to the extraction. + + """ + + for key in instance.data: + if key not in self.options: + continue + + # Ensure the data is of correct type + value = instance.data[key] + if not isinstance(value, self.options[key]): + self.log.warning( + "Overridden attribute {key} was of " + "the wrong type: {invalid_type} " + "- should have been {valid_type}".format( + key=key, + invalid_type=type(value).__name__, + valid_type=self.options[key].__name__)) + continue + + options[key] = value + + return options + + def set_options_from_instance(self, instance): + # type: (Instance) -> None + """Sets FBX export options from data in the instance. + + Args: + instance (Instance): Instance data. + + """ + # Parse export options + options = self.default_options + options = self.parse_overrides(instance, options) + self.log.info("Export options: {0}".format(options)) + + # Collect the start and end including handles + start = instance.data.get("frameStartHandle") or \ + instance.context.data.get("frameStartHandle") + end = instance.data.get("frameEndHandle") or \ + instance.context.data.get("frameEndHandle") + + options['bakeComplexStart'] = start + options['bakeComplexEnd'] = end + + # First apply the default export settings to be fully consistent + # each time for successive publishes + mel.eval("FBXResetExport") + + # Apply the FBX overrides through MEL since the commands + # only work correctly in MEL according to online + # available discussions on the topic + _iteritems = getattr(options, "iteritems", options.items) + for option, value in _iteritems(): + key = option[0].upper() + option[1:] # uppercase first letter + + # Boolean must be passed as lower-case strings + # as to MEL standards + if isinstance(value, bool): + value = str(value).lower() + + template = "FBXExport{0} {1}" if key == "UpAxis" else \ + "FBXExport{0} -v {1}" # noqa + cmd = template.format(key, value) + self.log.info(cmd) + mel.eval(cmd) + + # Never show the UI or generate a log + mel.eval("FBXExportShowUI -v false") + mel.eval("FBXExportGenerateLog -v false") + + @staticmethod + def export(members, path): + # type: (list, str) -> None + """Export members as FBX with given path. + + Args: + members (list): List of members to export. + path (str): Path to use for export. + + """ + cmds.select(members, r=1, noExpand=True) + mel.eval('FBXExport -f "{}" -s'.format(path)) diff --git a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py index 9ad560ab7c..f62d15fe62 100644 --- a/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/create/create_unreal_staticmesh.py @@ -10,7 +10,7 @@ class CreateUnrealStaticMesh(plugin.Creator): """Unreal Static Meshes with collisions.""" name = "staticMeshMain" label = "Unreal - Static Mesh" - family = "unrealStaticMesh" + family = "staticMesh" icon = "cube" dynamic_subset_keys = ["asset"] @@ -28,10 +28,10 @@ class CreateUnrealStaticMesh(plugin.Creator): variant, task_name, asset_id, project_name, host_name ) dynamic_data["asset"] = Session.get("AVALON_ASSET") - return dynamic_data def process(self): + self.name = "{}_{}".format(self.family, self.name) with lib.undo_chunk(): instance = super(CreateUnrealStaticMesh, self).process() content = cmds.sets(instance, query=True) diff --git a/openpype/hosts/maya/plugins/load/load_reference.py b/openpype/hosts/maya/plugins/load/load_reference.py index 0565b0b95c..8713182d3f 100644 --- a/openpype/hosts/maya/plugins/load/load_reference.py +++ b/openpype/hosts/maya/plugins/load/load_reference.py @@ -20,7 +20,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader): "camera", "rig", "camerarig", - "xgen"] + "xgen", + "staticMesh"] representations = ["ma", "abc", "fbx", "mb"] label = "Reference" diff --git a/openpype/hosts/maya/plugins/publish/clean_nodes.py b/openpype/hosts/maya/plugins/publish/clean_nodes.py deleted file mode 100644 index 03995cdabe..0000000000 --- a/openpype/hosts/maya/plugins/publish/clean_nodes.py +++ /dev/null @@ -1,31 +0,0 @@ -# -*- coding: utf-8 -*- -"""Cleanup leftover nodes.""" -from maya import cmds # noqa -import pyblish.api - - -class CleanNodesUp(pyblish.api.InstancePlugin): - """Cleans up the staging directory after a successful publish. - - This will also clean published renders and delete their parent directories. - - """ - - order = pyblish.api.IntegratorOrder + 10 - label = "Clean Nodes" - optional = True - active = True - - def process(self, instance): - if not instance.data.get("cleanNodes"): - self.log.info("Nothing to clean.") - return - - nodes_to_clean = instance.data.pop("cleanNodes", []) - self.log.info("Removing {} nodes".format(len(nodes_to_clean))) - for node in nodes_to_clean: - try: - cmds.delete(node) - except ValueError: - # object might be already deleted, don't complain about it - pass diff --git a/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py b/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py index b1fb0542f2..ddcc3f691f 100644 --- a/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/publish/collect_unreal_staticmesh.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- from maya import cmds import pyblish.api +from avalon.api import Session +from openpype.api import get_project_settings class CollectUnrealStaticMesh(pyblish.api.InstancePlugin): @@ -13,13 +15,22 @@ class CollectUnrealStaticMesh(pyblish.api.InstancePlugin): order = pyblish.api.CollectorOrder + 0.2 label = "Collect Unreal Static Meshes" - families = ["unrealStaticMesh"] + families = ["staticMesh"] def process(self, instance): - # add fbx family to trigger fbx extractor - instance.data["families"].append("fbx") - # take the name from instance (without the `S_` prefix) - instance.data["staticMeshCombinedName"] = instance.name[2:] + project_settings = get_project_settings(Session["AVALON_PROJECT"]) + sm_prefix = ( + project_settings + ["maya"] + ["create"] + ["CreateUnrealStaticMesh"] + ["static_mesh_prefix"] + ) + # take the name from instance (without the `staticMesh_` prefix) + instance.data["staticMeshCombinedName"] = "{}_{}".format( + sm_prefix, + instance.name[len(instance.data.get("family")) + 1:] + ) geometry_set = [i for i in instance if i == "geometry_SET"] instance.data["membersToCombine"] = cmds.sets( diff --git a/openpype/hosts/maya/plugins/publish/extract_fbx.py b/openpype/hosts/maya/plugins/publish/extract_fbx.py index a2adcb3091..fbbe8e06b0 100644 --- a/openpype/hosts/maya/plugins/publish/extract_fbx.py +++ b/openpype/hosts/maya/plugins/publish/extract_fbx.py @@ -5,152 +5,29 @@ from maya import cmds # noqa import maya.mel as mel # noqa import pyblish.api import openpype.api -from openpype.hosts.maya.api.lib import ( - root_parent, - maintained_selection -) +from openpype.hosts.maya.api.lib import maintained_selection + +from openpype.hosts.maya.api import fbx class ExtractFBX(openpype.api.Extractor): """Extract FBX from Maya. - This extracts reproducible FBX exports ignoring any of the settings set - on the local machine in the FBX export options window. - - All export settings are applied with the `FBXExport*` commands prior - to the `FBXExport` call itself. The options can be overridden with their - nice names as seen in the "options" property on this class. - - For more information on FBX exports see: - - https://knowledge.autodesk.com/support/maya/learn-explore/caas - /CloudHelp/cloudhelp/2016/ENU/Maya/files/GUID-6CCE943A-2ED4-4CEE-96D4 - -9CB19C28F4E0-htm.html - - http://forums.cgsociety.org/archive/index.php?t-1032853.html - - https://groups.google.com/forum/#!msg/python_inside_maya/cLkaSo361oE - /LKs9hakE28kJ + This extracts reproducible FBX exports ignoring any of the + settings set on the local machine in the FBX export options window. """ - order = pyblish.api.ExtractorOrder label = "Extract FBX" families = ["fbx"] - @property - def options(self): - """Overridable options for FBX Export - - Given in the following format - - {NAME: EXPECTED TYPE} - - If the overridden option's type does not match, - the option is not included and a warning is logged. - - """ - - return { - "cameras": bool, - "smoothingGroups": bool, - "hardEdges": bool, - "tangents": bool, - "smoothMesh": bool, - "instances": bool, - # "referencedContainersContent": bool, # deprecated in Maya 2016+ - "bakeComplexAnimation": int, - "bakeComplexStart": int, - "bakeComplexEnd": int, - "bakeComplexStep": int, - "bakeResampleAnimation": bool, - "animationOnly": bool, - "useSceneName": bool, - "quaternion": str, # "euler" - "shapes": bool, - "skins": bool, - "constraints": bool, - "lights": bool, - "embeddedTextures": bool, - "inputConnections": bool, - "upAxis": str, # x, y or z, - "triangulate": bool - } - - @property - def default_options(self): - """The default options for FBX extraction. - - This includes shapes, skins, constraints, lights and incoming - connections and exports with the Y-axis as up-axis. - - By default this uses the time sliders start and end time. - - """ - - start_frame = int(cmds.playbackOptions(query=True, - animationStartTime=True)) - end_frame = int(cmds.playbackOptions(query=True, - animationEndTime=True)) - - return { - "cameras": False, - "smoothingGroups": False, - "hardEdges": False, - "tangents": False, - "smoothMesh": False, - "instances": False, - "bakeComplexAnimation": True, - "bakeComplexStart": start_frame, - "bakeComplexEnd": end_frame, - "bakeComplexStep": 1, - "bakeResampleAnimation": True, - "animationOnly": False, - "useSceneName": False, - "quaternion": "euler", - "shapes": True, - "skins": True, - "constraints": False, - "lights": True, - "embeddedTextures": True, - "inputConnections": True, - "upAxis": "y", - "triangulate": False - } - - def parse_overrides(self, instance, options): - """Inspect data of instance to determine overridden options - - An instance may supply any of the overridable options - as data, the option is then added to the extraction. - - """ - - for key in instance.data: - if key not in self.options: - continue - - # Ensure the data is of correct type - value = instance.data[key] - if not isinstance(value, self.options[key]): - self.log.warning( - "Overridden attribute {key} was of " - "the wrong type: {invalid_type} " - "- should have been {valid_type}".format( - key=key, - invalid_type=type(value).__name__, - valid_type=self.options[key].__name__)) - continue - - options[key] = value - - return options - def process(self, instance): - - # Ensure FBX plug-in is loaded - cmds.loadPlugin("fbxmaya", quiet=True) + fbx_exporter = fbx.FBXExtractor(log=self.log) # Define output path - stagingDir = self.staging_dir(instance) + staging_dir = self.staging_dir(instance) filename = "{0}.fbx".format(instance.name) - path = os.path.join(stagingDir, filename) + path = os.path.join(staging_dir, filename) # The export requires forward slashes because we need # to format it into a string in a mel expression @@ -162,54 +39,13 @@ class ExtractFBX(openpype.api.Extractor): self.log.info("Members: {0}".format(members)) self.log.info("Instance: {0}".format(instance[:])) - # Parse export options - options = self.default_options - options = self.parse_overrides(instance, options) - self.log.info("Export options: {0}".format(options)) - - # Collect the start and end including handles - start = instance.data["frameStartHandle"] - end = instance.data["frameEndHandle"] - - options['bakeComplexStart'] = start - options['bakeComplexEnd'] = end - - # First apply the default export settings to be fully consistent - # each time for successive publishes - mel.eval("FBXResetExport") - - # Apply the FBX overrides through MEL since the commands - # only work correctly in MEL according to online - # available discussions on the topic - _iteritems = getattr(options, "iteritems", options.items) - for option, value in _iteritems(): - key = option[0].upper() + option[1:] # uppercase first letter - - # Boolean must be passed as lower-case strings - # as to MEL standards - if isinstance(value, bool): - value = str(value).lower() - - template = "FBXExport{0} {1}" if key == "UpAxis" else "FBXExport{0} -v {1}" # noqa - cmd = template.format(key, value) - self.log.info(cmd) - mel.eval(cmd) - - # Never show the UI or generate a log - mel.eval("FBXExportShowUI -v false") - mel.eval("FBXExportGenerateLog -v false") + fbx_exporter.set_options_from_instance(instance) # Export - if "unrealStaticMesh" in instance.data["families"]: - with maintained_selection(): - with root_parent(members): - self.log.info("Un-parenting: {}".format(members)) - cmds.select(members, r=1, noExpand=True) - mel.eval('FBXExport -f "{}" -s'.format(path)) - else: - with maintained_selection(): - cmds.select(members, r=1, noExpand=True) - mel.eval('FBXExport -f "{}" -s'.format(path)) + with maintained_selection(): + fbx_exporter.export(members, path) + cmds.select(members, r=1, noExpand=True) + mel.eval('FBXExport -f "{}" -s'.format(path)) if "representations" not in instance.data: instance.data["representations"] = [] @@ -218,7 +54,7 @@ class ExtractFBX(openpype.api.Extractor): 'name': 'fbx', 'ext': 'fbx', 'files': filename, - "stagingDir": stagingDir, + "stagingDir": staging_dir, } instance.data["representations"].append(representation) diff --git a/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py index 32dc9d1d1c..0c7d61f8f5 100644 --- a/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py +++ b/openpype/hosts/maya/plugins/publish/extract_unreal_staticmesh.py @@ -1,33 +1,87 @@ # -*- coding: utf-8 -*- """Create Unreal Static Mesh data to be extracted as FBX.""" -import openpype.api -import pyblish.api +import os + from maya import cmds # noqa +import pyblish.api +import openpype.api +from openpype.hosts.maya.api.lib import ( + root_parent, + maintained_selection, + delete_after +) +from openpype.hosts.maya.api import fbx + class ExtractUnrealStaticMesh(openpype.api.Extractor): """Extract FBX from Maya. """ order = pyblish.api.ExtractorOrder - 0.1 label = "Extract Unreal Static Mesh" - families = ["unrealStaticMesh"] + families = ["staticMesh"] def process(self, instance): to_combine = instance.data.get("membersToCombine") static_mesh_name = instance.data.get("staticMeshCombinedName") - self.log.info( - "merging {} into {}".format( - " + ".join(to_combine), static_mesh_name)) - duplicates = cmds.duplicate(to_combine, ic=True) - cmds.polyUnite( - *duplicates, - n=static_mesh_name, ch=False) + duplicates = [] - if not instance.data.get("cleanNodes"): - instance.data["cleanNodes"] = [] + # delete created temporary nodes after extraction + with delete_after() as delete_bin: + # if we have more objects, combine them into one + # or just duplicate the single one + if len(to_combine) > 1: + self.log.info( + "merging {} into {}".format( + " + ".join(to_combine), static_mesh_name)) + duplicates = cmds.duplicate(to_combine, ic=True) + cmds.polyUnite( + *duplicates, + n=static_mesh_name, ch=False) + else: + self.log.info( + "duplicating {} to {} for export".format( + to_combine[0], static_mesh_name) + ) + cmds.duplicate(to_combine[0], name=static_mesh_name, ic=True) - instance.data["cleanNodes"].append(static_mesh_name) - instance.data["cleanNodes"] += duplicates + delete_bin.extend([static_mesh_name]) + delete_bin.extend(duplicates) - instance.data["setMembers"] = [static_mesh_name] - instance.data["setMembers"] += instance.data["collisionMembers"] + members = [static_mesh_name] + members += instance.data["collisionMembers"] + + fbx_exporter = fbx.FBXExtractor(log=self.log) + + # Define output path + staging_dir = self.staging_dir(instance) + filename = "{0}.fbx".format(instance.name) + path = os.path.join(staging_dir, filename) + + # The export requires forward slashes because we need + # to format it into a string in a mel expression + path = path.replace('\\', '/') + + self.log.info("Extracting FBX to: {0}".format(path)) + self.log.info("Members: {0}".format(members)) + self.log.info("Instance: {0}".format(instance[:])) + + fbx_exporter.set_options_from_instance(instance) + + with maintained_selection(): + with root_parent(members): + self.log.info("Un-parenting: {}".format(members)) + fbx_exporter.export(members, path) + + if "representations" not in instance.data: + instance.data["representations"] = [] + + representation = { + 'name': 'fbx', + 'ext': 'fbx', + 'files': filename, + "stagingDir": staging_dir, + } + instance.data["representations"].append(representation) + + self.log.info("Extract FBX successful to: {0}".format(path)) diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py b/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py index b2ef174374..c05121a1b0 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_mesh_triangulated.py @@ -10,10 +10,11 @@ class ValidateUnrealMeshTriangulated(pyblish.api.InstancePlugin): order = openpype.api.ValidateMeshOrder hosts = ["maya"] - families = ["unrealStaticMesh"] + families = ["staticMesh"] category = "geometry" label = "Mesh is Triangulated" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] + active = False @classmethod def get_invalid(cls, instance): diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py index 901a2ec75e..d15d52f3bd 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_staticmesh_naming.py @@ -52,8 +52,8 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): optional = True order = openpype.api.ValidateContentsOrder hosts = ["maya"] - families = ["unrealStaticMesh"] - label = "Unreal StaticMesh Name" + families = ["staticMesh"] + label = "Unreal Static Mesh Name" actions = [openpype.hosts.maya.api.action.SelectInvalidAction] regex_mesh = r"(?P.*))" regex_collision = r"(?P.*)" @@ -71,7 +71,17 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): ["CreateUnrealStaticMesh"] ["collision_prefixes"] ) + static_mesh_prefix = ( + project_settings + ["maya"] + ["create"] + ["CreateUnrealStaticMesh"] + ["static_mesh_prefix"] + ) + to_combine = instance.data.get("membersToCombine") + if not to_combine: + raise ValueError("Missing geometry to export.") combined_geometry_name = instance.data.get( "staticMeshCombinedName", None) if cls.validate_mesh: @@ -91,7 +101,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): cls.log.warning("No collision objects to validate.") return False - regex_collision = "{}{}".format( + regex_collision = "{}{}_(\\d+)".format( "(?P({}))_".format( "|".join("{0}".format(p) for p in collision_prefixes) ) or "", cls.regex_collision @@ -107,7 +117,7 @@ class ValidateUnrealStaticMeshName(pyblish.api.InstancePlugin): else: expected_collision = "{}_{}".format( cl_m.group("prefix"), - combined_geometry_name + combined_geometry_name[len(static_mesh_prefix) + 1:] ) if not obj.startswith(expected_collision): diff --git a/openpype/hosts/maya/plugins/publish/validate_unreal_up_axis.py b/openpype/hosts/maya/plugins/publish/validate_unreal_up_axis.py index 5a8c29c22d..5e1b04889f 100644 --- a/openpype/hosts/maya/plugins/publish/validate_unreal_up_axis.py +++ b/openpype/hosts/maya/plugins/publish/validate_unreal_up_axis.py @@ -9,9 +9,10 @@ class ValidateUnrealUpAxis(pyblish.api.ContextPlugin): """Validate if Z is set as up axis in Maya""" optional = True + active = False order = openpype.api.ValidateContentsOrder hosts = ["maya"] - families = ["unrealStaticMesh"] + families = ["staticMesh"] label = "Unreal Up-Axis check" actions = [openpype.api.RepairAction] diff --git a/openpype/plugins/publish/integrate_new.py b/openpype/plugins/publish/integrate_new.py index 6e0940d459..48b87c697b 100644 --- a/openpype/plugins/publish/integrate_new.py +++ b/openpype/plugins/publish/integrate_new.py @@ -101,7 +101,8 @@ class IntegrateAssetNew(pyblish.api.InstancePlugin): "effect", "xgen", "hda", - "usd" + "usd", + "staticMesh" ] exclude_families = ["clip"] db_representation_context_keys = [ diff --git a/openpype/settings/defaults/project_anatomy/templates.json b/openpype/settings/defaults/project_anatomy/templates.json index d46d449c77..7d01248653 100644 --- a/openpype/settings/defaults/project_anatomy/templates.json +++ b/openpype/settings/defaults/project_anatomy/templates.json @@ -28,9 +28,18 @@ }, "delivery": {}, "unreal": { - "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}/{subset}/{@version}", - "file": "{subset}_{@version}<_{output}><.{@frame}>.{ext}", + "folder": "{root[work]}/{project[name]}/unreal/{task[name]}", + "file": "{project[code]}_{asset}", "path": "{@folder}/{@file}" }, - "others": {} + "others": { + "maya2unreal": { + "folder": "{root[work]}/{project[name]}/{hierarchy}/{asset}/publish/{family}", + "file": "{subset}_{@version}<_{output}><.{@frame}>.{ext}", + "path": "{@folder}/{@file}" + }, + "__dynamic_keys_labels__": { + "maya2unreal": "Maya to Unreal" + } + } } \ No newline at end of file diff --git a/openpype/settings/defaults/project_settings/global.json b/openpype/settings/defaults/project_settings/global.json index f08bee8b2d..86786cc9ed 100644 --- a/openpype/settings/defaults/project_settings/global.json +++ b/openpype/settings/defaults/project_settings/global.json @@ -192,6 +192,17 @@ "task_types": [], "tasks": [], "template_name": "render" + }, + { + "families": [ + "staticMesh" + ], + "hosts": [ + "maya" + ], + "task_types": [], + "tasks": [], + "template_name": "maya2unreal" } ], "subset_grouping_profiles": [ @@ -287,7 +298,7 @@ }, { "families": [ - "unrealStaticMesh" + "staticMesh" ], "hosts": [ "maya" @@ -304,6 +315,13 @@ "task_types": [], "hosts": [], "workfile_template": "work" + }, + { + "task_types": [], + "hosts": [ + "unreal" + ], + "workfile_template": "unreal" } ], "last_workfile_on_startup": [ diff --git a/openpype/settings/defaults/project_settings/maya.json b/openpype/settings/defaults/project_settings/maya.json index c25f416562..362835e558 100644 --- a/openpype/settings/defaults/project_settings/maya.json +++ b/openpype/settings/defaults/project_settings/maya.json @@ -52,7 +52,7 @@ "", "_Main" ], - "static_mesh_prefix": "S_", + "static_mesh_prefix": "S", "collision_prefixes": [ "UBX", "UCP",