Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Maya: Implementation of JSON layout for Unreal workflow #3353

Merged
merged 15 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion openpype/hosts/blender/plugins/publish/extract_layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def process(self, instance):
"rotation": {
"x": asset.rotation_euler.x,
"y": asset.rotation_euler.y,
"z": asset.rotation_euler.z,
"z": asset.rotation_euler.z
},
"scale": {
"x": asset.scale.x,
Expand All @@ -189,6 +189,18 @@ def process(self, instance):
}
}

json_element["transform_matrix"] = []

for row in list(asset.matrix_world.transposed()):
json_element["transform_matrix"].append(list(row))

json_element["basis"] = [
[1, 0, 0, 0],
[0, -1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]
]

# Extract the animation as well
if family == "rig":
f, n = self._export_animation(
Expand Down
146 changes: 146 additions & 0 deletions openpype/hosts/maya/plugins/publish/extract_layout.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
import math
import os
import json

from maya import cmds
from maya.api import OpenMaya as om

from bson.objectid import ObjectId

from openpype.pipeline import legacy_io
import openpype.api


class ExtractLayout(openpype.api.Extractor):
"""Extract a layout."""

label = "Extract Layout"
hosts = ["maya"]
families = ["layout"]
optional = True

def process(self, instance):
# Define extract output file path
stagingdir = self.staging_dir(instance)

# Perform extraction
self.log.info("Performing extraction..")

if "representations" not in instance.data:
instance.data["representations"] = []

json_data = []

for asset in cmds.sets(str(instance), query=True):
# Find the container
grp_name = asset.split(':')[0]
containers = cmds.ls(f"{grp_name}*_CON")

assert len(containers) == 1, \
f"More than one container found for {asset}"

container = containers[0]

representation_id = cmds.getAttr(f"{container}.representation")

representation = legacy_io.find_one(
{
"type": "representation",
"_id": ObjectId(representation_id)
}, projection={"parent": True, "context.family": True})

self.log.info(representation)

version_id = representation.get("parent")
family = representation.get("context").get("family")

json_element = {
"family": family,
"instance_name": cmds.getAttr(f"{container}.name"),
"representation": str(representation_id),
"version": str(version_id)
}

loc = cmds.xform(asset, query=True, translation=True)
rot = cmds.xform(asset, query=True, rotation=True, euler=True)
scl = cmds.xform(asset, query=True, relative=True, scale=True)

json_element["transform"] = {
"translation": {
"x": loc[0],
"y": loc[1],
"z": loc[2]
},
"rotation": {
"x": math.radians(rot[0]),
"y": math.radians(rot[1]),
"z": math.radians(rot[2])
},
"scale": {
"x": scl[0],
"y": scl[1],
"z": scl[2]
}
}

row_length = 4
t_matrix_list = cmds.xform(asset, query=True, matrix=True)

transform_mm = om.MMatrix(t_matrix_list)
transform = om.MTransformationMatrix(transform_mm)

t = transform.translation(om.MSpace.kWorld)
t = om.MVector(t.x, t.z, -t.y)
transform.setTranslation(t, om.MSpace.kWorld)
transform.rotateBy(
om.MEulerRotation(math.radians(-90), 0, 0), om.MSpace.kWorld)
transform.scaleBy([1.0, 1.0, -1.0], om.MSpace.kObject)

t_matrix_list = list(transform.asMatrix())

t_matrix = []
for i in range(0, len(t_matrix_list), row_length):
t_matrix.append(t_matrix_list[i:i + row_length])

json_element["transform_matrix"] = []
for row in t_matrix:
json_element["transform_matrix"].append(list(row))

basis_list = [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1
]

basis_mm = om.MMatrix(basis_list)
basis = om.MTransformationMatrix(basis_mm)

b_matrix_list = list(basis.asMatrix())
b_matrix = []

for i in range(0, len(b_matrix_list), row_length):
b_matrix.append(b_matrix_list[i:i + row_length])

json_element["basis"] = []
for row in b_matrix:
json_element["basis"].append(list(row))

json_data.append(json_element)

json_filename = "{}.json".format(instance.name)
json_path = os.path.join(stagingdir, json_filename)

with open(json_path, "w+") as file:
json.dump(json_data, fp=file, indent=2)

json_representation = {
'name': 'json',
'ext': 'json',
'files': json_filename,
"stagingDir": stagingdir,
}
instance.data["representations"].append(json_representation)

self.log.info("Extracted instance '%s' to: %s",
instance.name, json_representation)
75 changes: 38 additions & 37 deletions openpype/hosts/unreal/plugins/load/load_alembic_skeletalmesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,34 @@ class SkeletalMeshAlembicLoader(plugin.Loader):
icon = "cube"
color = "orange"

def get_task(self, filename, asset_dir, asset_name, replace):
task = unreal.AssetImportTask()
options = unreal.AbcImportSettings()
sm_settings = unreal.AbcStaticMeshSettings()
conversion_settings = unreal.AbcConversionSettings(
preset=unreal.AbcConversionPreset.CUSTOM,
flip_u=False, flip_v=False,
rotation=[0.0, 0.0, 0.0],
scale=[1.0, 1.0, 1.0])

task.set_editor_property('filename', filename)
task.set_editor_property('destination_path', asset_dir)
task.set_editor_property('destination_name', asset_name)
task.set_editor_property('replace_existing', replace)
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.set_editor_property(
'import_type', unreal.AlembicImportType.SKELETAL)

options.static_mesh_settings = sm_settings
options.conversion_settings = conversion_settings
task.options = options

return task

def load(self, context, name, namespace, data):
"""Load and containerise representation into Content Browser.

Expand Down Expand Up @@ -50,36 +78,24 @@ def load(self, context, name, namespace, data):
asset_name = "{}_{}".format(asset, name)
else:
asset_name = "{}".format(name)
version = context.get('version').get('name')

tools = unreal.AssetToolsHelpers().get_asset_tools()
asset_dir, container_name = tools.create_unique_asset_name(
"{}/{}/{}".format(root, asset, name), suffix="")
f"{root}/{asset}/{name}_v{version:03d}", suffix="")

container_name += suffix

unreal.EditorAssetLibrary.make_directory(asset_dir)
if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir):
unreal.EditorAssetLibrary.make_directory(asset_dir)

task = unreal.AssetImportTask()
task = self.get_task(self.fname, asset_dir, asset_name, False)

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)
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501

# 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
unreal_pipeline.create_container(
container=container_name, path=asset_dir)
# Create Asset Container
unreal_pipeline.create_container(
container=container_name, path=asset_dir)

data = {
"schema": "openpype:container-2.0",
Expand Down Expand Up @@ -110,23 +126,8 @@ def update(self, container, representation):
source_path = get_representation_path(representation)
destination_path = container["namespace"]

task = unreal.AssetImportTask()
task = self.get_task(source_path, destination_path, name, True)

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"],
Expand Down
29 changes: 14 additions & 15 deletions openpype/hosts/unreal/plugins/load/load_alembic_staticmesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ def get_task(self, filename, asset_dir, asset_name, replace):
task = unreal.AssetImportTask()
options = unreal.AbcImportSettings()
sm_settings = unreal.AbcStaticMeshSettings()
conversion_settings = unreal.AbcConversionSettings()
conversion_settings = unreal.AbcConversionSettings(
preset=unreal.AbcConversionPreset.CUSTOM,
flip_u=False, flip_v=False,
rotation=[0.0, 0.0, 0.0],
scale=[1.0, 1.0, 1.0])

task.set_editor_property('filename', filename)
task.set_editor_property('destination_path', asset_dir)
Expand All @@ -40,13 +44,6 @@ def get_task(self, filename, asset_dir, asset_name, replace):

sm_settings.set_editor_property('merge_meshes', True)

conversion_settings.set_editor_property('flip_u', False)
conversion_settings.set_editor_property('flip_v', True)
conversion_settings.set_editor_property(
'scale', unreal.Vector(x=100.0, y=100.0, z=100.0))
conversion_settings.set_editor_property(
'rotation', unreal.Vector(x=-90.0, y=0.0, z=180.0))

options.static_mesh_settings = sm_settings
options.conversion_settings = conversion_settings
task.options = options
Expand Down Expand Up @@ -83,22 +80,24 @@ def load(self, context, name, namespace, data):
asset_name = "{}_{}".format(asset, name)
else:
asset_name = "{}".format(name)
version = context.get('version').get('name')

tools = unreal.AssetToolsHelpers().get_asset_tools()
asset_dir, container_name = tools.create_unique_asset_name(
"{}/{}/{}".format(root, asset, name), suffix="")
f"{root}/{asset}/{name}_v{version:03d}", suffix="")

container_name += suffix

unreal.EditorAssetLibrary.make_directory(asset_dir)
if not unreal.EditorAssetLibrary.does_directory_exist(asset_dir):
unreal.EditorAssetLibrary.make_directory(asset_dir)

task = self.get_task(self.fname, asset_dir, asset_name, False)
task = self.get_task(self.fname, asset_dir, asset_name, False)

unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501
unreal.AssetToolsHelpers.get_asset_tools().import_asset_tasks([task]) # noqa: E501

# Create Asset Container
unreal_pipeline.create_container(
container=container_name, path=asset_dir)
# Create Asset Container
unreal_pipeline.create_container(
container=container_name, path=asset_dir)

data = {
"schema": "openpype:container-2.0",
Expand Down
Loading