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

Commit

Permalink
Merge pull request #4376 from moonyuet/feature/OP-4244-Data-Exchange-…
Browse files Browse the repository at this point in the history
…Cameras
  • Loading branch information
antirotor committed Jan 31, 2023
2 parents 6f68527 + 46996bb commit ce449f9
Show file tree
Hide file tree
Showing 9 changed files with 388 additions and 2 deletions.
26 changes: 26 additions & 0 deletions openpype/hosts/max/plugins/create/create_camera.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
"""Creator plugin for creating camera."""
from openpype.hosts.max.api import plugin
from openpype.pipeline import CreatedInstance


class CreateCamera(plugin.MaxCreator):
identifier = "io.openpype.creators.max.camera"
label = "Camera"
family = "camera"
icon = "gear"

def create(self, subset_name, instance_data, pre_create_data):
from pymxs import runtime as rt
sel_obj = list(rt.selection)
instance = super(CreateCamera, self).create(
subset_name,
instance_data,
pre_create_data) # type: CreatedInstance
container = rt.getNodeByName(instance.data.get("instance_node"))
# TODO: Disable "Add to Containers?" Panel
# parent the selected cameras into the container
for obj in sel_obj:
obj.parent = container
# for additional work on the node:
# instance_node = rt.getNodeByName(instance.get("instance_node"))
49 changes: 49 additions & 0 deletions openpype/hosts/max/plugins/load/load_camera_fbx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import os
from openpype.pipeline import (
load
)


class FbxLoader(load.LoaderPlugin):
"""Fbx Loader"""

families = ["camera"]
representations = ["fbx"]
order = -9
icon = "code-fork"
color = "white"

def load(self, context, name=None, namespace=None, data=None):
from pymxs import runtime as rt

filepath = os.path.normpath(self.fname)

fbx_import_cmd = (
f"""
FBXImporterSetParam "Animation" true
FBXImporterSetParam "Cameras" true
FBXImporterSetParam "AxisConversionMethod" true
FbxExporterSetParam "UpAxis" "Y"
FbxExporterSetParam "Preserveinstances" true
importFile @"{filepath}" #noPrompt using:FBXIMP
""")

self.log.debug(f"Executing command: {fbx_import_cmd}")
rt.execute(fbx_import_cmd)

container_name = f"{name}_CON"

asset = rt.getNodeByName(f"{name}")
# rename the container with "_CON"
container = rt.container(name=container_name)
asset.Parent = container

return container

def remove(self, container):
from pymxs import runtime as rt

node = container["node"]
rt.delete(node)
50 changes: 50 additions & 0 deletions openpype/hosts/max/plugins/load/load_max_scene.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import os
from openpype.pipeline import (
load
)


class MaxSceneLoader(load.LoaderPlugin):
"""Max Scene Loader"""

families = ["camera"]
representations = ["max"]
order = -8
icon = "code-fork"
color = "green"

def load(self, context, name=None, namespace=None, data=None):
from pymxs import runtime as rt
path = os.path.normpath(self.fname)
# import the max scene by using "merge file"
path = path.replace('\\', '/')

merge_before = {
c for c in rt.rootNode.Children
if rt.classOf(c) == rt.Container
}
rt.mergeMaxFile(path)

merge_after = {
c for c in rt.rootNode.Children
if rt.classOf(c) == rt.Container
}
max_containers = merge_after.difference(merge_before)

if len(max_containers) != 1:
self.log.error("Something failed when loading.")

max_container = max_containers.pop()
container_name = f"{name}_CON"
# rename the container with "_CON"
# get the original container
container = rt.container(name=container_name)
max_container.Parent = container

return container

def remove(self, container):
from pymxs import runtime as rt

node = container["node"]
rt.delete(node)
5 changes: 4 additions & 1 deletion openpype/hosts/max/plugins/load/load_pointcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
class AbcLoader(load.LoaderPlugin):
"""Alembic loader."""

families = ["model", "animation", "pointcache"]
families = ["model",
"camera",
"animation",
"pointcache"]
label = "Load Alembic"
representations = ["abc"]
order = -10
Expand Down
75 changes: 75 additions & 0 deletions openpype/hosts/max/plugins/publish/extract_camera_abc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import os
import pyblish.api
from openpype.pipeline import (
publish,
OptionalPyblishPluginMixin
)
from pymxs import runtime as rt
from openpype.hosts.max.api import (
maintained_selection,
get_all_children
)


class ExtractCameraAlembic(publish.Extractor,
OptionalPyblishPluginMixin):
"""
Extract Camera with AlembicExport
"""

order = pyblish.api.ExtractorOrder - 0.1
label = "Extract Alembic Camera"
hosts = ["max"]
families = ["camera"]
optional = True

def process(self, instance):
if not self.is_active(instance.data):
return
start = float(instance.data.get("frameStartHandle", 1))
end = float(instance.data.get("frameEndHandle", 1))

container = instance.data["instance_node"]

self.log.info("Extracting Camera ...")

stagingdir = self.staging_dir(instance)
filename = "{name}.abc".format(**instance.data)
path = os.path.join(stagingdir, filename)

# We run the render
self.log.info("Writing alembic '%s' to '%s'" % (filename,
stagingdir))

export_cmd = (
f"""
AlembicExport.ArchiveType = #ogawa
AlembicExport.CoordinateSystem = #maya
AlembicExport.StartFrame = {start}
AlembicExport.EndFrame = {end}
AlembicExport.CustomAttributes = true
exportFile @"{path}" #noPrompt selectedOnly:on using:AlembicExport
""")

self.log.debug(f"Executing command: {export_cmd}")

with maintained_selection():
# select and export
rt.select(get_all_children(rt.getNodeByName(container)))
rt.execute(export_cmd)

self.log.info("Performing Extraction ...")
if "representations" not in instance.data:
instance.data["representations"] = []

representation = {
'name': 'abc',
'ext': 'abc',
'files': filename,
"stagingDir": stagingdir,
}
instance.data["representations"].append(representation)
self.log.info("Extracted instance '%s' to: %s" % (instance.name,
path))
75 changes: 75 additions & 0 deletions openpype/hosts/max/plugins/publish/extract_camera_fbx.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import os
import pyblish.api
from openpype.pipeline import (
publish,
OptionalPyblishPluginMixin
)
from pymxs import runtime as rt
from openpype.hosts.max.api import (
maintained_selection,
get_all_children
)


class ExtractCameraFbx(publish.Extractor,
OptionalPyblishPluginMixin):
"""
Extract Camera with FbxExporter
"""

order = pyblish.api.ExtractorOrder - 0.2
label = "Extract Fbx Camera"
hosts = ["max"]
families = ["camera"]
optional = True

def process(self, instance):
if not self.is_active(instance.data):
return
container = instance.data["instance_node"]

self.log.info("Extracting Camera ...")
stagingdir = self.staging_dir(instance)
filename = "{name}.fbx".format(**instance.data)

filepath = os.path.join(stagingdir, filename)
self.log.info("Writing fbx file '%s' to '%s'" % (filename,
filepath))

# Need to export:
# Animation = True
# Cameras = True
# AxisConversionMethod
fbx_export_cmd = (
f"""
FBXExporterSetParam "Animation" true
FBXExporterSetParam "Cameras" true
FBXExporterSetParam "AxisConversionMethod" "Animation"
FbxExporterSetParam "UpAxis" "Y"
FbxExporterSetParam "Preserveinstances" true
exportFile @"{filepath}" #noPrompt selectedOnly:true using:FBXEXP
""")

self.log.debug(f"Executing command: {fbx_export_cmd}")

with maintained_selection():
# select and export
rt.select(get_all_children(rt.getNodeByName(container)))
rt.execute(fbx_export_cmd)

self.log.info("Performing Extraction ...")
if "representations" not in instance.data:
instance.data["representations"] = []

representation = {
'name': 'fbx',
'ext': 'fbx',
'files': filename,
"stagingDir": stagingdir,
}
instance.data["representations"].append(representation)
self.log.info("Extracted instance '%s' to: %s" % (instance.name,
filepath))
60 changes: 60 additions & 0 deletions openpype/hosts/max/plugins/publish/extract_max_scene_raw.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import os
import pyblish.api
from openpype.pipeline import (
publish,
OptionalPyblishPluginMixin
)
from pymxs import runtime as rt
from openpype.hosts.max.api import (
maintained_selection,
get_all_children
)


class ExtractMaxSceneRaw(publish.Extractor,
OptionalPyblishPluginMixin):
"""
Extract Raw Max Scene with SaveSelected
"""

order = pyblish.api.ExtractorOrder - 0.2
label = "Extract Max Scene (Raw)"
hosts = ["max"]
families = ["camera"]
optional = True

def process(self, instance):
if not self.is_active(instance.data):
return
container = instance.data["instance_node"]

# publish the raw scene for camera
self.log.info("Extracting Raw Max Scene ...")

stagingdir = self.staging_dir(instance)
filename = "{name}.max".format(**instance.data)

max_path = os.path.join(stagingdir, filename)
self.log.info("Writing max file '%s' to '%s'" % (filename,
max_path))

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

# saving max scene
with maintained_selection():
# need to figure out how to select the camera
rt.select(get_all_children(rt.getNodeByName(container)))
rt.execute(f'saveNodes selection "{max_path}" quiet:true')

self.log.info("Performing Extraction ...")

representation = {
'name': 'max',
'ext': 'max',
'files': filename,
"stagingDir": stagingdir,
}
instance.data["representations"].append(representation)
self.log.info("Extracted instance '%s' to: %s" % (instance.name,
max_path))
2 changes: 1 addition & 1 deletion openpype/hosts/max/plugins/publish/extract_pointcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class ExtractAlembic(publish.Extractor):
order = pyblish.api.ExtractorOrder
label = "Extract Pointcache"
hosts = ["max"]
families = ["pointcache", "camera"]
families = ["pointcache"]

def process(self, instance):
start = float(instance.data.get("frameStartHandle", 1))
Expand Down
Loading

0 comments on commit ce449f9

Please sign in to comment.