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

Maya: implement matchmove publishing #5445

Merged
merged 46 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1720c77
OP-6360 - allow export of multiple cameras as alembic
kalisp Aug 10, 2023
ee3d91a
OP-6360 - make validation of camera count optional
kalisp Aug 10, 2023
4096e81
OP-6360 - make ValidatorCameraContents optional
kalisp Aug 11, 2023
96cafd1
OP-6360 - allow extraction of multiple cameras to .ma
kalisp Aug 11, 2023
4391b25
OP-6360 - update defaults for Ayon
kalisp Aug 11, 2023
635216a
OP-6360 - new matchmove creator
kalisp Aug 14, 2023
9cf0ef8
OP-6360 - updated camera extractors
kalisp Aug 14, 2023
6626da9
OP-6360 - added matchmove to reference loader
kalisp Aug 14, 2023
818e9ee
Revert "OP-6360 - make ValidatorCameraContents optional"
kalisp Aug 14, 2023
70da282
Revert "OP-6360 - update defaults for Ayon"
kalisp Aug 14, 2023
54613c3
OP-6360 - performance update
kalisp Aug 14, 2023
479ae76
Revert "OP-6360 - make validation of camera count optional"
kalisp Aug 15, 2023
cc32957
OP-6360 - explicitly cast to list for Maya functions
kalisp Aug 15, 2023
2953b5a
OP-6360 - added documentation about matchmove family
kalisp Aug 23, 2023
491c0b6
OP-6360 - copy input planes
kalisp Aug 28, 2023
da25aea
OP-6360 - expose Settings to keep Image planes
kalisp Aug 28, 2023
b4d0f72
OP-6360 - make both camera extractors optional
kalisp Aug 28, 2023
8b7bc7e
OP-6360 - used long name
kalisp Aug 28, 2023
bf9bb2e
OP-6360 - fix wrong variable
kalisp Aug 28, 2023
9050e3f
Update openpype/hosts/maya/plugins/publish/extract_camera_mayaScene.py
kalisp Aug 29, 2023
e5e2b54
Merge branch 'develop' of github.com:ynput/OpenPype into enhancement/…
kalisp Aug 29, 2023
e769940
Merge remote-tracking branch 'origin/enhancement/OP-6360_Maya-multipl…
kalisp Aug 29, 2023
2841590
OP-6360 - removed shortening of varible
kalisp Aug 29, 2023
35a23ac
OP-6360 - Hound
kalisp Aug 29, 2023
a65af8f
OP-6360 - fix wrong key
kalisp Aug 29, 2023
9f9bc99
Update openpype/hosts/maya/plugins/publish/extract_camera_mayaScene.py
kalisp Sep 1, 2023
4f40ad6
Update openpype/hosts/maya/api/lib.py
kalisp Sep 1, 2023
87dbaae
Update openpype/hosts/maya/plugins/publish/extract_camera_alembic.py
kalisp Sep 1, 2023
27ec8bc
OP-6360 - fix wrong variable
kalisp Sep 1, 2023
3647da9
OP-6360 - added reattaching method
kalisp Sep 1, 2023
9625bba
Revert "Update openpype/hosts/maya/api/lib.py"
kalisp Sep 1, 2023
5853f6e
OP-6360 - exported baked camera should be deleted
kalisp Sep 5, 2023
888b3e2
Merge branch 'develop' into enhancement/OP-6360_Maya-multiple-cameras…
kalisp Sep 25, 2023
b312e1a
OP-6360 - updated docstring
kalisp Sep 26, 2023
5e12ef0
OP-6360 - remove scale keys
kalisp Sep 26, 2023
a2aeed2
OP-6360 - cleaned up resetting of scale keys
kalisp Sep 26, 2023
084c20b
OP-6360 - removed unnecessary logging
kalisp Sep 27, 2023
fab7b54
OP-6360 - reattach image plane to original camera
kalisp Sep 27, 2023
4e9dc80
OP-6360 - added context manager to keep image planes attached to orig…
kalisp Sep 27, 2023
5bc201d
OP-6360 - refactored contextmanager
kalisp Sep 27, 2023
2c130e2
OP-6360 - renamed flag
kalisp Sep 29, 2023
99dd27a
OP-6360 - removed copyInputConnections
kalisp Sep 29, 2023
e2b5c44
OP-6360 - updated plugin labels
kalisp Sep 29, 2023
3cb6a9e
Update openpype/hosts/maya/plugins/create/create_matchmove.py
kalisp Sep 29, 2023
cbccbd4
OP-6360 - fixed formatting
kalisp Sep 29, 2023
b2cba2c
Merge remote-tracking branch 'origin/develop' into enhancement/OP-636…
kalisp Sep 29, 2023
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
2 changes: 1 addition & 1 deletion openpype/hosts/maya/api/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -2571,7 +2571,7 @@ def _get_attrs(node):
new_name = "{0}_baked".format(short_name)
new_node = cmds.duplicate(node,
name=new_name,
renameChildren=True)[0]
renameChildren=True)[0] # noqa

# Connect all attributes on the node except for transform
# attributes
Expand Down
32 changes: 32 additions & 0 deletions openpype/hosts/maya/plugins/create/create_matchmove.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from openpype.hosts.maya.api import (
lib,
plugin
)
from openpype.lib import BoolDef


class CreateMatchmove(plugin.MayaCreator):
"""Instance for more complex setup of cameras.

Might contain multiple cameras, geometries etc.

It is expected to be extracted into .abc or .ma
"""

identifier = "io.openpype.creators.maya.matchmove"
label = "Matchmove"
family = "matchmove"
icon = "video-camera"

def get_instance_attr_defs(self):

defs = lib.collect_animation_defs()

defs.extend([
BoolDef("bakeToWorldSpace",
label="Bake Cameras to World-Space",
tooltip="Bake Cameras to World-Space",
default=True),
])

return defs
3 changes: 2 additions & 1 deletion openpype/hosts/maya/plugins/load/load_reference.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,8 @@ class ReferenceLoader(openpype.hosts.maya.api.plugin.ReferenceLoader):
"camerarig",
"staticMesh",
"skeletalMesh",
"mvLook"]
"mvLook",
"matchmove"]

representations = ["ma", "abc", "fbx", "mb"]

Expand Down
22 changes: 14 additions & 8 deletions openpype/hosts/maya/plugins/publish/extract_camera_alembic.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,21 @@
from openpype.hosts.maya.api import lib


class ExtractCameraAlembic(publish.Extractor):
class ExtractCameraAlembic(publish.Extractor,
publish.OptionalPyblishPluginMixin):
"""Extract a Camera as Alembic.

The cameras gets baked to world space by default. Only when the instance's
The camera gets baked to world space by default. Only when the instance's
`bakeToWorldSpace` is set to False it will include its full hierarchy.

'camera' family expects only single camera, if multiple cameras are needed,
'matchmove' is better choice.
kalisp marked this conversation as resolved.
Show resolved Hide resolved

"""

label = "Camera (Alembic)"
label = "Extract Camera (Alembic)"
hosts = ["maya"]
families = ["camera"]
families = ["camera", "matchmove"]
bake_attributes = []

def process(self, instance):
Expand All @@ -35,10 +39,11 @@ def process(self, instance):

# validate required settings
assert isinstance(step, float), "Step must be a float value"
camera = cameras[0]

# Define extract output file path
dir_path = self.staging_dir(instance)
tokejepsen marked this conversation as resolved.
Show resolved Hide resolved
if not os.path.exists(dir_path):
os.makedirs(dir_path)
filename = "{0}.abc".format(instance.name)
path = os.path.join(dir_path, filename)

Expand All @@ -64,9 +69,10 @@ def process(self, instance):

# if baked, drop the camera hierarchy to maintain
# clean output and backwards compatibility
camera_root = cmds.listRelatives(
camera, parent=True, fullPath=True)[0]
job_str += ' -root {0}'.format(camera_root)
camera_roots = cmds.listRelatives(
cameras, parent=True, fullPath=True)
for camera_root in camera_roots:
job_str += ' -root {0}'.format(camera_root)
kalisp marked this conversation as resolved.
Show resolved Hide resolved

for member in members:
descendants = cmds.listRelatives(member,
Expand Down
144 changes: 110 additions & 34 deletions openpype/hosts/maya/plugins/publish/extract_camera_mayaScene.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@
"""Extract camera as Maya Scene."""
import os
import itertools
import contextlib

from maya import cmds

from openpype.pipeline import publish
from openpype.hosts.maya.api import lib
from openpype.lib import (
BoolDef
)


def massage_ma_file(path):
Expand Down Expand Up @@ -78,7 +82,8 @@ def unlock(plug):
cmds.disconnectAttr(source, destination)


class ExtractCameraMayaScene(publish.Extractor):
class ExtractCameraMayaScene(publish.Extractor,
publish.OptionalPyblishPluginMixin):
"""Extract a Camera as Maya Scene.

This will create a duplicate of the camera that will be baked *with*
Expand All @@ -88,17 +93,22 @@ class ExtractCameraMayaScene(publish.Extractor):
The cameras gets baked to world space by default. Only when the instance's
`bakeToWorldSpace` is set to False it will include its full hierarchy.

'camera' family expects only single camera, if multiple cameras are needed,
'matchmove' is better choice.

Note:
The extracted Maya ascii file gets "massaged" removing the uuid values
so they are valid for older versions of Fusion (e.g. 6.4)

"""

label = "Camera (Maya Scene)"
label = "Extract Camera (Maya Scene)"
hosts = ["maya"]
families = ["camera"]
families = ["camera", "matchmove"]
scene_type = "ma"

keep_image_planes = True

def process(self, instance):
"""Plugin entry point."""
# get settings
Expand Down Expand Up @@ -131,15 +141,15 @@ def process(self, instance):
"bake to world space is ignored...")

# get cameras
members = cmds.ls(instance.data['setMembers'], leaf=True, shapes=True,
long=True, dag=True)
cameras = cmds.ls(members, leaf=True, shapes=True, long=True,
dag=True, type="camera")
members = set(cmds.ls(instance.data['setMembers'], leaf=True,
shapes=True, long=True, dag=True))
cameras = set(cmds.ls(members, leaf=True, shapes=True, long=True,
dag=True, type="camera"))

# validate required settings
assert isinstance(step, float), "Step must be a float value"
camera = cameras[0]
transform = cmds.listRelatives(camera, parent=True, fullPath=True)
transforms = cmds.listRelatives(list(cameras),
parent=True, fullPath=True)

# Define extract output file path
dir_path = self.staging_dir(instance)
Expand All @@ -151,23 +161,21 @@ def process(self, instance):
with lib.evaluation("off"):
with lib.suspended_refresh():
if bake_to_worldspace:
self.log.debug(
"Performing camera bakes: {}".format(transform))
baked = lib.bake_to_world_space(
transform,
transforms,
frame_range=[start, end],
step=step
)
baked_camera_shapes = cmds.ls(baked,
type="camera",
dag=True,
shapes=True,
long=True)

members = members + baked_camera_shapes
members.remove(camera)
baked_camera_shapes = set(cmds.ls(baked,
type="camera",
dag=True,
shapes=True,
long=True))

members.update(baked_camera_shapes)
members.difference_update(cameras)
else:
baked_camera_shapes = cmds.ls(cameras,
baked_camera_shapes = cmds.ls(list(cameras),
type="camera",
dag=True,
shapes=True,
Expand All @@ -186,19 +194,28 @@ def process(self, instance):
unlock(plug)
cmds.setAttr(plug, value)

self.log.debug("Performing extraction..")
cmds.select(cmds.ls(members, dag=True,
shapes=True, long=True), noExpand=True)
cmds.file(path,
force=True,
typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501
exportSelected=True,
preserveReferences=False,
constructionHistory=False,
channels=True, # allow animation
constraints=False,
shader=False,
expressions=False)
attr_values = self.get_attr_values_from_data(
instance.data)
keep_image_planes = attr_values.get("keep_image_planes")

with transfer_image_planes(sorted(cameras),
sorted(baked_camera_shapes),
keep_image_planes):

self.log.info("Performing extraction..")
cmds.select(cmds.ls(list(members), dag=True,
shapes=True, long=True),
noExpand=True)
cmds.file(path,
force=True,
typ="mayaAscii" if self.scene_type == "ma" else "mayaBinary", # noqa: E501
exportSelected=True,
preserveReferences=False,
constructionHistory=False,
channels=True, # allow animation
constraints=False,
shader=False,
expressions=False)

# Delete the baked hierarchy
if bake_to_worldspace:
Expand All @@ -219,3 +236,62 @@ def process(self, instance):

self.log.debug("Extracted instance '{0}' to: {1}".format(
instance.name, path))

@classmethod
def get_attribute_defs(cls):
defs = super(ExtractCameraMayaScene, cls).get_attribute_defs()

defs.extend([
BoolDef("keep_image_planes",
label="Keep Image Planes",
tooltip="Preserving connected image planes on camera",
default=cls.keep_image_planes),

])

return defs
kalisp marked this conversation as resolved.
Show resolved Hide resolved


@contextlib.contextmanager
def transfer_image_planes(source_cameras, target_cameras,
keep_input_connections):
"""Reattaches image planes to baked or original cameras.

Baked cameras are duplicates of original ones.
This attaches it to duplicated camera properly and after
export it reattaches it back to original to keep image plane in workfile.
"""
originals = {}
try:
for source_camera, target_camera in zip(source_cameras,
target_cameras):
image_planes = cmds.listConnections(source_camera,
type="imagePlane") or []

# Split of the parent path they are attached - we want
# the image plane node name.
# TODO: Does this still mean the image plane name is unique?
image_planes = [x.split("->", 1)[1] for x in image_planes]

if not image_planes:
continue

originals[source_camera] = []
for image_plane in image_planes:
if keep_input_connections:
if source_camera == target_camera:
continue
_attach_image_plane(target_camera, image_plane)
else: # explicitly dettaching image planes
cmds.imagePlane(image_plane, edit=True, detach=True)
originals[source_camera].append(image_plane)
yield
finally:
for camera, image_planes in originals.items():
for image_plane in image_planes:
_attach_image_plane(camera, image_plane)


def _attach_image_plane(camera, image_plane):
cmds.imagePlane(image_plane, edit=True, detach=True)
cmds.imagePlane(image_plane, edit=True, camera=camera)
6 changes: 6 additions & 0 deletions openpype/settings/defaults/project_settings/maya.json
Original file line number Diff line number Diff line change
Expand Up @@ -1338,6 +1338,12 @@
"active": true,
"bake_attributes": []
},
"ExtractCameraMayaScene": {
"enabled": true,
"optional": true,
"active": true,
"keep_image_planes": false
},
"ExtractGLB": {
"enabled": true,
"active": true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -978,6 +978,35 @@
}
]
},
{
"type": "dict",
"collapsible": true,
"key": "ExtractCameraMayaScene",
"label": "Extract camera to Maya scene",
"checkbox_key": "enabled",
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
},
{
"type": "boolean",
"key": "optional",
"label": "Optional"
},
{
"type": "boolean",
"key": "active",
"label": "Active"
},
{
"type": "boolean",
"key": "keep_image_planes",
"label": "Export Image planes"
}
]
},
{
"type": "dict",
"collapsible": true,
Expand Down
Loading
Loading